From a7d0bdc3c04e2b79d4bee43184775830eaa5eca3 Mon Sep 17 00:00:00 2001 From: zhouzhongping Date: Sat, 25 Apr 2020 22:20:29 +0800 Subject: [PATCH] 1.1.38 --- .gitignore | 6 + README.md | 48 + doc/database/install.sql | 5598 ++++++++ doc/database/upgrade/v1.1.0.sql | 31 + doc/database/upgrade/v1.1.1.sql | 22 + doc/database/upgrade/v1.1.12.sql | 43 + doc/database/upgrade/v1.1.13.sql | 48 + doc/database/upgrade/v1.1.16.sql | 33 + doc/database/upgrade/v1.1.17.sql | 87 + doc/database/upgrade/v1.1.18.sql | 11 + doc/database/upgrade/v1.1.19.sql | 29 + doc/database/upgrade/v1.1.21.sql | 79 + doc/database/upgrade/v1.1.22.sql | 27 + doc/database/upgrade/v1.1.23.sql | 9 + doc/database/upgrade/v1.1.24.sql | 100 + doc/database/upgrade/v1.1.25.sql | 168 + doc/database/upgrade/v1.1.26.sql | 6 + doc/database/upgrade/v1.1.27.sql | 117 + doc/database/upgrade/v1.1.29.sql | 83 + doc/database/upgrade/v1.1.3.sql | 148 + doc/database/upgrade/v1.1.32.sql | 119 + doc/database/upgrade/v1.1.35.sql | 66 + doc/database/upgrade/v1.1.36.sql | 15 + doc/database/upgrade/v1.1.37.sql | 4 + doc/database/upgrade/v1.1.4.sql | 5 + doc/database/upgrade/v1.1.6.sql | 219 + doc/database/upgrade/v1.1.7.sql | 83 + doc/database/upgrade/v1.1.8.sql | 11 + doc/database/upgrade/v1.1.9.sql | 370 + doc/更新日志.txt | 598 + icon.jpg | Bin 0 -> 26385 bytes source/application/admin/config.php | 28 + .../admin/controller/Controller.php | 187 + source/application/admin/controller/Index.php | 22 + .../application/admin/controller/Passport.php | 44 + source/application/admin/controller/Store.php | 122 + .../admin/controller/admin/User.php | 31 + .../admin/controller/setting/Cache.php | 84 + .../admin/controller/setting/Science.php | 204 + .../admin/controller/store/Access.php | 94 + source/application/admin/extra/menus.php | 47 + source/application/admin/model/Setting.php | 31 + source/application/admin/model/Wxapp.php | 103 + .../application/admin/model/WxappCategory.php | 28 + source/application/admin/model/WxappHelp.php | 29 + source/application/admin/model/WxappPage.php | 75 + source/application/admin/model/admin/User.php | 81 + .../application/admin/model/store/Access.php | 656 + source/application/admin/model/store/User.php | 46 + .../admin/view/admin/user/renew.php | 55 + source/application/admin/view/index/index.php | 13 + .../application/admin/view/layouts/layout.php | 104 + .../application/admin/view/passport/login.php | 63 + .../admin/view/setting/cache/clear.php | 78 + .../admin/view/setting/science/index.php | 100 + .../admin/view/store/access/add.php | 67 + .../admin/view/store/access/edit.php | 69 + .../admin/view/store/access/index.php | 83 + source/application/admin/view/store/add.php | 70 + source/application/admin/view/store/index.php | 80 + .../application/admin/view/store/recycle.php | 75 + .../api/behavior/order/PaySuccess.php | 114 + source/application/api/config.php | 8 + source/application/api/controller/Address.php | 112 + source/application/api/controller/Article.php | 53 + source/application/api/controller/Cart.php | 96 + .../application/api/controller/Category.php | 29 + source/application/api/controller/Comment.php | 29 + .../application/api/controller/Controller.php | 139 + source/application/api/controller/Coupon.php | 29 + source/application/api/controller/Goods.php | 81 + source/application/api/controller/Notify.php | 25 + source/application/api/controller/Order.php | 141 + source/application/api/controller/Page.php | 31 + .../application/api/controller/Recharge.php | 67 + source/application/api/controller/Shop.php | 41 + source/application/api/controller/Upload.php | 86 + source/application/api/controller/User.php | 43 + source/application/api/controller/Wxapp.php | 39 + .../api/controller/balance/Log.php | 28 + .../api/controller/bargain/Active.php | 84 + .../api/controller/bargain/Order.php | 101 + .../api/controller/bargain/Task.php | 91 + .../application/api/controller/points/Log.php | 28 + .../api/controller/recharge/Order.php | 28 + .../api/controller/sharing/Active.php | 38 + .../api/controller/sharing/Comment.php | 68 + .../api/controller/sharing/Goods.php | 83 + .../api/controller/sharing/Index.php | 26 + .../api/controller/sharing/Order.php | 242 + .../api/controller/sharing/Refund.php | 109 + .../api/controller/sharing/Setting.php | 25 + .../api/controller/sharp/Goods.php | 70 + .../api/controller/sharp/Index.php | 32 + .../api/controller/sharp/Order.php | 114 + .../application/api/controller/shop/Order.php | 80 + .../api/controller/user/Comment.php | 53 + .../api/controller/user/Coupon.php | 74 + .../api/controller/user/Dealer.php | 115 + .../application/api/controller/user/Index.php | 44 + .../application/api/controller/user/Order.php | 179 + .../api/controller/user/Refund.php | 109 + .../api/controller/user/Wallet.php | 44 + .../api/controller/user/dealer/Apply.php | 46 + .../api/controller/user/dealer/Order.php | 56 + .../api/controller/user/dealer/Qrcode.php | 57 + .../api/controller/user/dealer/Team.php | 60 + .../api/controller/user/dealer/Withdraw.php | 72 + .../api/controller/wxapp/Formid.php | 33 + source/application/api/model/Article.php | 88 + source/application/api/model/Cart.php | 292 + source/application/api/model/Category.php | 28 + source/application/api/model/Comment.php | 227 + source/application/api/model/CommentImage.php | 23 + source/application/api/model/Coupon.php | 86 + source/application/api/model/Delivery.php | 24 + source/application/api/model/DeliveryRule.php | 24 + source/application/api/model/Express.php | 26 + source/application/api/model/Goods.php | 176 + source/application/api/model/GoodsImage.php | 23 + source/application/api/model/GoodsSku.php | 24 + source/application/api/model/GoodsSpecRel.php | 14 + source/application/api/model/Order.php | 382 + source/application/api/model/OrderAddress.php | 23 + source/application/api/model/OrderExtract.php | 15 + source/application/api/model/OrderGoods.php | 35 + source/application/api/model/OrderRefund.php | 171 + .../api/model/OrderRefundAddress.php | 23 + .../api/model/OrderRefundImage.php | 15 + source/application/api/model/Setting.php | 23 + source/application/api/model/Spec.php | 14 + source/application/api/model/SpecValue.php | 14 + source/application/api/model/UploadFile.php | 23 + source/application/api/model/User.php | 204 + source/application/api/model/UserAddress.php | 148 + source/application/api/model/UserCoupon.php | 190 + source/application/api/model/Wxapp.php | 29 + .../application/api/model/WxappCategory.php | 24 + source/application/api/model/WxappHelp.php | 22 + source/application/api/model/WxappNavbar.php | 24 + source/application/api/model/WxappPage.php | 299 + .../application/api/model/WxappPrepayId.php | 37 + .../api/model/article/Category.php | 76 + .../application/api/model/bargain/Active.php | 184 + .../application/api/model/bargain/Setting.php | 35 + source/application/api/model/bargain/Task.php | 366 + .../api/model/bargain/TaskHelp.php | 108 + source/application/api/model/dealer/Apply.php | 92 + .../application/api/model/dealer/Capital.php | 23 + source/application/api/model/dealer/Order.php | 117 + .../application/api/model/dealer/Referee.php | 100 + .../application/api/model/dealer/Setting.php | 22 + source/application/api/model/dealer/User.php | 51 + .../application/api/model/dealer/Withdraw.php | 99 + .../application/api/model/recharge/Order.php | 206 + .../api/model/recharge/OrderPlan.php | 32 + .../application/api/model/recharge/Plan.php | 73 + .../application/api/model/sharing/Active.php | 50 + .../api/model/sharing/ActiveUsers.php | 23 + .../api/model/sharing/Category.php | 23 + .../application/api/model/sharing/Comment.php | 228 + .../api/model/sharing/CommentImage.php | 23 + .../application/api/model/sharing/Goods.php | 159 + .../api/model/sharing/GoodsImage.php | 23 + .../api/model/sharing/GoodsSku.php | 24 + .../api/model/sharing/GoodsSpecRel.php | 14 + .../application/api/model/sharing/Order.php | 477 + .../api/model/sharing/OrderAddress.php | 23 + .../api/model/sharing/OrderExtract.php | 15 + .../api/model/sharing/OrderGoods.php | 35 + .../api/model/sharing/OrderRefund.php | 171 + .../api/model/sharing/OrderRefundAddress.php | 23 + .../api/model/sharing/OrderRefundImage.php | 15 + .../application/api/model/sharing/Setting.php | 22 + source/application/api/model/sharp/Active.php | 68 + .../api/model/sharp/ActiveGoods.php | 115 + .../api/model/sharp/ActiveTime.php | 77 + source/application/api/model/sharp/Goods.php | 25 + .../application/api/model/sharp/GoodsSku.php | 24 + .../application/api/model/sharp/Setting.php | 23 + source/application/api/model/store/Shop.php | 131 + .../api/model/store/shop/Clerk.php | 65 + .../api/model/store/shop/ClerkRel.php | 15 + .../api/model/store/shop/Order.php | 15 + .../application/api/model/user/BalanceLog.php | 38 + source/application/api/model/user/Grade.php | 15 + .../application/api/model/user/GradeLog.php | 15 + .../application/api/model/user/PointsLog.php | 30 + source/application/api/model/wow/Order.php | 15 + source/application/api/model/wow/Setting.php | 15 + source/application/api/model/wow/Shoping.php | 15 + source/application/api/model/wxapp/Formid.php | 30 + source/application/api/service/Basics.php | 8 + source/application/api/service/Goods.php | 15 + source/application/api/service/Payment.php | 66 + source/application/api/service/User.php | 36 + .../api/service/bargain/Amount.php | 147 + .../api/service/bargain/order/PaySuccess.php | 45 + .../api/service/coupon/GoodsDeduct.php | 90 + .../api/service/master/order/PaySuccess.php | 43 + .../api/service/order/Checkout.php | 856 ++ .../api/service/order/PaySuccess.php | 150 + .../api/service/order/source/Bargain.php | 70 + .../api/service/order/source/Basics.php | 43 + .../api/service/order/source/Factory.php | 36 + .../api/service/order/source/Master.php | 78 + .../api/service/order/source/Sharp.php | 76 + .../service/order/source/checkout/Bargain.php | 28 + .../service/order/source/checkout/Basics.php | 38 + .../service/order/source/checkout/Factory.php | 35 + .../service/order/source/checkout/Master.php | 33 + .../service/order/source/checkout/Sharp.php | 96 + .../api/service/points/GoodsDeduct.php | 82 + .../api/service/recharge/PaySuccess.php | 79 + .../api/service/sharing/order/Checkout.php | 887 ++ .../api/service/sharing/order/PaySuccess.php | 152 + .../application/api/service/sharp/Active.php | 357 + .../application/api/service/sharp/Order.php | 38 + .../api/service/sharp/order/PaySuccess.php | 47 + source/application/api/tags.php | 20 + .../api/validate/order/Checkout.php | 50 + .../api/validate/sharing/order/Checkout.php | 44 + source/application/common.php | 348 + source/application/common/behavior/App.php | 29 + .../application/common/enum/DeliveryType.php | 36 + source/application/common/enum/EnumBasics.php | 15 + .../application/common/enum/OrderStatus.php | 14 + source/application/common/enum/OrderType.php | 57 + .../application/common/enum/PrinterType.php | 30 + source/application/common/enum/Setting.php | 85 + .../common/enum/goods/DeductStockType.php | 39 + .../common/enum/order/OrderSource.php | 45 + .../common/enum/order/PayStatus.php | 38 + .../application/common/enum/order/PayType.php | 38 + .../application/common/enum/order/Status.php | 47 + .../common/enum/recharge/order/PayStatus.php | 38 + .../enum/recharge/order/RechargeType.php | 38 + .../common/enum/sharp/ActiveStatus.php | 45 + .../common/enum/sharp/GoodsStatus.php | 45 + .../common/enum/user/balanceLog/Scene.php | 56 + .../common/enum/user/grade/log/ChangeType.php | 20 + .../common/exception/BaseException.php | 33 + .../common/exception/ExceptionHandler.php | 49 + source/application/common/library/Lock.php | 61 + .../common/library/express/Kuaidi100.php | 76 + source/application/common/library/helper.php | 147 + .../common/library/printer/Driver.php | 72 + .../common/library/printer/engine/Basics.php | 55 + .../common/library/printer/engine/Feie.php | 68 + .../library/printer/engine/PrintCenter.php | 44 + .../library/printer/party/FeieHttpClient.php | 368 + .../application/common/library/sms/Driver.php | 73 + .../common/library/sms/engine/Aliyun.php | 86 + .../common/library/sms/engine/Server.php | 19 + .../sms/package/aliyun/SignatureHelper.php | 88 + .../common/library/storage/Driver.php | 112 + .../common/library/storage/engine/Aliyun.php | 84 + .../common/library/storage/engine/Local.php | 86 + .../common/library/storage/engine/Qcloud.php | 93 + .../common/library/storage/engine/Qiniu.php | 88 + .../common/library/storage/engine/Server.php | 125 + .../common/library/wechat/Qrcode.php | 49 + .../common/library/wechat/WxBase.php | 155 + .../common/library/wechat/WxPay.php | 400 + .../common/library/wechat/WxTplMsg.php | 64 + .../common/library/wechat/WxUser.php | 38 + .../common/library/wechat/cert/.gitignore | 2 + .../common/library/wechat/logs/.gitignore | 2 + .../common/library/wechat/wow/Order.php | 121 + .../common/library/wechat/wow/Shoping.php | 83 + source/application/common/model/Article.php | 56 + source/application/common/model/BaseModel.php | 143 + source/application/common/model/Category.php | 122 + source/application/common/model/Comment.php | 113 + .../application/common/model/CommentImage.php | 25 + source/application/common/model/Coupon.php | 117 + source/application/common/model/Delivery.php | 87 + .../application/common/model/DeliveryRule.php | 32 + source/application/common/model/Express.php | 76 + source/application/common/model/Goods.php | 350 + .../application/common/model/GoodsImage.php | 25 + source/application/common/model/GoodsSku.php | 35 + .../application/common/model/GoodsSpecRel.php | 24 + source/application/common/model/Order.php | 360 + .../application/common/model/OrderAddress.php | 45 + .../application/common/model/OrderExtract.php | 15 + .../application/common/model/OrderGoods.php | 72 + .../application/common/model/OrderRefund.php | 113 + .../common/model/OrderRefundAddress.php | 15 + .../common/model/OrderRefundImage.php | 25 + source/application/common/model/Printer.php | 96 + source/application/common/model/Region.php | 208 + .../common/model/ReturnAddress.php | 25 + source/application/common/model/Setting.php | 253 + source/application/common/model/Spec.php | 15 + source/application/common/model/SpecValue.php | 24 + source/application/common/model/Store.php | 13 + .../application/common/model/UploadFile.php | 83 + .../common/model/UploadFileUsed.php | 14 + .../application/common/model/UploadGroup.php | 24 + source/application/common/model/User.php | 158 + .../application/common/model/UserAddress.php | 36 + .../application/common/model/UserCoupon.php | 133 + source/application/common/model/Wxapp.php | 68 + .../common/model/WxappCategory.php | 24 + source/application/common/model/WxappHelp.php | 37 + .../application/common/model/WxappNavbar.php | 51 + source/application/common/model/WxappPage.php | 697 + .../common/model/WxappPrepayId.php | 57 + .../application/common/model/admin/User.php | 16 + .../common/model/article/Category.php | 32 + .../common/model/bargain/Active.php | 98 + .../common/model/bargain/Setting.php | 103 + .../application/common/model/bargain/Task.php | 140 + .../common/model/bargain/TaskHelp.php | 28 + .../application/common/model/dealer/Apply.php | 95 + .../common/model/dealer/Capital.php | 27 + .../application/common/model/dealer/Order.php | 231 + .../common/model/dealer/Referee.php | 81 + .../common/model/dealer/Setting.php | 389 + .../application/common/model/dealer/User.php | 114 + .../common/model/dealer/Withdraw.php | 57 + .../common/model/recharge/Order.php | 94 + .../common/model/recharge/OrderPlan.php | 16 + .../common/model/recharge/Plan.php | 27 + .../common/model/sharing/Active.php | 196 + .../common/model/sharing/ActiveUsers.php | 46 + .../common/model/sharing/Category.php | 98 + .../common/model/sharing/Comment.php | 64 + .../common/model/sharing/CommentImage.php | 28 + .../common/model/sharing/Goods.php | 391 + .../common/model/sharing/GoodsImage.php | 28 + .../common/model/sharing/GoodsSku.php | 51 + .../common/model/sharing/GoodsSpecRel.php | 26 + .../common/model/sharing/Order.php | 433 + .../common/model/sharing/OrderAddress.php | 48 + .../common/model/sharing/OrderExtract.php | 17 + .../common/model/sharing/OrderGoods.php | 106 + .../common/model/sharing/OrderRefund.php | 117 + .../model/sharing/OrderRefundAddress.php | 17 + .../common/model/sharing/OrderRefundImage.php | 28 + .../common/model/sharing/Setting.php | 105 + .../application/common/model/sharp/Active.php | 38 + .../common/model/sharp/ActiveGoods.php | 111 + .../common/model/sharp/ActiveTime.php | 56 + .../application/common/model/sharp/Goods.php | 153 + .../common/model/sharp/GoodsSku.php | 26 + .../common/model/sharp/Setting.php | 100 + .../application/common/model/store/Access.php | 61 + .../application/common/model/store/Role.php | 27 + .../common/model/store/RoleAccess.php | 17 + .../application/common/model/store/Shop.php | 59 + .../application/common/model/store/User.php | 82 + .../common/model/store/UserRole.php | 17 + .../common/model/store/shop/Clerk.php | 48 + .../common/model/store/shop/Order.php | 73 + .../common/model/user/BalanceLog.php | 66 + .../application/common/model/user/Grade.php | 111 + .../common/model/user/GradeLog.php | 36 + .../common/model/user/PointsLog.php | 48 + source/application/common/model/wow/Order.php | 123 + .../application/common/model/wow/Setting.php | 98 + .../application/common/model/wow/Shoping.php | 98 + .../application/common/model/wxapp/Formid.php | 50 + source/application/common/service/Basics.php | 31 + source/application/common/service/Goods.php | 53 + source/application/common/service/Message.php | 346 + source/application/common/service/Order.php | 91 + .../common/service/delivery/Express.php | 249 + .../common/service/goods/source/Bargain.php | 13 + .../common/service/goods/source/Basics.php | 34 + .../common/service/goods/source/Factory.php | 36 + .../common/service/goods/source/Master.php | 123 + .../common/service/goods/source/Sharp.php | 151 + .../common/service/order/Complete.php | 183 + .../common/service/order/Printer.php | 115 + .../common/service/order/Refund.php | 79 + .../common/service/order/Source.php | 9 + .../common/service/qrcode/Base.php | 77 + .../common/service/qrcode/Extract.php | 98 + .../common/service/qrcode/Goods.php | 177 + .../common/service/qrcode/Poster.php | 175 + .../common/service/qrcode/bargain/Goods.php | 171 + .../service/qrcode/resource/goods_bg.png | Bin 0 -> 8980 bytes .../common/service/qrcode/sharp/Goods.php | 170 + .../common/service/wechat/wow/Order.php | 364 + .../common/service/wechat/wow/Shoping.php | 163 + .../common/service/wxapp/FormId.php | 38 + source/application/config.php | 241 + source/application/database.php | 54 + source/application/route.php | 15 + source/application/store/common.php | 20 + source/application/store/config.php | 37 + .../store/controller/Controller.php | 230 + source/application/store/controller/Goods.php | 127 + source/application/store/controller/Index.php | 32 + source/application/store/controller/Order.php | 147 + .../application/store/controller/Passport.php | 48 + .../application/store/controller/Setting.php | 132 + source/application/store/controller/Shop.php | 90 + .../application/store/controller/Upload.php | 88 + source/application/store/controller/User.php | 83 + source/application/store/controller/Wxapp.php | 52 + .../store/controller/apps/bargain/Active.php | 86 + .../store/controller/apps/bargain/Setting.php | 33 + .../store/controller/apps/bargain/Task.php | 60 + .../store/controller/apps/dealer/Apply.php | 46 + .../store/controller/apps/dealer/Order.php | 29 + .../store/controller/apps/dealer/Setting.php | 59 + .../store/controller/apps/dealer/User.php | 108 + .../store/controller/apps/dealer/Withdraw.php | 79 + .../store/controller/apps/sharing/Active.php | 42 + .../controller/apps/sharing/Category.php | 82 + .../store/controller/apps/sharing/Comment.php | 62 + .../store/controller/apps/sharing/Goods.php | 184 + .../store/controller/apps/sharing/Order.php | 87 + .../store/controller/apps/sharing/Setting.php | 33 + .../controller/apps/sharing/order/Operate.php | 118 + .../controller/apps/sharing/order/Refund.php | 82 + .../store/controller/apps/sharp/Active.php | 78 + .../controller/apps/sharp/ActiveTime.php | 120 + .../store/controller/apps/sharp/Goods.php | 124 + .../store/controller/apps/sharp/Setting.php | 33 + .../store/controller/apps/wow/Order.php | 44 + .../store/controller/apps/wow/Setting.php | 33 + .../store/controller/apps/wow/Shoping.php | 44 + .../store/controller/content/Article.php | 85 + .../store/controller/content/Files.php | 88 + .../controller/content/article/Category.php | 78 + .../store/controller/content/files/Group.php | 81 + .../store/controller/data/Goods.php | 47 + .../store/controller/data/Shop.php | 39 + .../store/controller/data/User.php | 50 + .../store/controller/data/bargain/Goods.php | 43 + .../store/controller/data/sharing/Goods.php | 47 + .../store/controller/data/sharp/Goods.php | 43 + .../store/controller/goods/Category.php | 83 + .../store/controller/goods/Comment.php | 62 + .../store/controller/goods/Spec.php | 93 + .../store/controller/market/Basic.php | 39 + .../store/controller/market/Coupon.php | 108 + .../store/controller/market/Points.php | 47 + .../store/controller/market/Push.php | 46 + .../store/controller/market/Recharge.php | 28 + .../store/controller/market/recharge/Plan.php | 95 + .../store/controller/order/Operate.php | 102 + .../store/controller/order/Refund.php | 82 + .../store/controller/setting/Address.php | 79 + .../store/controller/setting/Cache.php | 137 + .../store/controller/setting/Delivery.php | 96 + .../store/controller/setting/Express.php | 89 + .../store/controller/setting/Help.php | 19 + .../store/controller/setting/Printer.php | 99 + .../store/controller/shop/Clerk.php | 91 + .../store/controller/shop/Order.php | 33 + .../store/controller/statistics/Data.php | 63 + .../store/controller/store/Role.php | 100 + .../store/controller/store/User.php | 115 + .../store/controller/upload/Library.php | 104 + .../store/controller/user/Balance.php | 31 + .../store/controller/user/Grade.php | 81 + .../store/controller/user/Recharge.php | 31 + .../store/controller/wxapp/Help.php | 82 + .../store/controller/wxapp/Page.php | 145 + source/application/store/extra/menus.php | 651 + source/application/store/model/Article.php | 88 + source/application/store/model/Category.php | 77 + source/application/store/model/Comment.php | 32 + .../application/store/model/CommentImage.php | 14 + source/application/store/model/Coupon.php | 66 + source/application/store/model/Delivery.php | 167 + .../application/store/model/DeliveryRule.php | 55 + source/application/store/model/Express.php | 45 + source/application/store/model/Goods.php | 149 + source/application/store/model/GoodsImage.php | 14 + source/application/store/model/GoodsSku.php | 70 + .../application/store/model/GoodsSpecRel.php | 14 + source/application/store/model/Order.php | 478 + .../application/store/model/OrderAddress.php | 15 + .../application/store/model/OrderExtract.php | 15 + source/application/store/model/OrderGoods.php | 15 + .../application/store/model/OrderRefund.php | 135 + .../store/model/OrderRefundAddress.php | 33 + .../store/model/OrderRefundImage.php | 10 + source/application/store/model/Printer.php | 41 + source/application/store/model/Region.php | 15 + .../application/store/model/ReturnAddress.php | 72 + source/application/store/model/Setting.php | 95 + source/application/store/model/Spec.php | 35 + source/application/store/model/SpecValue.php | 38 + source/application/store/model/Store.php | 196 + source/application/store/model/UploadFile.php | 92 + .../store/model/UploadFileUsed.php | 38 + .../application/store/model/UploadGroup.php | 66 + source/application/store/model/User.php | 208 + .../application/store/model/UserAddress.php | 15 + source/application/store/model/UserCoupon.php | 28 + source/application/store/model/Wxapp.php | 111 + .../application/store/model/WxappCategory.php | 24 + source/application/store/model/WxappHelp.php | 43 + .../application/store/model/WxappNavbar.php | 26 + source/application/store/model/WxappPage.php | 87 + .../store/model/article/Category.php | 76 + .../store/model/bargain/Active.php | 130 + .../store/model/bargain/Setting.php | 43 + .../application/store/model/bargain/Task.php | 59 + .../store/model/bargain/TaskHelp.php | 31 + .../application/store/model/dealer/Apply.php | 69 + .../store/model/dealer/Capital.php | 15 + .../application/store/model/dealer/Order.php | 46 + .../store/model/dealer/Referee.php | 76 + .../store/model/dealer/Setting.php | 111 + .../application/store/model/dealer/User.php | 165 + .../store/model/dealer/Withdraw.php | 152 + .../store/model/recharge/Order.php | 61 + .../store/model/recharge/OrderPlan.php | 15 + .../application/store/model/recharge/Plan.php | 58 + .../store/model/sharing/Active.php | 30 + .../store/model/sharing/ActiveUsers.php | 30 + .../store/model/sharing/Category.php | 79 + .../store/model/sharing/Comment.php | 91 + .../store/model/sharing/CommentImage.php | 14 + .../application/store/model/sharing/Goods.php | 150 + .../store/model/sharing/GoodsImage.php | 14 + .../store/model/sharing/GoodsSku.php | 70 + .../store/model/sharing/GoodsSpecRel.php | 14 + .../application/store/model/sharing/Order.php | 507 + .../store/model/sharing/OrderAddress.php | 15 + .../store/model/sharing/OrderExtract.php | 15 + .../store/model/sharing/OrderGoods.php | 15 + .../store/model/sharing/OrderRefund.php | 135 + .../model/sharing/OrderRefundAddress.php | 34 + .../store/model/sharing/OrderRefundImage.php | 15 + .../store/model/sharing/Setting.php | 43 + .../application/store/model/sharp/Active.php | 132 + .../store/model/sharp/ActiveGoods.php | 33 + .../store/model/sharp/ActiveTime.php | 248 + .../application/store/model/sharp/Goods.php | 183 + .../store/model/sharp/GoodsSku.php | 44 + .../application/store/model/sharp/Setting.php | 43 + .../application/store/model/store/Access.php | 56 + source/application/store/model/store/Role.php | 187 + .../store/model/store/RoleAccess.php | 95 + source/application/store/model/store/Shop.php | 113 + source/application/store/model/store/User.php | 201 + .../store/model/store/UserRole.php | 94 + .../store/model/store/shop/Clerk.php | 102 + .../store/model/store/shop/Order.php | 43 + .../store/model/user/BalanceLog.php | 61 + source/application/store/model/user/Grade.php | 97 + .../application/store/model/user/GradeLog.php | 26 + .../store/model/user/PointsLog.php | 58 + source/application/store/model/wow/Order.php | 43 + .../application/store/model/wow/Setting.php | 43 + .../application/store/model/wow/Shoping.php | 43 + .../application/store/model/wxapp/Formid.php | 32 + source/application/store/service/Auth.php | 168 + source/application/store/service/Goods.php | 64 + source/application/store/service/Menus.php | 213 + .../application/store/service/goods/Apply.php | 55 + .../store/service/order/Export.php | 121 + .../store/service/statistics/Data.php | 64 + .../service/statistics/data/GoodsRanking.php | 42 + .../store/service/statistics/data/Survey.php | 147 + .../service/statistics/data/Trade7days.php | 107 + .../statistics/data/UserExpendRanking.php | 31 + .../store/service/wxapp/Message.php | 113 + .../store/view/apps/bargain/active/add.php | 226 + .../store/view/apps/bargain/active/edit.php | 200 + .../store/view/apps/bargain/active/index.php | 132 + .../store/view/apps/bargain/setting/index.php | 60 + .../store/view/apps/bargain/task/help.php | 65 + .../store/view/apps/bargain/task/index.php | 130 + .../store/view/apps/dealer/apply/index.php | 209 + .../store/view/apps/dealer/order/index.php | 187 + .../store/view/apps/dealer/setting/index.php | 930 ++ .../store/view/apps/dealer/setting/qrcode.php | 206 + .../store/view/apps/dealer/user/edit.php | 66 + .../store/view/apps/dealer/user/fans.php | 64 + .../store/view/apps/dealer/user/index.php | 205 + .../store/view/apps/dealer/withdraw/index.php | 301 + .../store/view/apps/sharing/active/index.php | 111 + .../store/view/apps/sharing/active/users.php | 90 + .../store/view/apps/sharing/category/add.php | 61 + .../store/view/apps/sharing/category/edit.php | 61 + .../view/apps/sharing/category/index.php | 128 + .../view/apps/sharing/comment/detail.php | 163 + .../store/view/apps/sharing/comment/index.php | 122 + .../store/view/apps/sharing/goods/add.php | 704 + .../view/apps/sharing/goods/copy_master.php | 744 ++ .../store/view/apps/sharing/goods/edit.php | 749 ++ .../store/view/apps/sharing/goods/index.php | 253 + .../store/view/apps/sharing/order/detail.php | 700 + .../store/view/apps/sharing/order/index.php | 359 + .../sharing/order/operate/batchDelivery.php | 80 + .../view/apps/sharing/order/refund/detail.php | 358 + .../view/apps/sharing/order/refund/index.php | 210 + .../store/view/apps/sharing/setting/index.php | 129 + .../store/view/apps/sharp/active/add.php | 173 + .../store/view/apps/sharp/active/index.php | 122 + .../store/view/apps/sharp/active_time/add.php | 156 + .../view/apps/sharp/active_time/edit.php | 148 + .../view/apps/sharp/active_time/index.php | 125 + .../store/view/apps/sharp/goods/edit.php | 247 + .../store/view/apps/sharp/goods/index.php | 127 + .../store/view/apps/sharp/goods/step1.php | 73 + .../store/view/apps/sharp/goods/step2.php | 236 + .../store/view/apps/sharp/setting/index.php | 67 + .../store/view/apps/wow/order/index.php | 122 + .../store/view/apps/wow/setting/index.php | 72 + .../store/view/apps/wow/shoping/index.php | 114 + .../store/view/content/article/add.php | 159 + .../view/content/article/category/add.php | 50 + .../view/content/article/category/edit.php | 50 + .../view/content/article/category/index.php | 79 + .../store/view/content/article/edit.php | 172 + .../store/view/content/article/index.php | 109 + .../store/view/content/files/group/add.php | 71 + .../store/view/content/files/group/edit.php | 60 + .../store/view/content/files/group/index.php | 81 + .../store/view/content/files/index.php | 87 + .../store/view/content/files/recycle.php | 96 + .../store/view/data/bargain/goods/list.php | 136 + .../store/view/data/goods/list.php | 165 + .../store/view/data/sharing/goods/list.php | 168 + .../store/view/data/sharp/goods/list.php | 133 + .../application/store/view/data/shop/list.php | 117 + .../application/store/view/data/user/list.php | 168 + .../store/view/goods/_template/spec_many.php | 72 + source/application/store/view/goods/add.php | 643 + .../store/view/goods/category/add.php | 87 + .../store/view/goods/category/edit.php | 96 + .../store/view/goods/category/index.php | 134 + .../store/view/goods/comment/detail.php | 163 + .../store/view/goods/comment/index.php | 123 + source/application/store/view/goods/edit.php | 691 + source/application/store/view/goods/index.php | 190 + source/application/store/view/index/index.php | 179 + .../view/layouts/_template/file_library.php | 123 + .../view/layouts/_template/tpl_file_item.php | 13 + .../application/store/view/layouts/error.php | 57 + .../application/store/view/layouts/layout.php | 134 + .../store/view/market/basic/full_free.php | 400 + .../store/view/market/coupon/add.php | 226 + .../store/view/market/coupon/edit.php | 243 + .../store/view/market/coupon/index.php | 120 + .../store/view/market/coupon/receive.php | 76 + .../store/view/market/points/log.php | 96 + .../store/view/market/points/setting.php | 153 + .../store/view/market/push/send.php | 116 + .../store/view/market/push/user.php | 64 + .../store/view/market/recharge/plan/add.php | 63 + .../store/view/market/recharge/plan/edit.php | 63 + .../store/view/market/recharge/plan/index.php | 93 + .../store/view/market/recharge/setting.php | 91 + .../application/store/view/order/detail.php | 612 + source/application/store/view/order/index.php | 251 + .../view/order/operate/batchDelivery.php | 80 + .../store/view/order/refund/detail.php | 358 + .../store/view/order/refund/index.php | 210 + .../application/store/view/passport/login.php | 67 + .../store/view/setting/address/add.php | 60 + .../store/view/setting/address/edit.php | 60 + .../store/view/setting/address/index.php | 92 + .../store/view/setting/cache/clear.php | 48 + .../store/view/setting/delivery/add.php | 197 + .../store/view/setting/delivery/index.php | 91 + .../store/view/setting/express/add.php | 56 + .../store/view/setting/express/company.php | 5433 ++++++++ .../store/view/setting/express/edit.php | 58 + .../store/view/setting/express/index.php | 91 + .../store/view/setting/help/tplMsg.php | 29 + .../store/view/setting/printer.php | 71 + .../store/view/setting/printer/add.php | 130 + .../store/view/setting/printer/edit.php | 141 + .../store/view/setting/printer/index.php | 91 + source/application/store/view/setting/sms.php | 156 + .../store/view/setting/storage.php | 197 + .../application/store/view/setting/store.php | 81 + .../application/store/view/setting/tplMsg.php | 145 + .../application/store/view/setting/trade.php | 112 + source/application/store/view/shop/add.php | 195 + .../application/store/view/shop/clerk/add.php | 123 + .../store/view/shop/clerk/edit.php | 110 + .../store/view/shop/clerk/index.php | 134 + source/application/store/view/shop/edit.php | 215 + .../application/store/view/shop/getpoint.php | 1179 ++ source/application/store/view/shop/index.php | 113 + .../store/view/shop/order/index.php | 102 + .../store/view/statistics/data/index.php | 314 + .../application/store/view/store/role/add.php | 105 + .../store/view/store/role/edit.php | 108 + .../store/view/store/role/index.php | 80 + .../application/store/view/store/user/add.php | 75 + .../store/view/store/user/edit.php | 77 + .../store/view/store/user/index.php | 89 + .../store/view/store/user/renew.php | 55 + .../store/view/user/balance/log.php | 116 + .../application/store/view/user/grade/add.php | 100 + .../store/view/user/grade/edit.php | 104 + .../store/view/user/grade/index.php | 100 + source/application/store/view/user/index.php | 412 + .../store/view/user/recharge/order.php | 146 + .../store/view/wxapp/custom/index.php | 78 + .../application/store/view/wxapp/help/add.php | 55 + .../store/view/wxapp/help/edit.php | 56 + .../store/view/wxapp/help/index.php | 83 + .../store/view/wxapp/page/category.php | 102 + .../store/view/wxapp/page/edit.php | 2247 ++++ .../store/view/wxapp/page/index.php | 113 + .../store/view/wxapp/page/links.php | 439 + .../application/store/view/wxapp/setting.php | 93 + source/application/tags.php | 69 + .../application/task/behavior/DealerOrder.php | 96 + source/application/task/behavior/Order.php | 212 + .../application/task/behavior/UserCoupon.php | 67 + .../task/behavior/bargain/Task.php | 79 + .../task/behavior/sharing/Active.php | 136 + .../task/behavior/sharing/Order.php | 223 + .../application/task/behavior/sharp/Order.php | 100 + .../application/task/behavior/user/Grade.php | 70 + source/application/task/common.php | 20 + source/application/task/model/Express.php | 14 + source/application/task/model/Goods.php | 15 + source/application/task/model/GoodsSku.php | 15 + source/application/task/model/Order.php | 31 + .../application/task/model/OrderAddress.php | 15 + source/application/task/model/OrderGoods.php | 15 + source/application/task/model/OrderRefund.php | 15 + source/application/task/model/Setting.php | 15 + source/application/task/model/User.php | 85 + source/application/task/model/UserCoupon.php | 43 + source/application/task/model/Wxapp.php | 15 + .../application/task/model/WxappPrepayId.php | 14 + .../application/task/model/bargain/Task.php | 42 + .../application/task/model/dealer/Apply.php | 14 + .../application/task/model/dealer/Order.php | 46 + .../application/task/model/dealer/Referee.php | 15 + .../application/task/model/dealer/Setting.php | 15 + source/application/task/model/dealer/User.php | 14 + .../application/task/model/recharge/Order.php | 24 + .../application/task/model/sharing/Active.php | 42 + .../task/model/sharing/ActiveUsers.php | 15 + .../application/task/model/sharing/Goods.php | 14 + .../task/model/sharing/GoodsSku.php | 15 + .../application/task/model/sharing/Order.php | 80 + .../task/model/sharing/OrderAddress.php | 15 + .../task/model/sharing/OrderGoods.php | 15 + .../task/model/sharing/OrderRefund.php | 15 + .../task/model/sharing/Setting.php | 15 + .../application/task/model/sharp/Active.php | 15 + .../task/model/sharp/ActiveGoods.php | 15 + .../task/model/sharp/ActiveTime.php | 15 + source/application/task/model/sharp/Goods.php | 15 + .../application/task/model/sharp/Setting.php | 15 + .../task/model/user/BalanceLog.php | 15 + source/application/task/model/user/Grade.php | 15 + .../application/task/model/user/GradeLog.php | 14 + .../application/task/model/user/PointsLog.php | 14 + source/application/task/service/Order.php | 75 + source/composer.json | 47 + source/runtime/.gitignore | 2 + source/thinkphp/.gitignore | 4 + source/thinkphp/.htaccess | 1 + source/thinkphp/.travis.yml | 47 + source/thinkphp/CONTRIBUTING.md | 119 + source/thinkphp/LICENSE.txt | 32 + source/thinkphp/README.md | 114 + source/thinkphp/base.php | 65 + source/thinkphp/codecov.yml | 12 + source/thinkphp/composer.json | 35 + source/thinkphp/console.php | 20 + source/thinkphp/convention.php | 298 + source/thinkphp/helper.php | 589 + source/thinkphp/lang/zh-cn.php | 136 + source/thinkphp/library/think/App.php | 677 + source/thinkphp/library/think/Build.php | 235 + source/thinkphp/library/think/Cache.php | 247 + source/thinkphp/library/think/Collection.php | 467 + source/thinkphp/library/think/Config.php | 214 + source/thinkphp/library/think/Console.php | 863 ++ source/thinkphp/library/think/Controller.php | 229 + source/thinkphp/library/think/Cookie.php | 268 + source/thinkphp/library/think/Db.php | 180 + source/thinkphp/library/think/Debug.php | 252 + source/thinkphp/library/think/Env.php | 39 + source/thinkphp/library/think/Error.php | 136 + source/thinkphp/library/think/Exception.php | 55 + source/thinkphp/library/think/File.php | 478 + source/thinkphp/library/think/Hook.php | 148 + source/thinkphp/library/think/Lang.php | 265 + source/thinkphp/library/think/Loader.php | 677 + source/thinkphp/library/think/Log.php | 237 + source/thinkphp/library/think/Model.php | 2350 ++++ source/thinkphp/library/think/Paginator.php | 409 + source/thinkphp/library/think/Process.php | 1205 ++ source/thinkphp/library/think/Request.php | 1690 +++ source/thinkphp/library/think/Response.php | 332 + source/thinkphp/library/think/Route.php | 1645 +++ source/thinkphp/library/think/Session.php | 366 + source/thinkphp/library/think/Template.php | 1139 ++ source/thinkphp/library/think/Url.php | 333 + source/thinkphp/library/think/Validate.php | 1371 ++ source/thinkphp/library/think/View.php | 239 + .../thinkphp/library/think/cache/Driver.php | 231 + .../library/think/cache/driver/File.php | 268 + .../library/think/cache/driver/Lite.php | 187 + .../library/think/cache/driver/Memcache.php | 177 + .../library/think/cache/driver/Memcached.php | 187 + .../library/think/cache/driver/Redis.php | 188 + .../library/think/cache/driver/Sqlite.php | 199 + .../library/think/cache/driver/Wincache.php | 152 + .../library/think/cache/driver/Xcache.php | 155 + .../library/think/config/driver/Ini.php | 24 + .../library/think/config/driver/Json.php | 24 + .../library/think/config/driver/Xml.php | 31 + .../library/think/console/Command.php | 470 + .../thinkphp/library/think/console/Input.php | 464 + source/thinkphp/library/think/console/LICENSE | 19 + .../thinkphp/library/think/console/Output.php | 222 + .../library/think/console/bin/README.md | 1 + .../library/think/console/bin/hiddeninput.exe | Bin 0 -> 9216 bytes .../library/think/console/command/Build.php | 56 + .../library/think/console/command/Clear.php | 63 + .../library/think/console/command/Help.php | 69 + .../library/think/console/command/Lists.php | 74 + .../library/think/console/command/Make.php | 110 + .../think/console/command/make/Controller.php | 50 + .../think/console/command/make/Model.php | 36 + .../command/make/stubs/controller.plain.stub | 10 + .../command/make/stubs/controller.stub | 85 + .../console/command/make/stubs/model.stub | 10 + .../console/command/optimize/Autoload.php | 294 + .../think/console/command/optimize/Config.php | 93 + .../think/console/command/optimize/Route.php | 75 + .../think/console/command/optimize/Schema.php | 118 + .../library/think/console/input/Argument.php | 115 + .../think/console/input/Definition.php | 375 + .../library/think/console/input/Option.php | 190 + .../library/think/console/output/Ask.php | 340 + .../think/console/output/Descriptor.php | 319 + .../think/console/output/Formatter.php | 198 + .../library/think/console/output/Question.php | 211 + .../console/output/descriptor/Console.php | 149 + .../think/console/output/driver/Buffer.php | 52 + .../think/console/output/driver/Console.php | 373 + .../think/console/output/driver/Nothing.php | 33 + .../think/console/output/formatter/Stack.php | 116 + .../think/console/output/formatter/Style.php | 189 + .../think/console/output/question/Choice.php | 163 + .../console/output/question/Confirmation.php | 57 + .../library/think/controller/Rest.php | 99 + .../thinkphp/library/think/controller/Yar.php | 51 + source/thinkphp/library/think/db/Builder.php | 899 ++ .../thinkphp/library/think/db/Connection.php | 1059 ++ .../thinkphp/library/think/db/Expression.php | 48 + source/thinkphp/library/think/db/Query.php | 3045 +++++ .../library/think/db/builder/Mysql.php | 137 + .../library/think/db/builder/Pgsql.php | 89 + .../library/think/db/builder/Sqlite.php | 82 + .../library/think/db/builder/Sqlsrv.php | 137 + .../library/think/db/connector/Mysql.php | 126 + .../library/think/db/connector/Pgsql.php | 103 + .../library/think/db/connector/Sqlite.php | 104 + .../library/think/db/connector/Sqlsrv.php | 125 + .../library/think/db/connector/pgsql.sql | 117 + .../think/db/exception/BindParamException.php | 35 + .../db/exception/DataNotFoundException.php | 43 + .../db/exception/ModelNotFoundException.php | 43 + .../thinkphp/library/think/debug/Console.php | 160 + source/thinkphp/library/think/debug/Html.php | 111 + .../exception/ClassNotFoundException.php | 32 + .../library/think/exception/DbException.php | 43 + .../think/exception/ErrorException.php | 57 + .../library/think/exception/Handle.php | 282 + .../library/think/exception/HttpException.php | 36 + .../think/exception/HttpResponseException.php | 33 + .../library/think/exception/PDOException.php | 39 + .../exception/RouteNotFoundException.php | 22 + .../exception/TemplateNotFoundException.php | 33 + .../think/exception/ThrowableError.php | 47 + .../think/exception/ValidateException.php | 33 + .../library/think/log/driver/File.php | 270 + .../library/think/log/driver/Socket.php | 250 + .../library/think/log/driver/Test.php | 30 + .../library/think/model/Collection.php | 79 + source/thinkphp/library/think/model/Merge.php | 322 + source/thinkphp/library/think/model/Pivot.php | 42 + .../thinkphp/library/think/model/Relation.php | 155 + .../think/model/relation/BelongsTo.php | 243 + .../think/model/relation/BelongsToMany.php | 644 + .../library/think/model/relation/HasMany.php | 318 + .../think/model/relation/HasManyThrough.php | 157 + .../library/think/model/relation/HasOne.php | 215 + .../think/model/relation/MorphMany.php | 314 + .../library/think/model/relation/MorphOne.php | 263 + .../library/think/model/relation/MorphTo.php | 299 + .../library/think/model/relation/OneToOne.php | 337 + .../think/paginator/driver/Bootstrap.php | 205 + .../library/think/process/Builder.php | 233 + .../thinkphp/library/think/process/Utils.php | 75 + .../think/process/exception/Failed.php | 42 + .../think/process/exception/Timeout.php | 61 + .../library/think/process/pipes/Pipes.php | 93 + .../library/think/process/pipes/Unix.php | 196 + .../library/think/process/pipes/Windows.php | 228 + .../thinkphp/library/think/response/Json.php | 51 + .../thinkphp/library/think/response/Jsonp.php | 58 + .../library/think/response/Redirect.php | 105 + .../thinkphp/library/think/response/View.php | 89 + .../thinkphp/library/think/response/Xml.php | 102 + .../library/think/session/driver/Memcache.php | 118 + .../think/session/driver/Memcached.php | 126 + .../library/think/session/driver/Redis.php | 128 + .../library/think/template/TagLib.php | 334 + .../library/think/template/driver/File.php | 74 + .../library/think/template/taglib/Cx.php | 673 + .../library/think/view/driver/Php.php | 160 + .../library/think/view/driver/Think.php | 167 + .../library/traits/controller/Jump.php | 167 + .../library/traits/model/SoftDelete.php | 200 + .../library/traits/think/Instance.php | 54 + source/thinkphp/logo.png | Bin 0 -> 6995 bytes source/thinkphp/phpunit.xml | 35 + source/thinkphp/start.php | 19 + source/thinkphp/tpl/default_index.tpl | 10 + source/thinkphp/tpl/dispatch_jump.tpl | 49 + source/thinkphp/tpl/page_trace.tpl | 71 + source/thinkphp/tpl/think_exception.tpl | 537 + .../aliyuncs/oss-sdk-php/.coveralls.yml | 2 + source/vendor/aliyuncs/oss-sdk-php/.gitignore | 8 + .../vendor/aliyuncs/oss-sdk-php/.travis.yml | 21 + .../vendor/aliyuncs/oss-sdk-php/CHANGELOG.md | 92 + source/vendor/aliyuncs/oss-sdk-php/LICENSE.md | 21 + .../vendor/aliyuncs/oss-sdk-php/README-CN.md | 149 + source/vendor/aliyuncs/oss-sdk-php/README.md | 150 + .../vendor/aliyuncs/oss-sdk-php/autoload.php | 11 + .../vendor/aliyuncs/oss-sdk-php/build-phar.sh | 13 + .../vendor/aliyuncs/oss-sdk-php/composer.json | 24 + .../vendor/aliyuncs/oss-sdk-php/example.jpg | Bin 0 -> 21839 bytes source/vendor/aliyuncs/oss-sdk-php/index.php | 3 + .../vendor/aliyuncs/oss-sdk-php/phpunit.xml | 19 + .../aliyuncs/oss-sdk-php/samples/Bucket.php | 167 + .../oss-sdk-php/samples/BucketCors.php | 108 + .../oss-sdk-php/samples/BucketLifecycle.php | 109 + .../oss-sdk-php/samples/BucketLogging.php | 95 + .../oss-sdk-php/samples/BucketReferer.php | 101 + .../oss-sdk-php/samples/BucketWebsite.php | 92 + .../aliyuncs/oss-sdk-php/samples/Callback.php | 83 + .../aliyuncs/oss-sdk-php/samples/Common.php | 84 + .../aliyuncs/oss-sdk-php/samples/Config.php | 15 + .../aliyuncs/oss-sdk-php/samples/Image.php | 87 + .../oss-sdk-php/samples/LiveChannel.php | 125 + .../oss-sdk-php/samples/MultipartUpload.php | 182 + .../aliyuncs/oss-sdk-php/samples/Object.php | 517 + .../aliyuncs/oss-sdk-php/samples/RunAll.php | 13 + .../oss-sdk-php/samples/Signature.php | 143 + .../oss-sdk-php/src/OSS/Core/MimeTypes.php | 262 + .../oss-sdk-php/src/OSS/Core/OssException.php | 54 + .../oss-sdk-php/src/OSS/Core/OssUtil.php | 461 + .../aliyuncs/oss-sdk-php/src/OSS/Http/LICENSE | 25 + .../oss-sdk-php/src/OSS/Http/RequestCore.php | 896 ++ .../src/OSS/Http/RequestCore_Exception.php | 8 + .../oss-sdk-php/src/OSS/Http/ResponseCore.php | 56 + .../oss-sdk-php/src/OSS/Model/BucketInfo.php | 78 + .../src/OSS/Model/BucketListInfo.php | 39 + .../oss-sdk-php/src/OSS/Model/CnameConfig.php | 99 + .../oss-sdk-php/src/OSS/Model/CorsConfig.php | 113 + .../oss-sdk-php/src/OSS/Model/CorsRule.php | 150 + .../src/OSS/Model/GetLiveChannelHistory.php | 34 + .../src/OSS/Model/GetLiveChannelInfo.php | 68 + .../src/OSS/Model/GetLiveChannelStatus.php | 107 + .../src/OSS/Model/LifecycleAction.php | 88 + .../src/OSS/Model/LifecycleConfig.php | 107 + .../src/OSS/Model/LifecycleRule.php | 126 + .../src/OSS/Model/ListMultipartUploadInfo.php | 134 + .../src/OSS/Model/ListPartsInfo.php | 97 + .../src/OSS/Model/LiveChannelConfig.php | 121 + .../src/OSS/Model/LiveChannelHistory.php | 59 + .../src/OSS/Model/LiveChannelInfo.php | 107 + .../src/OSS/Model/LiveChannelListInfo.php | 107 + .../src/OSS/Model/LoggingConfig.php | 86 + .../oss-sdk-php/src/OSS/Model/ObjectInfo.php | 93 + .../src/OSS/Model/ObjectListInfo.php | 126 + .../oss-sdk-php/src/OSS/Model/PartInfo.php | 63 + .../oss-sdk-php/src/OSS/Model/PrefixInfo.php | 36 + .../src/OSS/Model/RefererConfig.php | 93 + .../src/OSS/Model/StorageCapacityConfig.php | 74 + .../oss-sdk-php/src/OSS/Model/UploadInfo.php | 55 + .../src/OSS/Model/WebsiteConfig.php | 76 + .../oss-sdk-php/src/OSS/Model/XmlConfig.php | 27 + .../oss-sdk-php/src/OSS/OssClient.php | 2739 ++++ .../oss-sdk-php/src/OSS/Result/AclResult.php | 32 + .../src/OSS/Result/AppendResult.php | 27 + .../oss-sdk-php/src/OSS/Result/BodyResult.php | 19 + .../src/OSS/Result/CallbackResult.php | 21 + .../src/OSS/Result/CopyObjectResult.php | 30 + .../src/OSS/Result/DeleteObjectsResult.php | 27 + .../src/OSS/Result/ExistResult.php | 35 + .../src/OSS/Result/GetCnameResult.php | 19 + .../src/OSS/Result/GetCorsResult.php | 35 + .../src/OSS/Result/GetLifecycleResult.php | 41 + .../Result/GetLiveChannelHistoryResult.php | 19 + .../OSS/Result/GetLiveChannelInfoResult.php | 19 + .../OSS/Result/GetLiveChannelStatusResult.php | 19 + .../src/OSS/Result/GetLocationResult.php | 30 + .../src/OSS/Result/GetLoggingResult.php | 41 + .../src/OSS/Result/GetRefererResult.php | 41 + .../OSS/Result/GetStorageCapacityResult.php | 34 + .../src/OSS/Result/GetWebsiteResult.php | 40 + .../src/OSS/Result/HeaderResult.php | 23 + .../Result/InitiateMultipartUploadResult.php | 29 + .../src/OSS/Result/ListBucketsResult.php | 33 + .../src/OSS/Result/ListLiveChannelResult.php | 16 + .../OSS/Result/ListMultipartUploadResult.php | 55 + .../src/OSS/Result/ListObjectsResult.php | 71 + .../src/OSS/Result/ListPartsResult.php | 42 + .../src/OSS/Result/PutLiveChannelResult.php | 16 + .../src/OSS/Result/PutSetDeleteResult.php | 20 + .../oss-sdk-php/src/OSS/Result/Result.php | 175 + .../src/OSS/Result/SymlinkResult.php | 24 + .../src/OSS/Result/UploadPartResult.php | 28 + .../tests/OSS/Tests/AclResultTest.php | 59 + .../tests/OSS/Tests/BodyResultTest.php | 26 + .../tests/OSS/Tests/BucketCnameTest.php | 77 + .../tests/OSS/Tests/BucketInfoTest.php | 21 + .../tests/OSS/Tests/BucketLiveChannelTest.php | 283 + .../tests/OSS/Tests/CallbackTest.php | 297 + .../tests/OSS/Tests/CnameConfigTest.php | 77 + .../oss-sdk-php/tests/OSS/Tests/Common.php | 70 + .../tests/OSS/Tests/ContentTypeTest.php | 133 + .../tests/OSS/Tests/CopyObjectResult.php | 52 + .../tests/OSS/Tests/CorsConfigTest.php | 140 + .../tests/OSS/Tests/ExistResultTest.php | 38 + .../tests/OSS/Tests/GetCorsResultTest.php | 67 + .../OSS/Tests/GetLifecycleResultTest.php | 59 + .../tests/OSS/Tests/GetLoggingResultTest.php | 51 + .../tests/OSS/Tests/GetRefererResultTest.php | 51 + .../tests/OSS/Tests/GetWebsiteResultTest.php | 50 + .../tests/OSS/Tests/HeaderResultTest.php | 23 + .../oss-sdk-php/tests/OSS/Tests/HttpTest.php | 77 + .../InitiateMultipartUploadResultTest.php | 47 + .../tests/OSS/Tests/LifecycleConfigTest.php | 130 + .../tests/OSS/Tests/ListBucketsResultTest.php | 97 + .../Tests/ListMultipartUploadResultTest.php | 114 + .../tests/OSS/Tests/ListObjectsResultTest.php | 151 + .../tests/OSS/Tests/ListPartsResultTest.php | 62 + .../tests/OSS/Tests/LiveChannelXmlTest.php | 249 + .../tests/OSS/Tests/LoggingConfigTest.php | 47 + .../tests/OSS/Tests/MimeTypesTest.php | 13 + .../tests/OSS/Tests/ObjectAclTest.php | 28 + .../OSS/Tests/OssClientBucketCorsTest.php | 84 + .../Tests/OssClientBucketLifecycleTest.php | 57 + .../OSS/Tests/OssClientBucketLoggingTest.php | 43 + .../OSS/Tests/OssClientBucketRefererTest.php | 48 + .../OssClientBucketStorageCapacityTest.php | 56 + .../tests/OSS/Tests/OssClientBucketTest.php | 113 + .../OSS/Tests/OssClientBucketWebsiteTest.php | 46 + .../tests/OSS/Tests/OssClientImageTest.php | 100 + .../Tests/OssClientMultipartUploadTest.php | 313 + .../tests/OSS/Tests/OssClientObjectTest.php | 588 + .../OSS/Tests/OssClientRestoreObjectTest.php | 96 + .../OSS/Tests/OssClientSignatureTest.php | 111 + .../tests/OSS/Tests/OssClientTest.php | 216 + .../tests/OSS/Tests/OssExceptionTest.php | 19 + .../tests/OSS/Tests/OssUtilTest.php | 225 + .../OSS/Tests/PutSetDeleteResultTest.php | 66 + .../tests/OSS/Tests/RefererConfigTest.php | 54 + .../tests/OSS/Tests/StorageCapacityTest.php | 59 + .../tests/OSS/Tests/SymlinkTest.php | 74 + .../tests/OSS/Tests/TestOssClientBase.php | 51 + .../tests/OSS/Tests/UploadPartResultTest.php | 33 + .../tests/OSS/Tests/WebsiteConfigTest.php | 56 + source/vendor/autoload.php | 7 + source/vendor/composer/ClassLoader.php | 445 + source/vendor/composer/LICENSE | 21 + source/vendor/composer/autoload_classmap.php | 9 + source/vendor/composer/autoload_files.php | 10 + .../vendor/composer/autoload_namespaces.php | 12 + source/vendor/composer/autoload_psr4.php | 18 + source/vendor/composer/autoload_real.php | 70 + source/vendor/composer/autoload_static.php | 118 + source/vendor/composer/installed.json | 534 + source/vendor/guzzle/guzzle/.gitignore | 27 + source/vendor/guzzle/guzzle/.travis.yml | 17 + source/vendor/guzzle/guzzle/CHANGELOG.md | 751 ++ source/vendor/guzzle/guzzle/LICENSE | 19 + source/vendor/guzzle/guzzle/README.md | 57 + source/vendor/guzzle/guzzle/UPGRADING.md | 537 + source/vendor/guzzle/guzzle/build.xml | 45 + source/vendor/guzzle/guzzle/composer.json | 82 + source/vendor/guzzle/guzzle/docs/Makefile | 153 + .../docs/_downloads/guzzle-schema-1.0.json | 176 + .../guzzle/docs/_static/guzzle-icon.png | Bin 0 -> 803 bytes .../guzzle/guzzle/docs/_static/homepage.css | 122 + .../guzzle/guzzle/docs/_static/logo.png | Bin 0 -> 247678 bytes .../guzzle/guzzle/docs/_static/prettify.css | 41 + .../guzzle/guzzle/docs/_static/prettify.js | 28 + .../guzzle/guzzle/docs/_templates/index.html | 106 + .../guzzle/docs/_templates/leftbar.html | 0 .../guzzle/docs/_templates/nav_links.html | 5 + .../guzzle/guzzle/docs/batching/batching.rst | 183 + source/vendor/guzzle/guzzle/docs/conf.py | 94 + source/vendor/guzzle/guzzle/docs/docs.rst | 73 + .../guzzle/docs/getting-started/faq.rst | 29 + .../docs/getting-started/installation.rst | 154 + .../guzzle/docs/getting-started/overview.rst | 85 + .../guzzle/guzzle/docs/http-client/client.rst | 569 + .../guzzle/docs/http-client/entity-bodies.rst | 151 + .../docs/http-client/http-redirects.rst | 99 + .../guzzle/docs/http-client/request.rst | 667 + .../guzzle/docs/http-client/response.rst | 141 + .../guzzle/docs/http-client/uri-templates.rst | 52 + source/vendor/guzzle/guzzle/docs/index.rst | 5 + .../docs/iterators/guzzle-iterators.rst | 97 + .../docs/iterators/resource-iterators.rst | 149 + .../guzzle/docs/plugins/async-plugin.rst | 18 + .../guzzle/docs/plugins/backoff-plugin.rst | 22 + .../guzzle/docs/plugins/cache-plugin.rst | 169 + .../guzzle/docs/plugins/cookie-plugin.rst | 33 + .../guzzle/docs/plugins/creating-plugins.rst | 93 + .../guzzle/docs/plugins/curl-auth-plugin.rst | 32 + .../guzzle/docs/plugins/history-plugin.rst | 24 + .../guzzle/guzzle/docs/plugins/log-plugin.rst | 69 + .../docs/plugins/md5-validator-plugin.rst | 29 + .../guzzle/docs/plugins/mock-plugin.rst | 27 + .../guzzle/docs/plugins/oauth-plugin.rst | 30 + .../guzzle/docs/plugins/plugins-list.rst.inc | 9 + .../guzzle/docs/plugins/plugins-overview.rst | 59 + .../guzzle/guzzle/docs/requirements.txt | 2 + .../guzzle/docs/testing/unit-testing.rst | 201 + .../guzzle-service-descriptions.rst | 619 + .../using-the-service-builder.rst | 316 + .../webservice-client/webservice-client.rst | 659 + source/vendor/guzzle/guzzle/phar-stub.php | 16 + .../guzzle/guzzle/phing/build.properties.dist | 16 + .../guzzle/phing/imports/dependencies.xml | 33 + .../guzzle/guzzle/phing/imports/deploy.xml | 142 + .../guzzle/phing/tasks/ComposerLintTask.php | 152 + .../phing/tasks/GuzzlePearPharPackageTask.php | 338 + .../guzzle/phing/tasks/GuzzleSubSplitTask.php | 385 + source/vendor/guzzle/guzzle/phpunit.xml.dist | 48 + .../Guzzle/Batch/AbstractBatchDecorator.php | 66 + .../guzzle/guzzle/src/Guzzle/Batch/Batch.php | 92 + .../guzzle/src/Guzzle/Batch/BatchBuilder.php | 199 + .../src/Guzzle/Batch/BatchClosureDivisor.php | 39 + .../src/Guzzle/Batch/BatchClosureTransfer.php | 40 + .../src/Guzzle/Batch/BatchCommandTransfer.php | 75 + .../Guzzle/Batch/BatchDivisorInterface.php | 18 + .../src/Guzzle/Batch/BatchInterface.php | 32 + .../src/Guzzle/Batch/BatchRequestTransfer.php | 65 + .../src/Guzzle/Batch/BatchSizeDivisor.php | 47 + .../Guzzle/Batch/BatchTransferInterface.php | 16 + .../Exception/BatchTransferException.php | 90 + .../Guzzle/Batch/ExceptionBufferingBatch.php | 50 + .../guzzle/src/Guzzle/Batch/FlushingBatch.php | 60 + .../guzzle/src/Guzzle/Batch/HistoryBatch.php | 39 + .../src/Guzzle/Batch/NotifyingBatch.php | 38 + .../guzzle/src/Guzzle/Batch/composer.json | 31 + .../src/Guzzle/Cache/AbstractCacheAdapter.php | 21 + .../src/Guzzle/Cache/CacheAdapterFactory.php | 117 + .../Guzzle/Cache/CacheAdapterInterface.php | 55 + .../src/Guzzle/Cache/ClosureCacheAdapter.php | 57 + .../src/Guzzle/Cache/DoctrineCacheAdapter.php | 41 + .../src/Guzzle/Cache/NullCacheAdapter.php | 31 + .../src/Guzzle/Cache/Zf1CacheAdapter.php | 44 + .../src/Guzzle/Cache/Zf2CacheAdapter.php | 41 + .../guzzle/src/Guzzle/Cache/composer.json | 27 + .../Guzzle/Common/AbstractHasDispatcher.php | 49 + .../guzzle/src/Guzzle/Common/Collection.php | 403 + .../guzzle/guzzle/src/Guzzle/Common/Event.php | 52 + .../Exception/BadMethodCallException.php | 5 + .../Common/Exception/ExceptionCollection.php | 108 + .../Common/Exception/GuzzleException.php | 8 + .../Exception/InvalidArgumentException.php | 5 + .../Common/Exception/RuntimeException.php | 5 + .../Exception/UnexpectedValueException.php | 5 + .../src/Guzzle/Common/FromConfigInterface.php | 18 + .../Guzzle/Common/HasDispatcherInterface.php | 54 + .../src/Guzzle/Common/ToArrayInterface.php | 16 + .../guzzle/src/Guzzle/Common/Version.php | 29 + .../guzzle/src/Guzzle/Common/composer.json | 20 + .../Http/AbstractEntityBodyDecorator.php | 221 + .../src/Guzzle/Http/CachingEntityBody.php | 229 + .../guzzle/guzzle/src/Guzzle/Http/Client.php | 524 + .../src/Guzzle/Http/ClientInterface.php | 223 + .../src/Guzzle/Http/Curl/CurlHandle.php | 464 + .../guzzle/src/Guzzle/Http/Curl/CurlMulti.php | 423 + .../Guzzle/Http/Curl/CurlMultiInterface.php | 58 + .../src/Guzzle/Http/Curl/CurlMultiProxy.php | 150 + .../src/Guzzle/Http/Curl/CurlVersion.php | 66 + .../src/Guzzle/Http/Curl/RequestMediator.php | 147 + .../guzzle/src/Guzzle/Http/EntityBody.php | 201 + .../src/Guzzle/Http/EntityBodyInterface.php | 73 + .../Http/Exception/BadResponseException.php | 69 + .../ClientErrorResponseException.php | 8 + .../CouldNotRewindStreamException.php | 7 + .../Guzzle/Http/Exception/CurlException.php | 101 + .../Guzzle/Http/Exception/HttpException.php | 10 + .../Http/Exception/MultiTransferException.php | 145 + .../Http/Exception/RequestException.php | 39 + .../ServerErrorResponseException.php | 8 + .../Exception/TooManyRedirectsException.php | 5 + .../src/Guzzle/Http/IoEmittingEntityBody.php | 83 + .../Guzzle/Http/Message/AbstractMessage.php | 220 + .../Http/Message/EntityEnclosingRequest.php | 247 + .../EntityEnclosingRequestInterface.php | 137 + .../guzzle/src/Guzzle/Http/Message/Header.php | 182 + .../Http/Message/Header/CacheControl.php | 121 + .../Http/Message/Header/HeaderCollection.php | 108 + .../Http/Message/Header/HeaderFactory.php | 26 + .../Message/Header/HeaderFactoryInterface.php | 19 + .../Http/Message/Header/HeaderInterface.php | 83 + .../src/Guzzle/Http/Message/Header/Link.php | 93 + .../Guzzle/Http/Message/MessageInterface.php | 102 + .../src/Guzzle/Http/Message/PostFile.php | 124 + .../Guzzle/Http/Message/PostFileInterface.php | 83 + .../src/Guzzle/Http/Message/Request.php | 638 + .../Guzzle/Http/Message/RequestFactory.php | 359 + .../Http/Message/RequestFactoryInterface.php | 105 + .../Guzzle/Http/Message/RequestInterface.php | 318 + .../src/Guzzle/Http/Message/Response.php | 968 ++ .../guzzle/src/Guzzle/Http/Mimetypes.php | 962 ++ .../Http/QueryAggregator/CommaAggregator.php | 20 + .../QueryAggregator/DuplicateAggregator.php | 22 + .../Http/QueryAggregator/PhpAggregator.php | 27 + .../QueryAggregatorInterface.php | 22 + .../guzzle/src/Guzzle/Http/QueryString.php | 297 + .../src/Guzzle/Http/ReadLimitEntityBody.php | 122 + .../guzzle/src/Guzzle/Http/RedirectPlugin.php | 250 + .../src/Guzzle/Http/Resources/cacert.pem | 3870 ++++++ .../guzzle/src/Guzzle/Http/StaticClient.php | 157 + .../guzzle/guzzle/src/Guzzle/Http/Url.php | 554 + .../guzzle/src/Guzzle/Http/composer.json | 32 + .../src/Guzzle/Inflection/Inflector.php | 38 + .../Guzzle/Inflection/InflectorInterface.php | 27 + .../Guzzle/Inflection/MemoizingInflector.php | 70 + .../Inflection/PreComputedInflector.php | 59 + .../src/Guzzle/Inflection/composer.json | 26 + .../src/Guzzle/Iterator/AppendIterator.php | 19 + .../src/Guzzle/Iterator/ChunkedIterator.php | 56 + .../src/Guzzle/Iterator/FilterIterator.php | 36 + .../src/Guzzle/Iterator/MapIterator.php | 34 + .../Guzzle/Iterator/MethodProxyIterator.php | 27 + .../guzzle/src/Guzzle/Iterator/README.md | 25 + .../guzzle/src/Guzzle/Iterator/composer.json | 27 + .../src/Guzzle/Log/AbstractLogAdapter.php | 16 + .../guzzle/src/Guzzle/Log/ArrayLogAdapter.php | 34 + .../src/Guzzle/Log/ClosureLogAdapter.php | 23 + .../src/Guzzle/Log/LogAdapterInterface.php | 18 + .../src/Guzzle/Log/MessageFormatter.php | 179 + .../src/Guzzle/Log/MonologLogAdapter.php | 34 + .../guzzle/src/Guzzle/Log/PsrLogAdapter.php | 36 + .../guzzle/src/Guzzle/Log/Zf1LogAdapter.php | 24 + .../guzzle/src/Guzzle/Log/Zf2LogAdapter.php | 21 + .../guzzle/src/Guzzle/Log/composer.json | 29 + .../src/Guzzle/Parser/Cookie/CookieParser.php | 131 + .../Parser/Cookie/CookieParserInterface.php | 33 + .../Parser/Message/AbstractMessageParser.php | 58 + .../Guzzle/Parser/Message/MessageParser.php | 110 + .../Parser/Message/MessageParserInterface.php | 27 + .../Parser/Message/PeclHttpMessageParser.php | 48 + .../src/Guzzle/Parser/ParserRegistry.php | 75 + .../Parser/UriTemplate/PeclUriTemplate.php | 26 + .../Guzzle/Parser/UriTemplate/UriTemplate.php | 254 + .../UriTemplate/UriTemplateInterface.php | 21 + .../src/Guzzle/Parser/Url/UrlParser.php | 48 + .../Guzzle/Parser/Url/UrlParserInterface.php | 19 + .../guzzle/src/Guzzle/Parser/composer.json | 19 + .../src/Guzzle/Plugin/Async/AsyncPlugin.php | 84 + .../src/Guzzle/Plugin/Async/composer.json | 27 + .../Backoff/AbstractBackoffStrategy.php | 91 + .../AbstractErrorCodeBackoffStrategy.php | 40 + .../Guzzle/Plugin/Backoff/BackoffLogger.php | 76 + .../Guzzle/Plugin/Backoff/BackoffPlugin.php | 126 + .../Backoff/BackoffStrategyInterface.php | 30 + .../Backoff/CallbackBackoffStrategy.php | 47 + .../Backoff/ConstantBackoffStrategy.php | 34 + .../Plugin/Backoff/CurlBackoffStrategy.php | 28 + .../Backoff/ExponentialBackoffStrategy.php | 25 + .../Plugin/Backoff/HttpBackoffStrategy.php | 30 + .../Plugin/Backoff/LinearBackoffStrategy.php | 36 + .../Backoff/ReasonPhraseBackoffStrategy.php | 25 + .../Backoff/TruncatedBackoffStrategy.php | 36 + .../src/Guzzle/Plugin/Backoff/composer.json | 28 + .../Cache/CacheKeyProviderInterface.php | 11 + .../src/Guzzle/Plugin/Cache/CachePlugin.php | 353 + .../Plugin/Cache/CacheStorageInterface.php | 43 + .../Plugin/Cache/CallbackCanCacheStrategy.php | 53 + .../Cache/CanCacheStrategyInterface.php | 30 + .../Plugin/Cache/DefaultCacheKeyProvider.php | 46 + .../Plugin/Cache/DefaultCacheStorage.php | 266 + .../Plugin/Cache/DefaultCanCacheStrategy.php | 32 + .../Plugin/Cache/DefaultRevalidation.php | 174 + .../Guzzle/Plugin/Cache/DenyRevalidation.php | 19 + .../Plugin/Cache/RevalidationInterface.php | 32 + .../Guzzle/Plugin/Cache/SkipRevalidation.php | 19 + .../src/Guzzle/Plugin/Cache/composer.json | 28 + .../src/Guzzle/Plugin/Cookie/Cookie.php | 538 + .../Cookie/CookieJar/ArrayCookieJar.php | 237 + .../Cookie/CookieJar/CookieJarInterface.php | 85 + .../Plugin/Cookie/CookieJar/FileCookieJar.php | 65 + .../src/Guzzle/Plugin/Cookie/CookiePlugin.php | 70 + .../Exception/InvalidCookieException.php | 7 + .../src/Guzzle/Plugin/Cookie/composer.json | 27 + .../Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php | 46 + .../src/Guzzle/Plugin/CurlAuth/composer.json | 27 + .../ErrorResponseExceptionInterface.php | 22 + .../ErrorResponse/ErrorResponsePlugin.php | 72 + .../Exception/ErrorResponseException.php | 7 + .../Guzzle/Plugin/ErrorResponse/composer.json | 27 + .../Guzzle/Plugin/History/HistoryPlugin.php | 163 + .../src/Guzzle/Plugin/History/composer.json | 27 + .../src/Guzzle/Plugin/Log/LogPlugin.php | 161 + .../src/Guzzle/Plugin/Log/composer.json | 28 + .../Plugin/Md5/CommandContentMd5Plugin.php | 57 + .../Guzzle/Plugin/Md5/Md5ValidatorPlugin.php | 88 + .../src/Guzzle/Plugin/Md5/composer.json | 27 + .../src/Guzzle/Plugin/Mock/MockPlugin.php | 245 + .../src/Guzzle/Plugin/Mock/composer.json | 27 + .../src/Guzzle/Plugin/Oauth/OauthPlugin.php | 306 + .../src/Guzzle/Plugin/Oauth/composer.json | 27 + .../guzzle/src/Guzzle/Plugin/composer.json | 44 + .../Guzzle/Service/AbstractConfigLoader.php | 177 + .../Guzzle/Service/Builder/ServiceBuilder.php | 189 + .../Builder/ServiceBuilderInterface.php | 40 + .../Service/Builder/ServiceBuilderLoader.php | 89 + .../Guzzle/Service/CachingConfigLoader.php | 46 + .../guzzle/src/Guzzle/Service/Client.php | 297 + .../src/Guzzle/Service/ClientInterface.php | 68 + .../Service/Command/AbstractCommand.php | 390 + .../Guzzle/Service/Command/ClosureCommand.php | 41 + .../Service/Command/CommandInterface.php | 128 + .../Command/CreateResponseClassEvent.php | 32 + .../Command/DefaultRequestSerializer.php | 169 + .../Service/Command/DefaultResponseParser.php | 55 + .../Service/Command/Factory/AliasFactory.php | 39 + .../Command/Factory/CompositeFactory.php | 154 + .../Command/Factory/ConcreteClassFactory.php | 47 + .../Command/Factory/FactoryInterface.php | 21 + .../Service/Command/Factory/MapFactory.php | 27 + .../Factory/ServiceDescriptionFactory.php | 71 + .../Request/AbstractRequestVisitor.php | 69 + .../LocationVisitor/Request/BodyVisitor.php | 58 + .../LocationVisitor/Request/HeaderVisitor.php | 44 + .../LocationVisitor/Request/JsonVisitor.php | 63 + .../Request/PostFieldVisitor.php | 18 + .../Request/PostFileVisitor.php | 24 + .../LocationVisitor/Request/QueryVisitor.php | 18 + .../Request/RequestVisitorInterface.php | 31 + .../Request/ResponseBodyVisitor.php | 18 + .../LocationVisitor/Request/XmlVisitor.php | 252 + .../Response/AbstractResponseVisitor.php | 26 + .../LocationVisitor/Response/BodyVisitor.php | 23 + .../Response/HeaderVisitor.php | 50 + .../LocationVisitor/Response/JsonVisitor.php | 93 + .../Response/ReasonPhraseVisitor.php | 23 + .../Response/ResponseVisitorInterface.php | 46 + .../Response/StatusCodeVisitor.php | 23 + .../LocationVisitor/Response/XmlVisitor.php | 151 + .../LocationVisitor/VisitorFlyweight.php | 138 + .../Service/Command/OperationCommand.php | 89 + .../Command/OperationResponseParser.php | 195 + .../Command/RequestSerializerInterface.php | 21 + .../Command/ResponseClassInterface.php | 18 + .../Command/ResponseParserInterface.php | 18 + .../Guzzle/Service/ConfigLoaderInterface.php | 22 + .../Guzzle/Service/Description/Operation.php | 547 + .../Description/OperationInterface.php | 159 + .../Guzzle/Service/Description/Parameter.php | 925 ++ .../Service/Description/SchemaFormatter.php | 156 + .../Service/Description/SchemaValidator.php | 291 + .../Description/ServiceDescription.php | 271 + .../ServiceDescriptionInterface.php | 106 + .../Description/ServiceDescriptionLoader.php | 64 + .../Description/ValidatorInterface.php | 28 + .../Service/Exception/CommandException.php | 7 + .../Exception/CommandTransferException.php | 119 + .../Exception/DescriptionBuilderException.php | 7 + .../InconsistentClientTransferException.php | 38 + .../Exception/ResponseClassException.php | 9 + .../Exception/ServiceBuilderException.php | 7 + .../Exception/ServiceNotFoundException.php | 5 + .../Service/Exception/ValidationException.php | 30 + .../AbstractResourceIteratorFactory.php | 37 + .../CompositeResourceIteratorFactory.php | 67 + .../Resource/MapResourceIteratorFactory.php | 34 + .../src/Guzzle/Service/Resource/Model.php | 64 + .../Service/Resource/ResourceIterator.php | 254 + .../Resource/ResourceIteratorApplyBatched.php | 111 + .../Resource/ResourceIteratorClassFactory.php | 60 + .../ResourceIteratorFactoryInterface.php | 30 + .../Resource/ResourceIteratorInterface.php | 61 + .../guzzle/src/Guzzle/Service/composer.json | 29 + .../Guzzle/Stream/PhpStreamRequestFactory.php | 284 + .../guzzle/src/Guzzle/Stream/Stream.php | 289 + .../src/Guzzle/Stream/StreamInterface.php | 218 + .../Stream/StreamRequestFactoryInterface.php | 24 + .../guzzle/src/Guzzle/Stream/composer.json | 30 + .../Batch/AbstractBatchDecoratorTest.php | 33 + .../Guzzle/Tests/Batch/BatchBuilderTest.php | 86 + .../Tests/Batch/BatchClosureDivisorTest.php | 36 + .../Tests/Batch/BatchClosureTransferTest.php | 52 + .../Tests/Batch/BatchCommandTransferTest.php | 83 + .../Tests/Batch/BatchRequestTransferTest.php | 80 + .../Tests/Batch/BatchSizeDivisorTest.php | 24 + .../tests/Guzzle/Tests/Batch/BatchTest.php | 91 + .../Batch/ExceptionBufferingBatchTest.php | 45 + .../Guzzle/Tests/Batch/FlushingBatchTest.php | 40 + .../Guzzle/Tests/Batch/HistoryBatchTest.php | 26 + .../Guzzle/Tests/Batch/NotifyingBatchTest.php | 45 + .../Tests/Cache/CacheAdapterFactoryTest.php | 64 + .../Guzzle/Tests/Cache/CacheAdapterTest.php | 68 + .../Tests/Cache/ClosureCacheAdapterTest.php | 94 + .../Tests/Cache/NullCacheAdapterTest.php | 20 + .../Tests/Cache/Zf2CacheAdapterTest.php | 58 + .../Common/AbstractHasDispatcherTest.php | 63 + .../Guzzle/Tests/Common/CollectionTest.php | 529 + .../tests/Guzzle/Tests/Common/EventTest.php | 62 + .../Exception/BatchTransferExceptionTest.php | 21 + .../Exception/ExceptionCollectionTest.php | 66 + .../tests/Guzzle/Tests/Common/VersionTest.php | 27 + .../tests/Guzzle/Tests/GuzzleTestCase.php | 235 + .../Http/AbstractEntityBodyDecoratorTest.php | 34 + .../Tests/Http/CachingEntityBodyTest.php | 249 + .../tests/Guzzle/Tests/Http/ClientTest.php | 601 + .../Guzzle/Tests/Http/Curl/CurlHandleTest.php | 947 ++ .../Tests/Http/Curl/CurlMultiProxyTest.php | 110 + .../Guzzle/Tests/Http/Curl/CurlMultiTest.php | 455 + .../Tests/Http/Curl/CurlVersionTest.php | 39 + .../Tests/Http/Curl/RequestMediatorTest.php | 67 + .../Guzzle/Tests/Http/EntityBodyTest.php | 182 + .../Http/Exception/CurlExceptionTest.php | 27 + .../Tests/Http/Exception/ExceptionTest.php | 66 + .../Exception/MultiTransferExceptionTest.php | 51 + .../Tests/Http/IoEmittingEntityBodyTest.php | 47 + .../Http/Message/AbstractMessageTest.php | 136 + .../Message/EntityEnclosingRequestTest.php | 434 + .../Http/Message/Header/HeaderFactoryTest.php | 29 + .../Tests/Http/Message/Header/LinkTest.php | 63 + .../Tests/Http/Message/HeaderComparison.php | 135 + .../Http/Message/HeaderComparisonTest.php | 115 + .../Guzzle/Tests/Http/Message/HeaderTest.php | 162 + .../Tests/Http/Message/PostFileTest.php | 88 + .../Tests/Http/Message/RequestFactoryTest.php | 616 + .../Guzzle/Tests/Http/Message/RequestTest.php | 639 + .../Tests/Http/Message/ResponseTest.php | 677 + .../tests/Guzzle/Tests/Http/MimetypesTest.php | 31 + .../QueryAggregator/CommaAggregatorTest.php | 30 + .../DuplicateAggregatorTest.php | 30 + .../QueryAggregator/PhpAggregatorTest.php | 32 + .../Guzzle/Tests/Http/QueryStringTest.php | 233 + .../Tests/Http/ReadLimitEntityBodyTest.php | 81 + .../Guzzle/Tests/Http/RedirectPluginTest.php | 277 + .../guzzle/tests/Guzzle/Tests/Http/Server.php | 191 + .../Guzzle/Tests/Http/StaticClientTest.php | 67 + .../tests/Guzzle/Tests/Http/UrlTest.php | 303 + .../guzzle/tests/Guzzle/Tests/Http/server.js | 146 + .../Guzzle/Tests/Inflection/InflectorTest.php | 37 + .../Inflection/MemoizingInflectorTest.php | 46 + .../Inflection/PreComputedInflectorTest.php | 45 + .../Tests/Iterator/AppendIteratorTest.php | 29 + .../Tests/Iterator/ChunkedIteratorTest.php | 52 + .../Tests/Iterator/FilterIteratorTest.php | 28 + .../Guzzle/Tests/Iterator/MapIteratorTest.php | 28 + .../Iterator/MethodProxyIteratorTest.php | 28 + .../Guzzle/Tests/Log/ArrayLogAdapterTest.php | 23 + .../Tests/Log/ClosureLogAdapterTest.php | 30 + .../Guzzle/Tests/Log/MessageFormatterTest.php | 143 + .../Guzzle/Tests/Log/PsrLogAdapterTest.php | 25 + .../Guzzle/Tests/Log/Zf2LogAdapterTest.php | 51 + .../Guzzle/Tests/Mock/CustomResponseModel.php | 21 + .../Guzzle/Tests/Mock/ErrorResponseMock.php | 25 + .../tests/Guzzle/Tests/Mock/ExceptionMock.php | 11 + .../tests/Guzzle/Tests/Mock/MockMulti.php | 11 + .../tests/Guzzle/Tests/Mock/MockObserver.php | 65 + .../tests/Guzzle/Tests/Mock/MockSubject.php | 7 + .../Parser/Cookie/CookieParserProvider.php | 381 + .../Tests/Parser/Cookie/CookieParserTest.php | 22 + .../Parser/Message/MessageParserProvider.php | 225 + .../Parser/Message/MessageParserTest.php | 58 + .../Message/PeclHttpMessageParserTest.php | 36 + .../Tests/Parser/ParserRegistryTest.php | 33 + .../UriTemplate/AbstractUriTemplateTest.php | 113 + .../UriTemplate/PeclUriTemplateTest.php | 27 + .../Parser/UriTemplate/UriTemplateTest.php | 106 + .../Tests/Plugin/Async/AsyncPluginTest.php | 93 + .../Backoff/AbstractBackoffStrategyTest.php | 86 + .../Plugin/Backoff/BackoffLoggerTest.php | 110 + .../Plugin/Backoff/BackoffPluginTest.php | 297 + .../Backoff/CallbackBackoffStrategyTest.php | 31 + .../Backoff/ConstantBackoffStrategyTest.php | 20 + .../Backoff/CurlBackoffStrategyTest.php | 36 + .../ExponentialBackoffStrategyTest.php | 23 + .../Backoff/HttpBackoffStrategyTest.php | 47 + .../Backoff/LinearBackoffStrategyTest.php | 21 + .../ReasonPhraseBackoffStrategyTest.php | 32 + .../Backoff/TruncatedBackoffStrategyTest.php | 30 + .../Tests/Plugin/Cache/CachePluginTest.php | 441 + .../Cache/CallbackCanCacheStrategyTest.php | 72 + .../Plugin/Cache/DefaultCacheStorageTest.php | 193 + .../Cache/DefaultCanCacheStrategyTest.php | 40 + .../Plugin/Cache/DefaultRevalidationTest.php | 248 + .../Plugin/Cache/DenyRevalidationTest.php | 19 + .../Plugin/Cache/SkipRevalidationTest.php | 19 + .../Cookie/CookieJar/ArrayCookieJarTest.php | 385 + .../Cookie/CookieJar/FileCookieJarTest.php | 63 + .../Tests/Plugin/Cookie/CookiePluginTest.php | 134 + .../Guzzle/Tests/Plugin/Cookie/CookieTest.php | 223 + .../Plugin/CurlAuth/CurlAuthPluginTest.php | 39 + .../ErrorResponse/ErrorResponsePluginTest.php | 137 + .../Plugin/History/HistoryPluginTest.php | 140 + .../Guzzle/Tests/Plugin/Log/LogPluginTest.php | 95 + .../Md5/CommandContentMd5PluginTest.php | 97 + .../Plugin/Md5/Md5ValidatorPluginTest.php | 120 + .../Tests/Plugin/Mock/MockPluginTest.php | 199 + .../Tests/Plugin/Oauth/OauthPluginTest.php | 345 + .../Service/AbstractConfigLoaderTest.php | 149 + .../Builder/ServiceBuilderLoaderTest.php | 177 + .../Service/Builder/ServiceBuilderTest.php | 317 + .../Tests/Service/CachingConfigLoaderTest.php | 43 + .../tests/Guzzle/Tests/Service/ClientTest.php | 320 + .../Service/Command/AbstractCommandTest.php | 16 + .../Service/Command/ClosureCommandTest.php | 54 + .../Tests/Service/Command/CommandTest.php | 445 + .../Command/DefaultRequestSerializerTest.php | 122 + .../Command/DefaultResponseParserTest.php | 59 + .../Command/Factory/AliasFactoryTest.php | 76 + .../Command/Factory/CompositeFactoryTest.php | 124 + .../Factory/ConcreteClassFactoryTest.php | 49 + .../Command/Factory/MapFactoryTest.php | 37 + .../Factory/ServiceDescriptionFactoryTest.php | 68 + .../Request/AbstractVisitorTestCase.php | 110 + .../Request/BodyVisitorTest.php | 63 + .../Request/HeaderVisitorTest.php | 48 + .../Request/JsonVisitorTest.php | 60 + .../Request/PostFieldVisitorTest.php | 33 + .../Request/PostFileVisitorTest.php | 54 + .../Request/QueryVisitorTest.php | 48 + .../Request/ResponseBodyVisitorTest.php | 20 + .../Request/XmlVisitorTest.php | 558 + .../Response/AbstractResponseVisitorTest.php | 29 + .../Response/BodyVisitorTest.php | 21 + .../Response/HeaderVisitorTest.php | 98 + .../Response/JsonVisitorTest.php | 157 + .../Response/ReasonPhraseVisitorTest.php | 21 + .../Response/StatusCodeVisitorTest.php | 21 + .../Response/XmlVisitorTest.php | 431 + .../LocationVisitor/VisitorFlyweightTest.php | 53 + .../Service/Command/OperationCommandTest.php | 102 + .../Command/OperationResponseParserTest.php | 335 + .../Service/Description/OperationTest.php | 308 + .../Service/Description/ParameterTest.php | 411 + .../Description/SchemaFormatterTest.php | 61 + .../Description/SchemaValidatorTest.php | 326 + .../ServiceDescriptionLoaderTest.php | 177 + .../Description/ServiceDescriptionTest.php | 240 + .../CommandTransferExceptionTest.php | 66 + ...nconsistentClientTransferExceptionTest.php | 15 + .../Exception/ValidationExceptionTest.php | 17 + .../Service/Mock/Command/IterableCommand.php | 31 + .../Service/Mock/Command/MockCommand.php | 32 + .../Service/Mock/Command/OtherCommand.php | 30 + .../Tests/Service/Mock/Command/Sub/Sub.php | 7 + .../Guzzle/Tests/Service/Mock/MockClient.php | 36 + .../Mock/Model/MockCommandIterator.php | 42 + .../CompositeResourceIteratorFactoryTest.php | 37 + .../MapResourceIteratorFactoryTest.php | 40 + .../Tests/Service/Resource/ModelTest.php | 65 + .../ResourceIteratorClassFactoryTest.php | 41 + .../Service/Resource/ResourceIteratorTest.php | 184 + .../Stream/PhpStreamRequestFactoryTest.php | 172 + .../tests/Guzzle/Tests/Stream/StreamTest.php | 189 + .../tests/Guzzle/Tests/TestData/FileBody.txt | 0 .../Tests/TestData/description/bar.json | 3 + .../Tests/TestData/description/baz.json | 3 + .../Tests/TestData/description/foo.json | 8 + .../Tests/TestData/description/recursive.json | 3 + .../tests/Guzzle/Tests/TestData/mock_response | 3 + .../Guzzle/Tests/TestData/services/json1.json | 18 + .../Guzzle/Tests/TestData/services/json2.json | 11 + .../Tests/TestData/services/services.json | 71 + .../Guzzle/Tests/TestData/test_service.json | 40 + .../Guzzle/Tests/TestData/test_service2.json | 7 + .../Guzzle/Tests/TestData/test_service_3.json | 40 + .../vendor/guzzle/guzzle/tests/bootstrap.php | 10 + source/vendor/kosinix/grafika/.travis.yml | 44 + source/vendor/kosinix/grafika/README.md | 32 + source/vendor/kosinix/grafika/composer.json | 25 + .../grafika/fonts/LiberationSans-Regular.ttf | Bin 0 -> 133828 bytes .../kosinix/grafika/fonts/st-heiti-light.ttc | Bin 0 -> 2729260 bytes source/vendor/kosinix/grafika/license.txt | 8 + source/vendor/kosinix/grafika/phpunit.xml | 18 + source/vendor/kosinix/grafika/sami.phar | Bin 0 -> 2213889 bytes source/vendor/kosinix/grafika/samiconf.php | 6 + .../kosinix/grafika/src/Grafika/Color.php | 105 + .../src/Grafika/DrawingObject/CubicBezier.php | 105 + .../src/Grafika/DrawingObject/Ellipse.php | 128 + .../src/Grafika/DrawingObject/Line.php | 86 + .../src/Grafika/DrawingObject/Polygon.php | 116 + .../Grafika/DrawingObject/QuadraticBezier.php | 89 + .../src/Grafika/DrawingObject/Rectangle.php | 126 + .../src/Grafika/DrawingObjectInterface.php | 17 + .../grafika/src/Grafika/EditorInterface.php | 255 + .../grafika/src/Grafika/FilterInterface.php | 17 + .../Grafika/Gd/DrawingObject/CubicBezier.php | 412 + .../src/Grafika/Gd/DrawingObject/Ellipse.php | 42 + .../src/Grafika/Gd/DrawingObject/Line.php | 36 + .../src/Grafika/Gd/DrawingObject/Polygon.php | 64 + .../Gd/DrawingObject/QuadraticBezier.php | 208 + .../Grafika/Gd/DrawingObject/Rectangle.php | 36 + .../kosinix/grafika/src/Grafika/Gd/Editor.php | 1179 ++ .../grafika/src/Grafika/Gd/Filter/Blur.php | 40 + .../src/Grafika/Gd/Filter/Brightness.php | 39 + .../src/Grafika/Gd/Filter/Colorize.php | 50 + .../src/Grafika/Gd/Filter/Contrast.php | 38 + .../grafika/src/Grafika/Gd/Filter/Dither.php | 190 + .../grafika/src/Grafika/Gd/Filter/Gamma.php | 38 + .../src/Grafika/Gd/Filter/Grayscale.php | 23 + .../grafika/src/Grafika/Gd/Filter/Invert.php | 24 + .../src/Grafika/Gd/Filter/Pixelate.php | 38 + .../grafika/src/Grafika/Gd/Filter/Sharpen.php | 49 + .../grafika/src/Grafika/Gd/Filter/Sobel.php | 128 + .../src/Grafika/Gd/Helper/GifByteStream.php | 123 + .../src/Grafika/Gd/Helper/GifHelper.php | 605 + .../kosinix/grafika/src/Grafika/Gd/Image.php | 457 + .../src/Grafika/Gd/ImageHash/AverageHash.php | 71 + .../Grafika/Gd/ImageHash/DifferenceHash.php | 73 + .../kosinix/grafika/src/Grafika/Grafika.php | 394 + .../grafika/src/Grafika/ImageInterface.php | 80 + .../kosinix/grafika/src/Grafika/ImageType.php | 21 + .../Imagick/DrawingObject/CubicBezier.php | 51 + .../Grafika/Imagick/DrawingObject/Ellipse.php | 40 + .../Grafika/Imagick/DrawingObject/Line.php | 41 + .../Grafika/Imagick/DrawingObject/Polygon.php | 52 + .../Imagick/DrawingObject/QuadraticBezier.php | 55 + .../Imagick/DrawingObject/Rectangle.php | 46 + .../grafika/src/Grafika/Imagick/Editor.php | 827 ++ .../src/Grafika/Imagick/Filter/Blur.php | 37 + .../src/Grafika/Imagick/Filter/Brightness.php | 39 + .../src/Grafika/Imagick/Filter/Colorize.php | 67 + .../src/Grafika/Imagick/Filter/Contrast.php | 38 + .../src/Grafika/Imagick/Filter/Dither.php | 168 + .../src/Grafika/Imagick/Filter/Gamma.php | 38 + .../src/Grafika/Imagick/Filter/Grayscale.php | 23 + .../src/Grafika/Imagick/Filter/Invert.php | 24 + .../src/Grafika/Imagick/Filter/Pixelate.php | 42 + .../src/Grafika/Imagick/Filter/Sharpen.php | 37 + .../src/Grafika/Imagick/Filter/Sobel.php | 142 + .../grafika/src/Grafika/Imagick/Image.php | 269 + .../Grafika/Imagick/ImageHash/AverageHash.php | 37 + .../Imagick/ImageHash/DifferenceHash.php | 67 + .../kosinix/grafika/src/Grafika/Position.php | 146 + .../vendor/kosinix/grafika/src/autoloader.php | 10 + .../kosinix/grafika/tests/GdEditorTest.php | 1039 ++ .../grafika/tests/ImagickEditorTest.php | 1027 ++ .../kosinix/grafika/tests/bootstrap.php | 40 + .../vendor/kosinix/grafika/tests/install.txt | 2 + source/vendor/lvht/geohash/.gitignore | 1 + source/vendor/lvht/geohash/LICENSE | 20 + source/vendor/lvht/geohash/README.md | 68 + source/vendor/lvht/geohash/composer.json | 23 + source/vendor/lvht/geohash/src/GeoHash.php | 159 + source/vendor/myclabs/php-enum/.gitattributes | 6 + source/vendor/myclabs/php-enum/.gitignore | 6 + source/vendor/myclabs/php-enum/LICENSE | 18 + source/vendor/myclabs/php-enum/README.md | 128 + source/vendor/myclabs/php-enum/composer.json | 31 + source/vendor/myclabs/php-enum/src/Enum.php | 198 + source/vendor/qcloud/cos-sdk-v5/.gitignore | 2 + source/vendor/qcloud/cos-sdk-v5/.travis.yml | 6 + source/vendor/qcloud/cos-sdk-v5/LICENSE | 21 + source/vendor/qcloud/cos-sdk-v5/README.md | 257 + source/vendor/qcloud/cos-sdk-v5/composer.json | 27 + source/vendor/qcloud/cos-sdk-v5/phpunit.xml | 7 + source/vendor/qcloud/cos-sdk-v5/sample.php | 824 ++ .../src/Qcloud/Cos/BucketStyleListener.php | 74 + .../cos-sdk-v5/src/Qcloud/Cos/Client.php | 270 + .../cos-sdk-v5/src/Qcloud/Cos/Command.php | 39 + .../qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php | 140 + .../BucketAlreadyExistsException.php | 5 + .../Cos/Exception/BucketNotEmptyException.php | 6 + .../src/Qcloud/Cos/Exception/CosException.php | 7 + .../Qcloud/Cos/Exception/CurlException.php | 5 + .../Exception/InvalidArgumentException.php | 5 + .../Cos/Exception/NoSuchBucketException.php | 6 + .../Cos/Exception/NoSuchKeyException.php | 6 + .../Cos/Exception/NoSuchUploadException.php | 8 + .../Exception/ServiceResponseException.php | 189 + .../src/Qcloud/Cos/ExceptionListener.php | 69 + .../src/Qcloud/Cos/ExceptionParser.php | 112 + .../cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php | 57 + .../src/Qcloud/Cos/MultipartUpload.php | 132 + .../cos-sdk-v5/src/Qcloud/Cos/Service.php | 4328 ++++++ .../cos-sdk-v5/src/Qcloud/Cos/Signature.php | 50 + .../src/Qcloud/Cos/SignatureListener.php | 45 + .../cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php | 1367 ++ .../src/Qcloud/Cos/Tests/TestHelper.php | 48 + .../src/Qcloud/Cos/TokenListener.php | 48 + .../src/Qcloud/Cos/UploadBodyListener.php | 79 + source/vendor/qiniu/php-sdk/.gitignore | 12 + source/vendor/qiniu/php-sdk/.scrutinizer.yml | 35 + source/vendor/qiniu/php-sdk/.travis.yml | 23 + source/vendor/qiniu/php-sdk/CHANGELOG.md | 105 + source/vendor/qiniu/php-sdk/CONTRIBUTING.md | 30 + source/vendor/qiniu/php-sdk/LICENSE | 22 + source/vendor/qiniu/php-sdk/README.md | 75 + source/vendor/qiniu/php-sdk/autoload.php | 14 + source/vendor/qiniu/php-sdk/composer.json | 26 + .../vendor/qiniu/php-sdk/docs/rtc/README.md | 71 + .../vendor/qiniu/php-sdk/docs/rtc/example.php | 42 + .../vendor/qiniu/php-sdk/examples/README.md | 10 + .../php-sdk/examples/cdn_get_bandwidth.php | 40 + .../qiniu/php-sdk/examples/cdn_get_flux.php | 34 + .../php-sdk/examples/cdn_get_log_list.php | 29 + .../examples/cdn_refresh_urls_dirs.php | 52 + .../examples/cdn_timestamp_antileech.php | 19 + .../php-sdk/examples/image_url_builder.php | 74 + .../php-sdk/examples/persistent_fop_init.php | 19 + .../examples/persistent_fop_status.php | 18 + .../qiniu/php-sdk/examples/pfop_mkzip.php | 43 + .../qiniu/php-sdk/examples/pfop_vframe.php | 46 + .../php-sdk/examples/pfop_video_avthumb.php | 47 + .../qiniu/php-sdk/examples/pfop_watermark.php | 52 + .../qiniu/php-sdk/examples/php-logo.png | Bin 0 -> 65062 bytes .../vendor/qiniu/php-sdk/examples/prefop.php | 29 + .../qiniu/php-sdk/examples/pulpvideo.php | 55 + .../vendor/qiniu/php-sdk/examples/qetag.php | 11 + .../php-sdk/examples/rs_batch_change_mime.php | 28 + .../php-sdk/examples/rs_batch_change_type.php | 34 + .../qiniu/php-sdk/examples/rs_batch_copy.php | 36 + .../php-sdk/examples/rs_batch_delete.php | 28 + .../examples/rs_batch_delete_after_days.php | 34 + .../qiniu/php-sdk/examples/rs_batch_move.php | 36 + .../qiniu/php-sdk/examples/rs_batch_stat.php | 28 + .../php-sdk/examples/rs_bucket_domains.php | 19 + .../qiniu/php-sdk/examples/rs_buckets.php | 19 + .../qiniu/php-sdk/examples/rs_change_mime.php | 20 + .../php-sdk/examples/rs_change_status.php | 20 + .../qiniu/php-sdk/examples/rs_change_type.php | 20 + .../vendor/qiniu/php-sdk/examples/rs_copy.php | 22 + .../qiniu/php-sdk/examples/rs_delete.php | 17 + .../php-sdk/examples/rs_delete_after_days.php | 20 + .../php-sdk/examples/rs_download_urls.php | 17 + .../qiniu/php-sdk/examples/rs_fetch.php | 34 + .../vendor/qiniu/php-sdk/examples/rs_move.php | 22 + .../qiniu/php-sdk/examples/rs_prefetch.php | 17 + .../vendor/qiniu/php-sdk/examples/rs_stat.php | 19 + .../php-sdk/examples/rsf_list_bucket.php | 46 + .../qiniu/php-sdk/examples/rsf_list_files.php | 38 + .../vendor/qiniu/php-sdk/examples/saveas.php | 28 + .../php-sdk/examples/upload_and_callback.php | 40 + .../php-sdk/examples/upload_and_pfop.php | 38 + .../php-sdk/examples/upload_mgr_init.php | 18 + .../php-sdk/examples/upload_multi_demos.php | 85 + .../php-sdk/examples/upload_simple_file.php | 37 + .../qiniu/php-sdk/examples/upload_tokens.php | 68 + .../examples/upload_verify_callback.php | 32 + source/vendor/qiniu/php-sdk/phpunit.xml.dist | 19 + .../vendor/qiniu/php-sdk/src/Qiniu/Auth.php | 187 + .../php-sdk/src/Qiniu/Cdn/CdnManager.php | 191 + .../vendor/qiniu/php-sdk/src/Qiniu/Config.php | 137 + .../vendor/qiniu/php-sdk/src/Qiniu/Etag.php | 76 + .../qiniu/php-sdk/src/Qiniu/Http/Client.php | 156 + .../qiniu/php-sdk/src/Qiniu/Http/Error.php | 35 + .../qiniu/php-sdk/src/Qiniu/Http/Request.php | 18 + .../qiniu/php-sdk/src/Qiniu/Http/Response.php | 176 + .../src/Qiniu/Processing/ImageUrlBuilder.php | 282 + .../src/Qiniu/Processing/Operation.php | 60 + .../src/Qiniu/Processing/PersistentFop.php | 94 + .../qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php | 204 + .../src/Qiniu/Storage/ArgusManager.php | 73 + .../src/Qiniu/Storage/BucketManager.php | 492 + .../src/Qiniu/Storage/FormUploader.php | 139 + .../src/Qiniu/Storage/ResumeUploader.php | 169 + .../src/Qiniu/Storage/UploadManager.php | 144 + .../vendor/qiniu/php-sdk/src/Qiniu/Zone.php | 197 + .../qiniu/php-sdk/src/Qiniu/functions.php | 264 + source/vendor/qiniu/php-sdk/test-env.sh | 4 + .../php-sdk/tests/Qiniu/Tests/AuthTest.php | 71 + .../php-sdk/tests/Qiniu/Tests/Base64Test.php | 14 + .../php-sdk/tests/Qiniu/Tests/BucketTest.php | 227 + .../tests/Qiniu/Tests/CdnManagerTest.php | 50 + .../php-sdk/tests/Qiniu/Tests/Crc32Test.php | 21 + .../tests/Qiniu/Tests/DownloadTest.php | 25 + .../php-sdk/tests/Qiniu/Tests/EtagTest.php | 52 + .../php-sdk/tests/Qiniu/Tests/FopTest.php | 37 + .../php-sdk/tests/Qiniu/Tests/FormUpTest.php | 59 + .../php-sdk/tests/Qiniu/Tests/HttpTest.php | 43 + .../tests/Qiniu/Tests/ImageUrlBuilderTest.php | 261 + .../php-sdk/tests/Qiniu/Tests/PfopTest.php | 66 + .../tests/Qiniu/Tests/ResumeUpTest.php | 60 + .../php-sdk/tests/Qiniu/Tests/ZoneTest.php | 59 + .../vendor/qiniu/php-sdk/tests/bootstrap.php | 43 + .../symfony/event-dispatcher/.gitignore | 3 + .../symfony/event-dispatcher/CHANGELOG.md | 23 + .../ContainerAwareEventDispatcher.php | 183 + .../Debug/TraceableEventDispatcher.php | 375 + .../TraceableEventDispatcherInterface.php | 34 + .../Debug/WrappedListener.php | 71 + .../RegisterListenersPass.php | 100 + .../vendor/symfony/event-dispatcher/Event.php | 120 + .../event-dispatcher/EventDispatcher.php | 198 + .../EventDispatcherInterface.php | 81 + .../EventSubscriberInterface.php | 46 + .../symfony/event-dispatcher/GenericEvent.php | 175 + .../ImmutableEventDispatcher.php | 91 + .../vendor/symfony/event-dispatcher/LICENSE | 19 + .../vendor/symfony/event-dispatcher/README.md | 15 + .../Tests/AbstractEventDispatcherTest.php | 398 + .../ContainerAwareEventDispatcherTest.php | 277 + .../Debug/TraceableEventDispatcherTest.php | 254 + .../RegisterListenersPassTest.php | 154 + .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 97 + .../Tests/GenericEventTest.php | 136 + .../Tests/ImmutableEventDispatcherTest.php | 106 + .../symfony/event-dispatcher/composer.json | 44 + .../symfony/event-dispatcher/phpunit.xml.dist | 31 + .../topthink/think-installer/.gitignore | 3 + .../topthink/think-installer/composer.json | 25 + .../topthink/think-installer/src/Plugin.php | 26 + .../think-installer/src/ThinkExtend.php | 77 + .../think-installer/src/ThinkFramework.php | 59 + .../think-installer/src/ThinkTesting.php | 68 + version.json | 3 + web/assets/admin/css/app.css | 1170 ++ web/assets/admin/css/login/style.css | 231 + web/assets/admin/img/login_bg.jpg | Bin 0 -> 244652 bytes web/assets/admin/img/login_bg1.jpg | Bin 0 -> 39353 bytes web/assets/admin/js/app.js | 153 + web/assets/admin/scss/app.scss | 1525 +++ web/assets/api/dealer-bg.png | Bin 0 -> 18771 bytes .../common/css/amazeui.datatables.min.css | 1 + web/assets/common/css/amazeui.min.css | 1 + web/assets/common/css/fullcalendar.min.css | 5 + web/assets/common/css/fullcalendar.print.css | 208 + web/assets/common/fonts/FontAwesome.otf | Bin 0 -> 124988 bytes .../common/fonts/fontawesome-webfont.eot | Bin 0 -> 76518 bytes .../common/fonts/fontawesome-webfont.ttf | Bin 0 -> 152796 bytes .../common/fonts/fontawesome-webfont.woff | Bin 0 -> 90412 bytes .../common/fonts/fontawesome-webfont.woff2 | Bin 0 -> 71896 bytes web/assets/common/i/favicon.ico | Bin 0 -> 318 bytes web/assets/common/js/Sortable.min.js | 3 + web/assets/common/js/amazeui.min.js | 8 + web/assets/common/js/art-template.js | 3 + web/assets/common/js/ddsort.js | 263 + web/assets/common/js/echarts-walden.js | 511 + web/assets/common/js/echarts.min.js | 35 + web/assets/common/js/jquery.dad.js | 307 + web/assets/common/js/jquery.form.min.js | 1 + web/assets/common/js/jquery.min.js | 6 + web/assets/common/js/jstree.min.js | 5 + web/assets/common/js/vue.min.js | 6 + web/assets/common/js/vuedraggable.min.js | 1 + web/assets/common/js/webuploader.html5only.js | 6030 +++++++++ web/assets/common/plugins/laydate/laydate.js | 2 + .../laydate/theme/default/font/iconfont.eot | Bin 0 -> 2456 bytes .../laydate/theme/default/font/iconfont.svg | 45 + .../laydate/theme/default/font/iconfont.ttf | Bin 0 -> 2272 bytes .../laydate/theme/default/font/iconfont.woff | Bin 0 -> 1492 bytes .../plugins/laydate/theme/default/laydate.css | 477 + web/assets/common/plugins/layer/layer.js | 2 + .../common/plugins/layer/mobile/layer.js | 2 + .../plugins/layer/mobile/need/layer.css | 1 + .../plugins/layer/theme/default/icon-ext.png | Bin 0 -> 5911 bytes .../plugins/layer/theme/default/icon.png | Bin 0 -> 11493 bytes .../plugins/layer/theme/default/layer.css | 208 + .../plugins/layer/theme/default/loading-0.gif | Bin 0 -> 5793 bytes .../plugins/layer/theme/default/loading-1.gif | Bin 0 -> 701 bytes .../plugins/layer/theme/default/loading-2.gif | Bin 0 -> 1787 bytes .../umeditor/dialogs/emotion/emotion.css | 87 + .../umeditor/dialogs/emotion/emotion.js | 272 + .../umeditor/dialogs/emotion/images/0.gif | Bin 0 -> 43 bytes .../umeditor/dialogs/emotion/images/bface.gif | Bin 0 -> 27167 bytes .../umeditor/dialogs/emotion/images/cface.gif | Bin 0 -> 8603 bytes .../umeditor/dialogs/emotion/images/fface.gif | Bin 0 -> 18479 bytes .../dialogs/emotion/images/jxface2.gif | Bin 0 -> 40706 bytes .../emotion/images/neweditor-tab-bg.png | Bin 0 -> 216 bytes .../umeditor/dialogs/emotion/images/tface.gif | Bin 0 -> 19805 bytes .../umeditor/dialogs/emotion/images/wface.gif | Bin 0 -> 49850 bytes .../umeditor/dialogs/emotion/images/yface.gif | Bin 0 -> 28409 bytes .../umeditor/dialogs/formula/formula.css | 32 + .../umeditor/dialogs/formula/formula.html | 212 + .../umeditor/dialogs/formula/formula.js | 124 + .../dialogs/formula/images/formula.png | Bin 0 -> 19301 bytes .../plugins/umeditor/dialogs/image/image.css | 42 + .../plugins/umeditor/dialogs/image/image.js | 454 + .../umeditor/dialogs/image/images/close.png | Bin 0 -> 1042 bytes .../umeditor/dialogs/image/images/upload1.png | Bin 0 -> 848 bytes .../umeditor/dialogs/image/images/upload2.png | Bin 0 -> 1910 bytes .../plugins/umeditor/dialogs/link/link.js | 73 + .../plugins/umeditor/dialogs/map/map.html | 148 + .../plugins/umeditor/dialogs/map/map.js | 263 + .../dialogs/video/images/center_focus.jpg | Bin 0 -> 11795 bytes .../dialogs/video/images/left_focus.jpg | Bin 0 -> 11423 bytes .../dialogs/video/images/none_focus.jpg | Bin 0 -> 11546 bytes .../dialogs/video/images/right_focus.jpg | Bin 0 -> 11334 bytes .../plugins/umeditor/dialogs/video/video.css | 170 + .../plugins/umeditor/dialogs/video/video.js | 314 + .../common/plugins/umeditor/lang/en/en.js | 150 + .../umeditor/lang/en/images/addimage.png | Bin 0 -> 3373 bytes .../lang/en/images/alldeletebtnhoverskin.png | Bin 0 -> 743 bytes .../lang/en/images/alldeletebtnupskin.png | Bin 0 -> 743 bytes .../umeditor/lang/en/images/background.png | Bin 0 -> 3854 bytes .../umeditor/lang/en/images/button.png | Bin 0 -> 4929 bytes .../plugins/umeditor/lang/en/images/copy.png | Bin 0 -> 1222 bytes .../umeditor/lang/en/images/deletedisable.png | Bin 0 -> 649 bytes .../umeditor/lang/en/images/deleteenable.png | Bin 0 -> 664 bytes .../umeditor/lang/en/images/imglabel.png | Bin 0 -> 672 bytes .../lang/en/images/listbackground.png | Bin 0 -> 3750 bytes .../umeditor/lang/en/images/localimage.png | Bin 0 -> 3083 bytes .../plugins/umeditor/lang/en/images/music.png | Bin 0 -> 91561 bytes .../lang/en/images/rotateleftdisable.png | Bin 0 -> 719 bytes .../lang/en/images/rotateleftenable.png | Bin 0 -> 952 bytes .../lang/en/images/rotaterightdisable.png | Bin 0 -> 754 bytes .../lang/en/images/rotaterightenable.png | Bin 0 -> 1007 bytes .../umeditor/lang/en/images/upload.png | Bin 0 -> 3941 bytes .../umeditor/lang/zh-cn/images/copy.png | Bin 0 -> 4319 bytes .../umeditor/lang/zh-cn/images/imglabel.png | Bin 0 -> 2973 bytes .../umeditor/lang/zh-cn/images/localimage.png | Bin 0 -> 6979 bytes .../umeditor/lang/zh-cn/images/music.png | Bin 0 -> 23106 bytes .../umeditor/lang/zh-cn/images/upload.png | Bin 0 -> 6608 bytes .../plugins/umeditor/lang/zh-cn/zh-cn.js | 150 + .../umeditor/themes/default/css/umeditor.css | 1 + .../themes/default/css/umeditor.min.css | 8 + .../umeditor/themes/default/images/caret.png | Bin 0 -> 293 bytes .../umeditor/themes/default/images/close.png | Bin 0 -> 291 bytes .../umeditor/themes/default/images/icons.gif | Bin 0 -> 20371 bytes .../umeditor/themes/default/images/icons.png | Bin 0 -> 41374 bytes .../umeditor/themes/default/images/ok.gif | Bin 0 -> 866 bytes .../umeditor/themes/default/images/pop-bg.png | Bin 0 -> 1003 bytes .../umeditor/themes/default/images/spacer.gif | Bin 0 -> 43 bytes .../themes/default/images/videologo.gif | Bin 0 -> 1604 bytes .../plugins/umeditor/umeditor.config.js | 255 + .../common/plugins/umeditor/umeditor.js | 10958 ++++++++++++++++ .../common/plugins/umeditor/umeditor.min.js | 41 + web/assets/store/css/app.css | 2264 ++++ web/assets/store/css/diy.css | 949 ++ web/assets/store/css/goods.css | 159 + web/assets/store/css/login/style.css | 232 + web/assets/store/img/category_tpl/10.png | Bin 0 -> 528002 bytes web/assets/store/img/category_tpl/11.png | Bin 0 -> 278554 bytes web/assets/store/img/category_tpl/20.png | Bin 0 -> 217670 bytes web/assets/store/img/chose.png | Bin 0 -> 674 bytes web/assets/store/img/dealer/avatar.png | Bin 0 -> 3934 bytes web/assets/store/img/dealer/backdrop.png | Bin 0 -> 248367 bytes web/assets/store/img/dealer/qrcode.png | Bin 0 -> 44432 bytes web/assets/store/img/diy/article/01.png | Bin 0 -> 1893 bytes web/assets/store/img/diy/banner/01.png | Bin 0 -> 6499 bytes web/assets/store/img/diy/circular.png | Bin 0 -> 2374 bytes web/assets/store/img/diy/goods/01.png | Bin 0 -> 3814 bytes web/assets/store/img/diy/navbar/01.png | Bin 0 -> 2374 bytes web/assets/store/img/diy/notice.png | Bin 0 -> 1139 bytes web/assets/store/img/diy/phone-top-black.png | Bin 0 -> 4420 bytes web/assets/store/img/diy/phone-top-white.png | Bin 0 -> 4108 bytes web/assets/store/img/diy/service.png | Bin 0 -> 6767 bytes web/assets/store/img/diy/special.png | Bin 0 -> 5486 bytes web/assets/store/img/diy/video_poster.png | Bin 0 -> 38563 bytes web/assets/store/img/diy/window/01.jpg | Bin 0 -> 4582 bytes web/assets/store/img/diy/window/02.jpg | Bin 0 -> 4988 bytes web/assets/store/img/diy/window/03.jpg | Bin 0 -> 4203 bytes web/assets/store/img/diy/window/04.jpg | Bin 0 -> 4434 bytes web/assets/store/img/login/login_bg.jpg | Bin 0 -> 567536 bytes web/assets/store/img/login/logo.png | Bin 0 -> 6834 bytes .../store/img/statistics/ranking/01.png | Bin 0 -> 2166 bytes .../store/img/statistics/ranking/02.png | Bin 0 -> 2426 bytes .../store/img/statistics/ranking/03.png | Bin 0 -> 2419 bytes web/assets/store/img/statistics/survey/01.png | Bin 0 -> 1092 bytes web/assets/store/img/statistics/survey/02.png | Bin 0 -> 1512 bytes web/assets/store/img/statistics/survey/03.png | Bin 0 -> 6209 bytes web/assets/store/img/statistics/survey/04.png | Bin 0 -> 4311 bytes web/assets/store/img/statistics/survey/05.png | Bin 0 -> 4582 bytes web/assets/store/img/tpl_msg/01.png | Bin 0 -> 70722 bytes web/assets/store/img/tpl_msg/02.png | Bin 0 -> 77170 bytes web/assets/store/img/tpl_msg/03_01.png | Bin 0 -> 63020 bytes web/assets/store/img/tpl_msg/04.png | Bin 0 -> 88510 bytes web/assets/store/js/app.js | 467 + web/assets/store/js/delivery.js | 313 + web/assets/store/js/diy.js | 296 + web/assets/store/js/file.library.js | 500 + web/assets/store/js/goods.spec.js | 301 + web/assets/store/js/select.data.js | 132 + web/assets/store/js/select.region.js | 69 + web/assets/store/scss/app.scss | 3080 +++++ web/assets/store/scss/diy.scss | 1367 ++ web/assets/store/scss/goods.scss | 230 + web/index.php | 12 + web/notice.php | 15 + web/temp/.gitignore | 2 + web/uploads/.gitignore | 2 + 安装说明..md | 148 + 1992 files changed, 270528 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 doc/database/install.sql create mode 100644 doc/database/upgrade/v1.1.0.sql create mode 100644 doc/database/upgrade/v1.1.1.sql create mode 100644 doc/database/upgrade/v1.1.12.sql create mode 100644 doc/database/upgrade/v1.1.13.sql create mode 100644 doc/database/upgrade/v1.1.16.sql create mode 100644 doc/database/upgrade/v1.1.17.sql create mode 100644 doc/database/upgrade/v1.1.18.sql create mode 100644 doc/database/upgrade/v1.1.19.sql create mode 100644 doc/database/upgrade/v1.1.21.sql create mode 100644 doc/database/upgrade/v1.1.22.sql create mode 100644 doc/database/upgrade/v1.1.23.sql create mode 100644 doc/database/upgrade/v1.1.24.sql create mode 100644 doc/database/upgrade/v1.1.25.sql create mode 100644 doc/database/upgrade/v1.1.26.sql create mode 100644 doc/database/upgrade/v1.1.27.sql create mode 100644 doc/database/upgrade/v1.1.29.sql create mode 100644 doc/database/upgrade/v1.1.3.sql create mode 100644 doc/database/upgrade/v1.1.32.sql create mode 100644 doc/database/upgrade/v1.1.35.sql create mode 100644 doc/database/upgrade/v1.1.36.sql create mode 100644 doc/database/upgrade/v1.1.37.sql create mode 100644 doc/database/upgrade/v1.1.4.sql create mode 100644 doc/database/upgrade/v1.1.6.sql create mode 100644 doc/database/upgrade/v1.1.7.sql create mode 100644 doc/database/upgrade/v1.1.8.sql create mode 100644 doc/database/upgrade/v1.1.9.sql create mode 100644 doc/更新日志.txt create mode 100644 icon.jpg create mode 100644 source/application/admin/config.php create mode 100644 source/application/admin/controller/Controller.php create mode 100644 source/application/admin/controller/Index.php create mode 100644 source/application/admin/controller/Passport.php create mode 100644 source/application/admin/controller/Store.php create mode 100644 source/application/admin/controller/admin/User.php create mode 100644 source/application/admin/controller/setting/Cache.php create mode 100644 source/application/admin/controller/setting/Science.php create mode 100644 source/application/admin/controller/store/Access.php create mode 100644 source/application/admin/extra/menus.php create mode 100644 source/application/admin/model/Setting.php create mode 100644 source/application/admin/model/Wxapp.php create mode 100644 source/application/admin/model/WxappCategory.php create mode 100644 source/application/admin/model/WxappHelp.php create mode 100644 source/application/admin/model/WxappPage.php create mode 100644 source/application/admin/model/admin/User.php create mode 100644 source/application/admin/model/store/Access.php create mode 100644 source/application/admin/model/store/User.php create mode 100644 source/application/admin/view/admin/user/renew.php create mode 100644 source/application/admin/view/index/index.php create mode 100644 source/application/admin/view/layouts/layout.php create mode 100644 source/application/admin/view/passport/login.php create mode 100644 source/application/admin/view/setting/cache/clear.php create mode 100644 source/application/admin/view/setting/science/index.php create mode 100644 source/application/admin/view/store/access/add.php create mode 100644 source/application/admin/view/store/access/edit.php create mode 100644 source/application/admin/view/store/access/index.php create mode 100644 source/application/admin/view/store/add.php create mode 100644 source/application/admin/view/store/index.php create mode 100644 source/application/admin/view/store/recycle.php create mode 100644 source/application/api/behavior/order/PaySuccess.php create mode 100644 source/application/api/config.php create mode 100644 source/application/api/controller/Address.php create mode 100644 source/application/api/controller/Article.php create mode 100644 source/application/api/controller/Cart.php create mode 100644 source/application/api/controller/Category.php create mode 100644 source/application/api/controller/Comment.php create mode 100644 source/application/api/controller/Controller.php create mode 100644 source/application/api/controller/Coupon.php create mode 100644 source/application/api/controller/Goods.php create mode 100644 source/application/api/controller/Notify.php create mode 100644 source/application/api/controller/Order.php create mode 100644 source/application/api/controller/Page.php create mode 100644 source/application/api/controller/Recharge.php create mode 100644 source/application/api/controller/Shop.php create mode 100644 source/application/api/controller/Upload.php create mode 100644 source/application/api/controller/User.php create mode 100644 source/application/api/controller/Wxapp.php create mode 100644 source/application/api/controller/balance/Log.php create mode 100644 source/application/api/controller/bargain/Active.php create mode 100644 source/application/api/controller/bargain/Order.php create mode 100644 source/application/api/controller/bargain/Task.php create mode 100644 source/application/api/controller/points/Log.php create mode 100644 source/application/api/controller/recharge/Order.php create mode 100644 source/application/api/controller/sharing/Active.php create mode 100644 source/application/api/controller/sharing/Comment.php create mode 100644 source/application/api/controller/sharing/Goods.php create mode 100644 source/application/api/controller/sharing/Index.php create mode 100644 source/application/api/controller/sharing/Order.php create mode 100644 source/application/api/controller/sharing/Refund.php create mode 100644 source/application/api/controller/sharing/Setting.php create mode 100644 source/application/api/controller/sharp/Goods.php create mode 100644 source/application/api/controller/sharp/Index.php create mode 100644 source/application/api/controller/sharp/Order.php create mode 100644 source/application/api/controller/shop/Order.php create mode 100644 source/application/api/controller/user/Comment.php create mode 100644 source/application/api/controller/user/Coupon.php create mode 100644 source/application/api/controller/user/Dealer.php create mode 100644 source/application/api/controller/user/Index.php create mode 100644 source/application/api/controller/user/Order.php create mode 100644 source/application/api/controller/user/Refund.php create mode 100644 source/application/api/controller/user/Wallet.php create mode 100644 source/application/api/controller/user/dealer/Apply.php create mode 100644 source/application/api/controller/user/dealer/Order.php create mode 100644 source/application/api/controller/user/dealer/Qrcode.php create mode 100644 source/application/api/controller/user/dealer/Team.php create mode 100644 source/application/api/controller/user/dealer/Withdraw.php create mode 100644 source/application/api/controller/wxapp/Formid.php create mode 100644 source/application/api/model/Article.php create mode 100644 source/application/api/model/Cart.php create mode 100644 source/application/api/model/Category.php create mode 100644 source/application/api/model/Comment.php create mode 100644 source/application/api/model/CommentImage.php create mode 100644 source/application/api/model/Coupon.php create mode 100644 source/application/api/model/Delivery.php create mode 100644 source/application/api/model/DeliveryRule.php create mode 100644 source/application/api/model/Express.php create mode 100644 source/application/api/model/Goods.php create mode 100644 source/application/api/model/GoodsImage.php create mode 100644 source/application/api/model/GoodsSku.php create mode 100644 source/application/api/model/GoodsSpecRel.php create mode 100644 source/application/api/model/Order.php create mode 100644 source/application/api/model/OrderAddress.php create mode 100644 source/application/api/model/OrderExtract.php create mode 100644 source/application/api/model/OrderGoods.php create mode 100644 source/application/api/model/OrderRefund.php create mode 100644 source/application/api/model/OrderRefundAddress.php create mode 100644 source/application/api/model/OrderRefundImage.php create mode 100644 source/application/api/model/Setting.php create mode 100644 source/application/api/model/Spec.php create mode 100644 source/application/api/model/SpecValue.php create mode 100644 source/application/api/model/UploadFile.php create mode 100644 source/application/api/model/User.php create mode 100644 source/application/api/model/UserAddress.php create mode 100644 source/application/api/model/UserCoupon.php create mode 100644 source/application/api/model/Wxapp.php create mode 100644 source/application/api/model/WxappCategory.php create mode 100644 source/application/api/model/WxappHelp.php create mode 100644 source/application/api/model/WxappNavbar.php create mode 100644 source/application/api/model/WxappPage.php create mode 100644 source/application/api/model/WxappPrepayId.php create mode 100644 source/application/api/model/article/Category.php create mode 100644 source/application/api/model/bargain/Active.php create mode 100644 source/application/api/model/bargain/Setting.php create mode 100644 source/application/api/model/bargain/Task.php create mode 100644 source/application/api/model/bargain/TaskHelp.php create mode 100644 source/application/api/model/dealer/Apply.php create mode 100644 source/application/api/model/dealer/Capital.php create mode 100644 source/application/api/model/dealer/Order.php create mode 100644 source/application/api/model/dealer/Referee.php create mode 100644 source/application/api/model/dealer/Setting.php create mode 100644 source/application/api/model/dealer/User.php create mode 100644 source/application/api/model/dealer/Withdraw.php create mode 100644 source/application/api/model/recharge/Order.php create mode 100644 source/application/api/model/recharge/OrderPlan.php create mode 100644 source/application/api/model/recharge/Plan.php create mode 100644 source/application/api/model/sharing/Active.php create mode 100644 source/application/api/model/sharing/ActiveUsers.php create mode 100644 source/application/api/model/sharing/Category.php create mode 100644 source/application/api/model/sharing/Comment.php create mode 100644 source/application/api/model/sharing/CommentImage.php create mode 100644 source/application/api/model/sharing/Goods.php create mode 100644 source/application/api/model/sharing/GoodsImage.php create mode 100644 source/application/api/model/sharing/GoodsSku.php create mode 100644 source/application/api/model/sharing/GoodsSpecRel.php create mode 100644 source/application/api/model/sharing/Order.php create mode 100644 source/application/api/model/sharing/OrderAddress.php create mode 100644 source/application/api/model/sharing/OrderExtract.php create mode 100644 source/application/api/model/sharing/OrderGoods.php create mode 100644 source/application/api/model/sharing/OrderRefund.php create mode 100644 source/application/api/model/sharing/OrderRefundAddress.php create mode 100644 source/application/api/model/sharing/OrderRefundImage.php create mode 100644 source/application/api/model/sharing/Setting.php create mode 100644 source/application/api/model/sharp/Active.php create mode 100644 source/application/api/model/sharp/ActiveGoods.php create mode 100644 source/application/api/model/sharp/ActiveTime.php create mode 100644 source/application/api/model/sharp/Goods.php create mode 100644 source/application/api/model/sharp/GoodsSku.php create mode 100644 source/application/api/model/sharp/Setting.php create mode 100644 source/application/api/model/store/Shop.php create mode 100644 source/application/api/model/store/shop/Clerk.php create mode 100644 source/application/api/model/store/shop/ClerkRel.php create mode 100644 source/application/api/model/store/shop/Order.php create mode 100644 source/application/api/model/user/BalanceLog.php create mode 100644 source/application/api/model/user/Grade.php create mode 100644 source/application/api/model/user/GradeLog.php create mode 100644 source/application/api/model/user/PointsLog.php create mode 100644 source/application/api/model/wow/Order.php create mode 100644 source/application/api/model/wow/Setting.php create mode 100644 source/application/api/model/wow/Shoping.php create mode 100644 source/application/api/model/wxapp/Formid.php create mode 100644 source/application/api/service/Basics.php create mode 100644 source/application/api/service/Goods.php create mode 100644 source/application/api/service/Payment.php create mode 100644 source/application/api/service/User.php create mode 100644 source/application/api/service/bargain/Amount.php create mode 100644 source/application/api/service/bargain/order/PaySuccess.php create mode 100644 source/application/api/service/coupon/GoodsDeduct.php create mode 100644 source/application/api/service/master/order/PaySuccess.php create mode 100644 source/application/api/service/order/Checkout.php create mode 100644 source/application/api/service/order/PaySuccess.php create mode 100644 source/application/api/service/order/source/Bargain.php create mode 100644 source/application/api/service/order/source/Basics.php create mode 100644 source/application/api/service/order/source/Factory.php create mode 100644 source/application/api/service/order/source/Master.php create mode 100644 source/application/api/service/order/source/Sharp.php create mode 100644 source/application/api/service/order/source/checkout/Bargain.php create mode 100644 source/application/api/service/order/source/checkout/Basics.php create mode 100644 source/application/api/service/order/source/checkout/Factory.php create mode 100644 source/application/api/service/order/source/checkout/Master.php create mode 100644 source/application/api/service/order/source/checkout/Sharp.php create mode 100644 source/application/api/service/points/GoodsDeduct.php create mode 100644 source/application/api/service/recharge/PaySuccess.php create mode 100644 source/application/api/service/sharing/order/Checkout.php create mode 100644 source/application/api/service/sharing/order/PaySuccess.php create mode 100644 source/application/api/service/sharp/Active.php create mode 100644 source/application/api/service/sharp/Order.php create mode 100644 source/application/api/service/sharp/order/PaySuccess.php create mode 100644 source/application/api/tags.php create mode 100644 source/application/api/validate/order/Checkout.php create mode 100644 source/application/api/validate/sharing/order/Checkout.php create mode 100644 source/application/common.php create mode 100644 source/application/common/behavior/App.php create mode 100644 source/application/common/enum/DeliveryType.php create mode 100644 source/application/common/enum/EnumBasics.php create mode 100644 source/application/common/enum/OrderStatus.php create mode 100644 source/application/common/enum/OrderType.php create mode 100644 source/application/common/enum/PrinterType.php create mode 100644 source/application/common/enum/Setting.php create mode 100644 source/application/common/enum/goods/DeductStockType.php create mode 100644 source/application/common/enum/order/OrderSource.php create mode 100644 source/application/common/enum/order/PayStatus.php create mode 100644 source/application/common/enum/order/PayType.php create mode 100644 source/application/common/enum/order/Status.php create mode 100644 source/application/common/enum/recharge/order/PayStatus.php create mode 100644 source/application/common/enum/recharge/order/RechargeType.php create mode 100644 source/application/common/enum/sharp/ActiveStatus.php create mode 100644 source/application/common/enum/sharp/GoodsStatus.php create mode 100644 source/application/common/enum/user/balanceLog/Scene.php create mode 100644 source/application/common/enum/user/grade/log/ChangeType.php create mode 100644 source/application/common/exception/BaseException.php create mode 100644 source/application/common/exception/ExceptionHandler.php create mode 100644 source/application/common/library/Lock.php create mode 100644 source/application/common/library/express/Kuaidi100.php create mode 100644 source/application/common/library/helper.php create mode 100644 source/application/common/library/printer/Driver.php create mode 100644 source/application/common/library/printer/engine/Basics.php create mode 100644 source/application/common/library/printer/engine/Feie.php create mode 100644 source/application/common/library/printer/engine/PrintCenter.php create mode 100644 source/application/common/library/printer/party/FeieHttpClient.php create mode 100644 source/application/common/library/sms/Driver.php create mode 100644 source/application/common/library/sms/engine/Aliyun.php create mode 100644 source/application/common/library/sms/engine/Server.php create mode 100644 source/application/common/library/sms/package/aliyun/SignatureHelper.php create mode 100644 source/application/common/library/storage/Driver.php create mode 100644 source/application/common/library/storage/engine/Aliyun.php create mode 100644 source/application/common/library/storage/engine/Local.php create mode 100644 source/application/common/library/storage/engine/Qcloud.php create mode 100644 source/application/common/library/storage/engine/Qiniu.php create mode 100644 source/application/common/library/storage/engine/Server.php create mode 100644 source/application/common/library/wechat/Qrcode.php create mode 100644 source/application/common/library/wechat/WxBase.php create mode 100644 source/application/common/library/wechat/WxPay.php create mode 100644 source/application/common/library/wechat/WxTplMsg.php create mode 100644 source/application/common/library/wechat/WxUser.php create mode 100644 source/application/common/library/wechat/cert/.gitignore create mode 100644 source/application/common/library/wechat/logs/.gitignore create mode 100644 source/application/common/library/wechat/wow/Order.php create mode 100644 source/application/common/library/wechat/wow/Shoping.php create mode 100644 source/application/common/model/Article.php create mode 100644 source/application/common/model/BaseModel.php create mode 100644 source/application/common/model/Category.php create mode 100644 source/application/common/model/Comment.php create mode 100644 source/application/common/model/CommentImage.php create mode 100644 source/application/common/model/Coupon.php create mode 100644 source/application/common/model/Delivery.php create mode 100644 source/application/common/model/DeliveryRule.php create mode 100644 source/application/common/model/Express.php create mode 100644 source/application/common/model/Goods.php create mode 100644 source/application/common/model/GoodsImage.php create mode 100644 source/application/common/model/GoodsSku.php create mode 100644 source/application/common/model/GoodsSpecRel.php create mode 100644 source/application/common/model/Order.php create mode 100644 source/application/common/model/OrderAddress.php create mode 100644 source/application/common/model/OrderExtract.php create mode 100644 source/application/common/model/OrderGoods.php create mode 100644 source/application/common/model/OrderRefund.php create mode 100644 source/application/common/model/OrderRefundAddress.php create mode 100644 source/application/common/model/OrderRefundImage.php create mode 100644 source/application/common/model/Printer.php create mode 100644 source/application/common/model/Region.php create mode 100644 source/application/common/model/ReturnAddress.php create mode 100644 source/application/common/model/Setting.php create mode 100644 source/application/common/model/Spec.php create mode 100644 source/application/common/model/SpecValue.php create mode 100644 source/application/common/model/Store.php create mode 100644 source/application/common/model/UploadFile.php create mode 100644 source/application/common/model/UploadFileUsed.php create mode 100644 source/application/common/model/UploadGroup.php create mode 100644 source/application/common/model/User.php create mode 100644 source/application/common/model/UserAddress.php create mode 100644 source/application/common/model/UserCoupon.php create mode 100644 source/application/common/model/Wxapp.php create mode 100644 source/application/common/model/WxappCategory.php create mode 100644 source/application/common/model/WxappHelp.php create mode 100644 source/application/common/model/WxappNavbar.php create mode 100644 source/application/common/model/WxappPage.php create mode 100644 source/application/common/model/WxappPrepayId.php create mode 100644 source/application/common/model/admin/User.php create mode 100644 source/application/common/model/article/Category.php create mode 100644 source/application/common/model/bargain/Active.php create mode 100644 source/application/common/model/bargain/Setting.php create mode 100644 source/application/common/model/bargain/Task.php create mode 100644 source/application/common/model/bargain/TaskHelp.php create mode 100644 source/application/common/model/dealer/Apply.php create mode 100644 source/application/common/model/dealer/Capital.php create mode 100644 source/application/common/model/dealer/Order.php create mode 100644 source/application/common/model/dealer/Referee.php create mode 100644 source/application/common/model/dealer/Setting.php create mode 100644 source/application/common/model/dealer/User.php create mode 100644 source/application/common/model/dealer/Withdraw.php create mode 100644 source/application/common/model/recharge/Order.php create mode 100644 source/application/common/model/recharge/OrderPlan.php create mode 100644 source/application/common/model/recharge/Plan.php create mode 100644 source/application/common/model/sharing/Active.php create mode 100644 source/application/common/model/sharing/ActiveUsers.php create mode 100644 source/application/common/model/sharing/Category.php create mode 100644 source/application/common/model/sharing/Comment.php create mode 100644 source/application/common/model/sharing/CommentImage.php create mode 100644 source/application/common/model/sharing/Goods.php create mode 100644 source/application/common/model/sharing/GoodsImage.php create mode 100644 source/application/common/model/sharing/GoodsSku.php create mode 100644 source/application/common/model/sharing/GoodsSpecRel.php create mode 100644 source/application/common/model/sharing/Order.php create mode 100644 source/application/common/model/sharing/OrderAddress.php create mode 100644 source/application/common/model/sharing/OrderExtract.php create mode 100644 source/application/common/model/sharing/OrderGoods.php create mode 100644 source/application/common/model/sharing/OrderRefund.php create mode 100644 source/application/common/model/sharing/OrderRefundAddress.php create mode 100644 source/application/common/model/sharing/OrderRefundImage.php create mode 100644 source/application/common/model/sharing/Setting.php create mode 100644 source/application/common/model/sharp/Active.php create mode 100644 source/application/common/model/sharp/ActiveGoods.php create mode 100644 source/application/common/model/sharp/ActiveTime.php create mode 100644 source/application/common/model/sharp/Goods.php create mode 100644 source/application/common/model/sharp/GoodsSku.php create mode 100644 source/application/common/model/sharp/Setting.php create mode 100644 source/application/common/model/store/Access.php create mode 100644 source/application/common/model/store/Role.php create mode 100644 source/application/common/model/store/RoleAccess.php create mode 100644 source/application/common/model/store/Shop.php create mode 100644 source/application/common/model/store/User.php create mode 100644 source/application/common/model/store/UserRole.php create mode 100644 source/application/common/model/store/shop/Clerk.php create mode 100644 source/application/common/model/store/shop/Order.php create mode 100644 source/application/common/model/user/BalanceLog.php create mode 100644 source/application/common/model/user/Grade.php create mode 100644 source/application/common/model/user/GradeLog.php create mode 100644 source/application/common/model/user/PointsLog.php create mode 100644 source/application/common/model/wow/Order.php create mode 100644 source/application/common/model/wow/Setting.php create mode 100644 source/application/common/model/wow/Shoping.php create mode 100644 source/application/common/model/wxapp/Formid.php create mode 100644 source/application/common/service/Basics.php create mode 100644 source/application/common/service/Goods.php create mode 100644 source/application/common/service/Message.php create mode 100644 source/application/common/service/Order.php create mode 100644 source/application/common/service/delivery/Express.php create mode 100644 source/application/common/service/goods/source/Bargain.php create mode 100644 source/application/common/service/goods/source/Basics.php create mode 100644 source/application/common/service/goods/source/Factory.php create mode 100644 source/application/common/service/goods/source/Master.php create mode 100644 source/application/common/service/goods/source/Sharp.php create mode 100644 source/application/common/service/order/Complete.php create mode 100644 source/application/common/service/order/Printer.php create mode 100644 source/application/common/service/order/Refund.php create mode 100644 source/application/common/service/order/Source.php create mode 100644 source/application/common/service/qrcode/Base.php create mode 100644 source/application/common/service/qrcode/Extract.php create mode 100644 source/application/common/service/qrcode/Goods.php create mode 100644 source/application/common/service/qrcode/Poster.php create mode 100644 source/application/common/service/qrcode/bargain/Goods.php create mode 100644 source/application/common/service/qrcode/resource/goods_bg.png create mode 100644 source/application/common/service/qrcode/sharp/Goods.php create mode 100644 source/application/common/service/wechat/wow/Order.php create mode 100644 source/application/common/service/wechat/wow/Shoping.php create mode 100644 source/application/common/service/wxapp/FormId.php create mode 100644 source/application/config.php create mode 100644 source/application/database.php create mode 100644 source/application/route.php create mode 100644 source/application/store/common.php create mode 100644 source/application/store/config.php create mode 100644 source/application/store/controller/Controller.php create mode 100644 source/application/store/controller/Goods.php create mode 100644 source/application/store/controller/Index.php create mode 100644 source/application/store/controller/Order.php create mode 100644 source/application/store/controller/Passport.php create mode 100644 source/application/store/controller/Setting.php create mode 100644 source/application/store/controller/Shop.php create mode 100644 source/application/store/controller/Upload.php create mode 100644 source/application/store/controller/User.php create mode 100644 source/application/store/controller/Wxapp.php create mode 100644 source/application/store/controller/apps/bargain/Active.php create mode 100644 source/application/store/controller/apps/bargain/Setting.php create mode 100644 source/application/store/controller/apps/bargain/Task.php create mode 100644 source/application/store/controller/apps/dealer/Apply.php create mode 100644 source/application/store/controller/apps/dealer/Order.php create mode 100644 source/application/store/controller/apps/dealer/Setting.php create mode 100644 source/application/store/controller/apps/dealer/User.php create mode 100644 source/application/store/controller/apps/dealer/Withdraw.php create mode 100644 source/application/store/controller/apps/sharing/Active.php create mode 100644 source/application/store/controller/apps/sharing/Category.php create mode 100644 source/application/store/controller/apps/sharing/Comment.php create mode 100644 source/application/store/controller/apps/sharing/Goods.php create mode 100644 source/application/store/controller/apps/sharing/Order.php create mode 100644 source/application/store/controller/apps/sharing/Setting.php create mode 100644 source/application/store/controller/apps/sharing/order/Operate.php create mode 100644 source/application/store/controller/apps/sharing/order/Refund.php create mode 100644 source/application/store/controller/apps/sharp/Active.php create mode 100644 source/application/store/controller/apps/sharp/ActiveTime.php create mode 100644 source/application/store/controller/apps/sharp/Goods.php create mode 100644 source/application/store/controller/apps/sharp/Setting.php create mode 100644 source/application/store/controller/apps/wow/Order.php create mode 100644 source/application/store/controller/apps/wow/Setting.php create mode 100644 source/application/store/controller/apps/wow/Shoping.php create mode 100644 source/application/store/controller/content/Article.php create mode 100644 source/application/store/controller/content/Files.php create mode 100644 source/application/store/controller/content/article/Category.php create mode 100644 source/application/store/controller/content/files/Group.php create mode 100644 source/application/store/controller/data/Goods.php create mode 100644 source/application/store/controller/data/Shop.php create mode 100644 source/application/store/controller/data/User.php create mode 100644 source/application/store/controller/data/bargain/Goods.php create mode 100644 source/application/store/controller/data/sharing/Goods.php create mode 100644 source/application/store/controller/data/sharp/Goods.php create mode 100644 source/application/store/controller/goods/Category.php create mode 100644 source/application/store/controller/goods/Comment.php create mode 100644 source/application/store/controller/goods/Spec.php create mode 100644 source/application/store/controller/market/Basic.php create mode 100644 source/application/store/controller/market/Coupon.php create mode 100644 source/application/store/controller/market/Points.php create mode 100644 source/application/store/controller/market/Push.php create mode 100644 source/application/store/controller/market/Recharge.php create mode 100644 source/application/store/controller/market/recharge/Plan.php create mode 100644 source/application/store/controller/order/Operate.php create mode 100644 source/application/store/controller/order/Refund.php create mode 100644 source/application/store/controller/setting/Address.php create mode 100644 source/application/store/controller/setting/Cache.php create mode 100644 source/application/store/controller/setting/Delivery.php create mode 100644 source/application/store/controller/setting/Express.php create mode 100644 source/application/store/controller/setting/Help.php create mode 100644 source/application/store/controller/setting/Printer.php create mode 100644 source/application/store/controller/shop/Clerk.php create mode 100644 source/application/store/controller/shop/Order.php create mode 100644 source/application/store/controller/statistics/Data.php create mode 100644 source/application/store/controller/store/Role.php create mode 100644 source/application/store/controller/store/User.php create mode 100644 source/application/store/controller/upload/Library.php create mode 100644 source/application/store/controller/user/Balance.php create mode 100644 source/application/store/controller/user/Grade.php create mode 100644 source/application/store/controller/user/Recharge.php create mode 100644 source/application/store/controller/wxapp/Help.php create mode 100644 source/application/store/controller/wxapp/Page.php create mode 100644 source/application/store/extra/menus.php create mode 100644 source/application/store/model/Article.php create mode 100644 source/application/store/model/Category.php create mode 100644 source/application/store/model/Comment.php create mode 100644 source/application/store/model/CommentImage.php create mode 100644 source/application/store/model/Coupon.php create mode 100644 source/application/store/model/Delivery.php create mode 100644 source/application/store/model/DeliveryRule.php create mode 100644 source/application/store/model/Express.php create mode 100644 source/application/store/model/Goods.php create mode 100644 source/application/store/model/GoodsImage.php create mode 100644 source/application/store/model/GoodsSku.php create mode 100644 source/application/store/model/GoodsSpecRel.php create mode 100644 source/application/store/model/Order.php create mode 100644 source/application/store/model/OrderAddress.php create mode 100644 source/application/store/model/OrderExtract.php create mode 100644 source/application/store/model/OrderGoods.php create mode 100644 source/application/store/model/OrderRefund.php create mode 100644 source/application/store/model/OrderRefundAddress.php create mode 100644 source/application/store/model/OrderRefundImage.php create mode 100644 source/application/store/model/Printer.php create mode 100644 source/application/store/model/Region.php create mode 100644 source/application/store/model/ReturnAddress.php create mode 100644 source/application/store/model/Setting.php create mode 100644 source/application/store/model/Spec.php create mode 100644 source/application/store/model/SpecValue.php create mode 100644 source/application/store/model/Store.php create mode 100644 source/application/store/model/UploadFile.php create mode 100644 source/application/store/model/UploadFileUsed.php create mode 100644 source/application/store/model/UploadGroup.php create mode 100644 source/application/store/model/User.php create mode 100644 source/application/store/model/UserAddress.php create mode 100644 source/application/store/model/UserCoupon.php create mode 100644 source/application/store/model/Wxapp.php create mode 100644 source/application/store/model/WxappCategory.php create mode 100644 source/application/store/model/WxappHelp.php create mode 100644 source/application/store/model/WxappNavbar.php create mode 100644 source/application/store/model/WxappPage.php create mode 100644 source/application/store/model/article/Category.php create mode 100644 source/application/store/model/bargain/Active.php create mode 100644 source/application/store/model/bargain/Setting.php create mode 100644 source/application/store/model/bargain/Task.php create mode 100644 source/application/store/model/bargain/TaskHelp.php create mode 100644 source/application/store/model/dealer/Apply.php create mode 100644 source/application/store/model/dealer/Capital.php create mode 100644 source/application/store/model/dealer/Order.php create mode 100644 source/application/store/model/dealer/Referee.php create mode 100644 source/application/store/model/dealer/Setting.php create mode 100644 source/application/store/model/dealer/User.php create mode 100644 source/application/store/model/dealer/Withdraw.php create mode 100644 source/application/store/model/recharge/Order.php create mode 100644 source/application/store/model/recharge/OrderPlan.php create mode 100644 source/application/store/model/recharge/Plan.php create mode 100644 source/application/store/model/sharing/Active.php create mode 100644 source/application/store/model/sharing/ActiveUsers.php create mode 100644 source/application/store/model/sharing/Category.php create mode 100644 source/application/store/model/sharing/Comment.php create mode 100644 source/application/store/model/sharing/CommentImage.php create mode 100644 source/application/store/model/sharing/Goods.php create mode 100644 source/application/store/model/sharing/GoodsImage.php create mode 100644 source/application/store/model/sharing/GoodsSku.php create mode 100644 source/application/store/model/sharing/GoodsSpecRel.php create mode 100644 source/application/store/model/sharing/Order.php create mode 100644 source/application/store/model/sharing/OrderAddress.php create mode 100644 source/application/store/model/sharing/OrderExtract.php create mode 100644 source/application/store/model/sharing/OrderGoods.php create mode 100644 source/application/store/model/sharing/OrderRefund.php create mode 100644 source/application/store/model/sharing/OrderRefundAddress.php create mode 100644 source/application/store/model/sharing/OrderRefundImage.php create mode 100644 source/application/store/model/sharing/Setting.php create mode 100644 source/application/store/model/sharp/Active.php create mode 100644 source/application/store/model/sharp/ActiveGoods.php create mode 100644 source/application/store/model/sharp/ActiveTime.php create mode 100644 source/application/store/model/sharp/Goods.php create mode 100644 source/application/store/model/sharp/GoodsSku.php create mode 100644 source/application/store/model/sharp/Setting.php create mode 100644 source/application/store/model/store/Access.php create mode 100644 source/application/store/model/store/Role.php create mode 100644 source/application/store/model/store/RoleAccess.php create mode 100644 source/application/store/model/store/Shop.php create mode 100644 source/application/store/model/store/User.php create mode 100644 source/application/store/model/store/UserRole.php create mode 100644 source/application/store/model/store/shop/Clerk.php create mode 100644 source/application/store/model/store/shop/Order.php create mode 100644 source/application/store/model/user/BalanceLog.php create mode 100644 source/application/store/model/user/Grade.php create mode 100644 source/application/store/model/user/GradeLog.php create mode 100644 source/application/store/model/user/PointsLog.php create mode 100644 source/application/store/model/wow/Order.php create mode 100644 source/application/store/model/wow/Setting.php create mode 100644 source/application/store/model/wow/Shoping.php create mode 100644 source/application/store/model/wxapp/Formid.php create mode 100644 source/application/store/service/Auth.php create mode 100644 source/application/store/service/Goods.php create mode 100644 source/application/store/service/Menus.php create mode 100644 source/application/store/service/goods/Apply.php create mode 100644 source/application/store/service/order/Export.php create mode 100644 source/application/store/service/statistics/Data.php create mode 100644 source/application/store/service/statistics/data/GoodsRanking.php create mode 100644 source/application/store/service/statistics/data/Survey.php create mode 100644 source/application/store/service/statistics/data/Trade7days.php create mode 100644 source/application/store/service/statistics/data/UserExpendRanking.php create mode 100644 source/application/store/service/wxapp/Message.php create mode 100644 source/application/store/view/apps/bargain/active/add.php create mode 100644 source/application/store/view/apps/bargain/active/edit.php create mode 100644 source/application/store/view/apps/bargain/active/index.php create mode 100644 source/application/store/view/apps/bargain/setting/index.php create mode 100644 source/application/store/view/apps/bargain/task/help.php create mode 100644 source/application/store/view/apps/bargain/task/index.php create mode 100644 source/application/store/view/apps/dealer/apply/index.php create mode 100644 source/application/store/view/apps/dealer/order/index.php create mode 100644 source/application/store/view/apps/dealer/setting/index.php create mode 100644 source/application/store/view/apps/dealer/setting/qrcode.php create mode 100644 source/application/store/view/apps/dealer/user/edit.php create mode 100644 source/application/store/view/apps/dealer/user/fans.php create mode 100644 source/application/store/view/apps/dealer/user/index.php create mode 100644 source/application/store/view/apps/dealer/withdraw/index.php create mode 100644 source/application/store/view/apps/sharing/active/index.php create mode 100644 source/application/store/view/apps/sharing/active/users.php create mode 100644 source/application/store/view/apps/sharing/category/add.php create mode 100644 source/application/store/view/apps/sharing/category/edit.php create mode 100644 source/application/store/view/apps/sharing/category/index.php create mode 100644 source/application/store/view/apps/sharing/comment/detail.php create mode 100644 source/application/store/view/apps/sharing/comment/index.php create mode 100644 source/application/store/view/apps/sharing/goods/add.php create mode 100644 source/application/store/view/apps/sharing/goods/copy_master.php create mode 100644 source/application/store/view/apps/sharing/goods/edit.php create mode 100644 source/application/store/view/apps/sharing/goods/index.php create mode 100644 source/application/store/view/apps/sharing/order/detail.php create mode 100644 source/application/store/view/apps/sharing/order/index.php create mode 100644 source/application/store/view/apps/sharing/order/operate/batchDelivery.php create mode 100644 source/application/store/view/apps/sharing/order/refund/detail.php create mode 100644 source/application/store/view/apps/sharing/order/refund/index.php create mode 100644 source/application/store/view/apps/sharing/setting/index.php create mode 100644 source/application/store/view/apps/sharp/active/add.php create mode 100644 source/application/store/view/apps/sharp/active/index.php create mode 100644 source/application/store/view/apps/sharp/active_time/add.php create mode 100644 source/application/store/view/apps/sharp/active_time/edit.php create mode 100644 source/application/store/view/apps/sharp/active_time/index.php create mode 100644 source/application/store/view/apps/sharp/goods/edit.php create mode 100644 source/application/store/view/apps/sharp/goods/index.php create mode 100644 source/application/store/view/apps/sharp/goods/step1.php create mode 100644 source/application/store/view/apps/sharp/goods/step2.php create mode 100644 source/application/store/view/apps/sharp/setting/index.php create mode 100644 source/application/store/view/apps/wow/order/index.php create mode 100644 source/application/store/view/apps/wow/setting/index.php create mode 100644 source/application/store/view/apps/wow/shoping/index.php create mode 100644 source/application/store/view/content/article/add.php create mode 100644 source/application/store/view/content/article/category/add.php create mode 100644 source/application/store/view/content/article/category/edit.php create mode 100644 source/application/store/view/content/article/category/index.php create mode 100644 source/application/store/view/content/article/edit.php create mode 100644 source/application/store/view/content/article/index.php create mode 100644 source/application/store/view/content/files/group/add.php create mode 100644 source/application/store/view/content/files/group/edit.php create mode 100644 source/application/store/view/content/files/group/index.php create mode 100644 source/application/store/view/content/files/index.php create mode 100644 source/application/store/view/content/files/recycle.php create mode 100644 source/application/store/view/data/bargain/goods/list.php create mode 100644 source/application/store/view/data/goods/list.php create mode 100644 source/application/store/view/data/sharing/goods/list.php create mode 100644 source/application/store/view/data/sharp/goods/list.php create mode 100644 source/application/store/view/data/shop/list.php create mode 100644 source/application/store/view/data/user/list.php create mode 100644 source/application/store/view/goods/_template/spec_many.php create mode 100644 source/application/store/view/goods/add.php create mode 100644 source/application/store/view/goods/category/add.php create mode 100644 source/application/store/view/goods/category/edit.php create mode 100644 source/application/store/view/goods/category/index.php create mode 100644 source/application/store/view/goods/comment/detail.php create mode 100644 source/application/store/view/goods/comment/index.php create mode 100644 source/application/store/view/goods/edit.php create mode 100644 source/application/store/view/goods/index.php create mode 100644 source/application/store/view/index/index.php create mode 100644 source/application/store/view/layouts/_template/file_library.php create mode 100644 source/application/store/view/layouts/_template/tpl_file_item.php create mode 100644 source/application/store/view/layouts/error.php create mode 100644 source/application/store/view/layouts/layout.php create mode 100644 source/application/store/view/market/basic/full_free.php create mode 100644 source/application/store/view/market/coupon/add.php create mode 100644 source/application/store/view/market/coupon/edit.php create mode 100644 source/application/store/view/market/coupon/index.php create mode 100644 source/application/store/view/market/coupon/receive.php create mode 100644 source/application/store/view/market/points/log.php create mode 100644 source/application/store/view/market/points/setting.php create mode 100644 source/application/store/view/market/push/send.php create mode 100644 source/application/store/view/market/push/user.php create mode 100644 source/application/store/view/market/recharge/plan/add.php create mode 100644 source/application/store/view/market/recharge/plan/edit.php create mode 100644 source/application/store/view/market/recharge/plan/index.php create mode 100644 source/application/store/view/market/recharge/setting.php create mode 100644 source/application/store/view/order/detail.php create mode 100644 source/application/store/view/order/index.php create mode 100644 source/application/store/view/order/operate/batchDelivery.php create mode 100644 source/application/store/view/order/refund/detail.php create mode 100644 source/application/store/view/order/refund/index.php create mode 100644 source/application/store/view/passport/login.php create mode 100644 source/application/store/view/setting/address/add.php create mode 100644 source/application/store/view/setting/address/edit.php create mode 100644 source/application/store/view/setting/address/index.php create mode 100644 source/application/store/view/setting/cache/clear.php create mode 100644 source/application/store/view/setting/delivery/add.php create mode 100644 source/application/store/view/setting/delivery/index.php create mode 100644 source/application/store/view/setting/express/add.php create mode 100644 source/application/store/view/setting/express/company.php create mode 100644 source/application/store/view/setting/express/edit.php create mode 100644 source/application/store/view/setting/express/index.php create mode 100644 source/application/store/view/setting/help/tplMsg.php create mode 100644 source/application/store/view/setting/printer.php create mode 100644 source/application/store/view/setting/printer/add.php create mode 100644 source/application/store/view/setting/printer/edit.php create mode 100644 source/application/store/view/setting/printer/index.php create mode 100644 source/application/store/view/setting/sms.php create mode 100644 source/application/store/view/setting/storage.php create mode 100644 source/application/store/view/setting/store.php create mode 100644 source/application/store/view/setting/tplMsg.php create mode 100644 source/application/store/view/setting/trade.php create mode 100644 source/application/store/view/shop/add.php create mode 100644 source/application/store/view/shop/clerk/add.php create mode 100644 source/application/store/view/shop/clerk/edit.php create mode 100644 source/application/store/view/shop/clerk/index.php create mode 100644 source/application/store/view/shop/edit.php create mode 100644 source/application/store/view/shop/getpoint.php create mode 100644 source/application/store/view/shop/index.php create mode 100644 source/application/store/view/shop/order/index.php create mode 100644 source/application/store/view/statistics/data/index.php create mode 100644 source/application/store/view/store/role/add.php create mode 100644 source/application/store/view/store/role/edit.php create mode 100644 source/application/store/view/store/role/index.php create mode 100644 source/application/store/view/store/user/add.php create mode 100644 source/application/store/view/store/user/edit.php create mode 100644 source/application/store/view/store/user/index.php create mode 100644 source/application/store/view/store/user/renew.php create mode 100644 source/application/store/view/user/balance/log.php create mode 100644 source/application/store/view/user/grade/add.php create mode 100644 source/application/store/view/user/grade/edit.php create mode 100644 source/application/store/view/user/grade/index.php create mode 100644 source/application/store/view/user/index.php create mode 100644 source/application/store/view/user/recharge/order.php create mode 100644 source/application/store/view/wxapp/custom/index.php create mode 100644 source/application/store/view/wxapp/help/add.php create mode 100644 source/application/store/view/wxapp/help/edit.php create mode 100644 source/application/store/view/wxapp/help/index.php create mode 100644 source/application/store/view/wxapp/page/category.php create mode 100644 source/application/store/view/wxapp/page/edit.php create mode 100644 source/application/store/view/wxapp/page/index.php create mode 100644 source/application/store/view/wxapp/page/links.php create mode 100644 source/application/store/view/wxapp/setting.php create mode 100644 source/application/tags.php create mode 100644 source/application/task/behavior/DealerOrder.php create mode 100644 source/application/task/behavior/Order.php create mode 100644 source/application/task/behavior/UserCoupon.php create mode 100644 source/application/task/behavior/bargain/Task.php create mode 100644 source/application/task/behavior/sharing/Active.php create mode 100644 source/application/task/behavior/sharing/Order.php create mode 100644 source/application/task/behavior/sharp/Order.php create mode 100644 source/application/task/behavior/user/Grade.php create mode 100644 source/application/task/common.php create mode 100644 source/application/task/model/Express.php create mode 100644 source/application/task/model/Goods.php create mode 100644 source/application/task/model/GoodsSku.php create mode 100644 source/application/task/model/Order.php create mode 100644 source/application/task/model/OrderAddress.php create mode 100644 source/application/task/model/OrderGoods.php create mode 100644 source/application/task/model/OrderRefund.php create mode 100644 source/application/task/model/Setting.php create mode 100644 source/application/task/model/User.php create mode 100644 source/application/task/model/UserCoupon.php create mode 100644 source/application/task/model/Wxapp.php create mode 100644 source/application/task/model/WxappPrepayId.php create mode 100644 source/application/task/model/bargain/Task.php create mode 100644 source/application/task/model/dealer/Apply.php create mode 100644 source/application/task/model/dealer/Order.php create mode 100644 source/application/task/model/dealer/Referee.php create mode 100644 source/application/task/model/dealer/Setting.php create mode 100644 source/application/task/model/dealer/User.php create mode 100644 source/application/task/model/recharge/Order.php create mode 100644 source/application/task/model/sharing/Active.php create mode 100644 source/application/task/model/sharing/ActiveUsers.php create mode 100644 source/application/task/model/sharing/Goods.php create mode 100644 source/application/task/model/sharing/GoodsSku.php create mode 100644 source/application/task/model/sharing/Order.php create mode 100644 source/application/task/model/sharing/OrderAddress.php create mode 100644 source/application/task/model/sharing/OrderGoods.php create mode 100644 source/application/task/model/sharing/OrderRefund.php create mode 100644 source/application/task/model/sharing/Setting.php create mode 100644 source/application/task/model/sharp/Active.php create mode 100644 source/application/task/model/sharp/ActiveGoods.php create mode 100644 source/application/task/model/sharp/ActiveTime.php create mode 100644 source/application/task/model/sharp/Goods.php create mode 100644 source/application/task/model/sharp/Setting.php create mode 100644 source/application/task/model/user/BalanceLog.php create mode 100644 source/application/task/model/user/Grade.php create mode 100644 source/application/task/model/user/GradeLog.php create mode 100644 source/application/task/model/user/PointsLog.php create mode 100644 source/application/task/service/Order.php create mode 100644 source/composer.json create mode 100644 source/runtime/.gitignore create mode 100644 source/thinkphp/.gitignore create mode 100644 source/thinkphp/.htaccess create mode 100644 source/thinkphp/.travis.yml create mode 100644 source/thinkphp/CONTRIBUTING.md create mode 100644 source/thinkphp/LICENSE.txt create mode 100644 source/thinkphp/README.md create mode 100644 source/thinkphp/base.php create mode 100644 source/thinkphp/codecov.yml create mode 100644 source/thinkphp/composer.json create mode 100644 source/thinkphp/console.php create mode 100644 source/thinkphp/convention.php create mode 100644 source/thinkphp/helper.php create mode 100644 source/thinkphp/lang/zh-cn.php create mode 100644 source/thinkphp/library/think/App.php create mode 100644 source/thinkphp/library/think/Build.php create mode 100644 source/thinkphp/library/think/Cache.php create mode 100644 source/thinkphp/library/think/Collection.php create mode 100644 source/thinkphp/library/think/Config.php create mode 100644 source/thinkphp/library/think/Console.php create mode 100644 source/thinkphp/library/think/Controller.php create mode 100644 source/thinkphp/library/think/Cookie.php create mode 100644 source/thinkphp/library/think/Db.php create mode 100644 source/thinkphp/library/think/Debug.php create mode 100644 source/thinkphp/library/think/Env.php create mode 100644 source/thinkphp/library/think/Error.php create mode 100644 source/thinkphp/library/think/Exception.php create mode 100644 source/thinkphp/library/think/File.php create mode 100644 source/thinkphp/library/think/Hook.php create mode 100644 source/thinkphp/library/think/Lang.php create mode 100644 source/thinkphp/library/think/Loader.php create mode 100644 source/thinkphp/library/think/Log.php create mode 100644 source/thinkphp/library/think/Model.php create mode 100644 source/thinkphp/library/think/Paginator.php create mode 100644 source/thinkphp/library/think/Process.php create mode 100644 source/thinkphp/library/think/Request.php create mode 100644 source/thinkphp/library/think/Response.php create mode 100644 source/thinkphp/library/think/Route.php create mode 100644 source/thinkphp/library/think/Session.php create mode 100644 source/thinkphp/library/think/Template.php create mode 100644 source/thinkphp/library/think/Url.php create mode 100644 source/thinkphp/library/think/Validate.php create mode 100644 source/thinkphp/library/think/View.php create mode 100644 source/thinkphp/library/think/cache/Driver.php create mode 100644 source/thinkphp/library/think/cache/driver/File.php create mode 100644 source/thinkphp/library/think/cache/driver/Lite.php create mode 100644 source/thinkphp/library/think/cache/driver/Memcache.php create mode 100644 source/thinkphp/library/think/cache/driver/Memcached.php create mode 100644 source/thinkphp/library/think/cache/driver/Redis.php create mode 100644 source/thinkphp/library/think/cache/driver/Sqlite.php create mode 100644 source/thinkphp/library/think/cache/driver/Wincache.php create mode 100644 source/thinkphp/library/think/cache/driver/Xcache.php create mode 100644 source/thinkphp/library/think/config/driver/Ini.php create mode 100644 source/thinkphp/library/think/config/driver/Json.php create mode 100644 source/thinkphp/library/think/config/driver/Xml.php create mode 100644 source/thinkphp/library/think/console/Command.php create mode 100644 source/thinkphp/library/think/console/Input.php create mode 100644 source/thinkphp/library/think/console/LICENSE create mode 100644 source/thinkphp/library/think/console/Output.php create mode 100644 source/thinkphp/library/think/console/bin/README.md create mode 100644 source/thinkphp/library/think/console/bin/hiddeninput.exe create mode 100644 source/thinkphp/library/think/console/command/Build.php create mode 100644 source/thinkphp/library/think/console/command/Clear.php create mode 100644 source/thinkphp/library/think/console/command/Help.php create mode 100644 source/thinkphp/library/think/console/command/Lists.php create mode 100644 source/thinkphp/library/think/console/command/Make.php create mode 100644 source/thinkphp/library/think/console/command/make/Controller.php create mode 100644 source/thinkphp/library/think/console/command/make/Model.php create mode 100644 source/thinkphp/library/think/console/command/make/stubs/controller.plain.stub create mode 100644 source/thinkphp/library/think/console/command/make/stubs/controller.stub create mode 100644 source/thinkphp/library/think/console/command/make/stubs/model.stub create mode 100644 source/thinkphp/library/think/console/command/optimize/Autoload.php create mode 100644 source/thinkphp/library/think/console/command/optimize/Config.php create mode 100644 source/thinkphp/library/think/console/command/optimize/Route.php create mode 100644 source/thinkphp/library/think/console/command/optimize/Schema.php create mode 100644 source/thinkphp/library/think/console/input/Argument.php create mode 100644 source/thinkphp/library/think/console/input/Definition.php create mode 100644 source/thinkphp/library/think/console/input/Option.php create mode 100644 source/thinkphp/library/think/console/output/Ask.php create mode 100644 source/thinkphp/library/think/console/output/Descriptor.php create mode 100644 source/thinkphp/library/think/console/output/Formatter.php create mode 100644 source/thinkphp/library/think/console/output/Question.php create mode 100644 source/thinkphp/library/think/console/output/descriptor/Console.php create mode 100644 source/thinkphp/library/think/console/output/driver/Buffer.php create mode 100644 source/thinkphp/library/think/console/output/driver/Console.php create mode 100644 source/thinkphp/library/think/console/output/driver/Nothing.php create mode 100644 source/thinkphp/library/think/console/output/formatter/Stack.php create mode 100644 source/thinkphp/library/think/console/output/formatter/Style.php create mode 100644 source/thinkphp/library/think/console/output/question/Choice.php create mode 100644 source/thinkphp/library/think/console/output/question/Confirmation.php create mode 100644 source/thinkphp/library/think/controller/Rest.php create mode 100644 source/thinkphp/library/think/controller/Yar.php create mode 100644 source/thinkphp/library/think/db/Builder.php create mode 100644 source/thinkphp/library/think/db/Connection.php create mode 100644 source/thinkphp/library/think/db/Expression.php create mode 100644 source/thinkphp/library/think/db/Query.php create mode 100644 source/thinkphp/library/think/db/builder/Mysql.php create mode 100644 source/thinkphp/library/think/db/builder/Pgsql.php create mode 100644 source/thinkphp/library/think/db/builder/Sqlite.php create mode 100644 source/thinkphp/library/think/db/builder/Sqlsrv.php create mode 100644 source/thinkphp/library/think/db/connector/Mysql.php create mode 100644 source/thinkphp/library/think/db/connector/Pgsql.php create mode 100644 source/thinkphp/library/think/db/connector/Sqlite.php create mode 100644 source/thinkphp/library/think/db/connector/Sqlsrv.php create mode 100644 source/thinkphp/library/think/db/connector/pgsql.sql create mode 100644 source/thinkphp/library/think/db/exception/BindParamException.php create mode 100644 source/thinkphp/library/think/db/exception/DataNotFoundException.php create mode 100644 source/thinkphp/library/think/db/exception/ModelNotFoundException.php create mode 100644 source/thinkphp/library/think/debug/Console.php create mode 100644 source/thinkphp/library/think/debug/Html.php create mode 100644 source/thinkphp/library/think/exception/ClassNotFoundException.php create mode 100644 source/thinkphp/library/think/exception/DbException.php create mode 100644 source/thinkphp/library/think/exception/ErrorException.php create mode 100644 source/thinkphp/library/think/exception/Handle.php create mode 100644 source/thinkphp/library/think/exception/HttpException.php create mode 100644 source/thinkphp/library/think/exception/HttpResponseException.php create mode 100644 source/thinkphp/library/think/exception/PDOException.php create mode 100644 source/thinkphp/library/think/exception/RouteNotFoundException.php create mode 100644 source/thinkphp/library/think/exception/TemplateNotFoundException.php create mode 100644 source/thinkphp/library/think/exception/ThrowableError.php create mode 100644 source/thinkphp/library/think/exception/ValidateException.php create mode 100644 source/thinkphp/library/think/log/driver/File.php create mode 100644 source/thinkphp/library/think/log/driver/Socket.php create mode 100644 source/thinkphp/library/think/log/driver/Test.php create mode 100644 source/thinkphp/library/think/model/Collection.php create mode 100644 source/thinkphp/library/think/model/Merge.php create mode 100644 source/thinkphp/library/think/model/Pivot.php create mode 100644 source/thinkphp/library/think/model/Relation.php create mode 100644 source/thinkphp/library/think/model/relation/BelongsTo.php create mode 100644 source/thinkphp/library/think/model/relation/BelongsToMany.php create mode 100644 source/thinkphp/library/think/model/relation/HasMany.php create mode 100644 source/thinkphp/library/think/model/relation/HasManyThrough.php create mode 100644 source/thinkphp/library/think/model/relation/HasOne.php create mode 100644 source/thinkphp/library/think/model/relation/MorphMany.php create mode 100644 source/thinkphp/library/think/model/relation/MorphOne.php create mode 100644 source/thinkphp/library/think/model/relation/MorphTo.php create mode 100644 source/thinkphp/library/think/model/relation/OneToOne.php create mode 100644 source/thinkphp/library/think/paginator/driver/Bootstrap.php create mode 100644 source/thinkphp/library/think/process/Builder.php create mode 100644 source/thinkphp/library/think/process/Utils.php create mode 100644 source/thinkphp/library/think/process/exception/Failed.php create mode 100644 source/thinkphp/library/think/process/exception/Timeout.php create mode 100644 source/thinkphp/library/think/process/pipes/Pipes.php create mode 100644 source/thinkphp/library/think/process/pipes/Unix.php create mode 100644 source/thinkphp/library/think/process/pipes/Windows.php create mode 100644 source/thinkphp/library/think/response/Json.php create mode 100644 source/thinkphp/library/think/response/Jsonp.php create mode 100644 source/thinkphp/library/think/response/Redirect.php create mode 100644 source/thinkphp/library/think/response/View.php create mode 100644 source/thinkphp/library/think/response/Xml.php create mode 100644 source/thinkphp/library/think/session/driver/Memcache.php create mode 100644 source/thinkphp/library/think/session/driver/Memcached.php create mode 100644 source/thinkphp/library/think/session/driver/Redis.php create mode 100644 source/thinkphp/library/think/template/TagLib.php create mode 100644 source/thinkphp/library/think/template/driver/File.php create mode 100644 source/thinkphp/library/think/template/taglib/Cx.php create mode 100644 source/thinkphp/library/think/view/driver/Php.php create mode 100644 source/thinkphp/library/think/view/driver/Think.php create mode 100644 source/thinkphp/library/traits/controller/Jump.php create mode 100644 source/thinkphp/library/traits/model/SoftDelete.php create mode 100644 source/thinkphp/library/traits/think/Instance.php create mode 100644 source/thinkphp/logo.png create mode 100644 source/thinkphp/phpunit.xml create mode 100644 source/thinkphp/start.php create mode 100644 source/thinkphp/tpl/default_index.tpl create mode 100644 source/thinkphp/tpl/dispatch_jump.tpl create mode 100644 source/thinkphp/tpl/page_trace.tpl create mode 100644 source/thinkphp/tpl/think_exception.tpl create mode 100644 source/vendor/aliyuncs/oss-sdk-php/.coveralls.yml create mode 100644 source/vendor/aliyuncs/oss-sdk-php/.gitignore create mode 100644 source/vendor/aliyuncs/oss-sdk-php/.travis.yml create mode 100644 source/vendor/aliyuncs/oss-sdk-php/CHANGELOG.md create mode 100644 source/vendor/aliyuncs/oss-sdk-php/LICENSE.md create mode 100644 source/vendor/aliyuncs/oss-sdk-php/README-CN.md create mode 100644 source/vendor/aliyuncs/oss-sdk-php/README.md create mode 100644 source/vendor/aliyuncs/oss-sdk-php/autoload.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/build-phar.sh create mode 100644 source/vendor/aliyuncs/oss-sdk-php/composer.json create mode 100644 source/vendor/aliyuncs/oss-sdk-php/example.jpg create mode 100644 source/vendor/aliyuncs/oss-sdk-php/index.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/phpunit.xml create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Bucket.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/BucketCors.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/BucketLifecycle.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/BucketLogging.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/BucketReferer.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/BucketWebsite.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Callback.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Common.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Config.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Image.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/LiveChannel.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/MultipartUpload.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Object.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/RunAll.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/samples/Signature.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/MimeTypes.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssException.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssUtil.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/LICENSE create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore_Exception.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/ResponseCore.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketListInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CnameConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsRule.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelHistory.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelStatus.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleAction.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleRule.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListMultipartUploadInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListPartsInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelHistory.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelListInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LoggingConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectListInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PartInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PrefixInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/RefererConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/StorageCapacityConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/UploadInfo.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/WebsiteConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/XmlConfig.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/OssClient.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AclResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AppendResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/BodyResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CallbackResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CopyObjectResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/DeleteObjectsResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ExistResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCnameResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCorsResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLifecycleResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelHistoryResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelInfoResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelStatusResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLocationResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLoggingResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetRefererResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetStorageCapacityResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetWebsiteResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/HeaderResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/InitiateMultipartUploadResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListBucketsResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListLiveChannelResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListMultipartUploadResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListObjectsResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListPartsResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutLiveChannelResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutSetDeleteResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/Result.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/SymlinkResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/UploadPartResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/AclResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BodyResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketCnameTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketInfoTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketLiveChannelTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CallbackTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CnameConfigTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/Common.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ContentTypeTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CopyObjectResult.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CorsConfigTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ExistResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetCorsResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLifecycleResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLoggingResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetRefererResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetWebsiteResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HeaderResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HttpTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/InitiateMultipartUploadResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LifecycleConfigTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListBucketsResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListMultipartUploadResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListObjectsResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListPartsResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LiveChannelXmlTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LoggingConfigTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/MimeTypesTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ObjectAclTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketCorsTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLifecycleTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLoggingTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketRefererTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketStorageCapacityTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketWebsiteTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientImageTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientMultipartUploadTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientObjectTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientRestoreObjectTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientSignatureTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssExceptionTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssUtilTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/PutSetDeleteResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/RefererConfigTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/StorageCapacityTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/SymlinkTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/TestOssClientBase.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/UploadPartResultTest.php create mode 100644 source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/WebsiteConfigTest.php create mode 100644 source/vendor/autoload.php create mode 100644 source/vendor/composer/ClassLoader.php create mode 100644 source/vendor/composer/LICENSE create mode 100644 source/vendor/composer/autoload_classmap.php create mode 100644 source/vendor/composer/autoload_files.php create mode 100644 source/vendor/composer/autoload_namespaces.php create mode 100644 source/vendor/composer/autoload_psr4.php create mode 100644 source/vendor/composer/autoload_real.php create mode 100644 source/vendor/composer/autoload_static.php create mode 100644 source/vendor/composer/installed.json create mode 100644 source/vendor/guzzle/guzzle/.gitignore create mode 100644 source/vendor/guzzle/guzzle/.travis.yml create mode 100644 source/vendor/guzzle/guzzle/CHANGELOG.md create mode 100644 source/vendor/guzzle/guzzle/LICENSE create mode 100644 source/vendor/guzzle/guzzle/README.md create mode 100644 source/vendor/guzzle/guzzle/UPGRADING.md create mode 100644 source/vendor/guzzle/guzzle/build.xml create mode 100644 source/vendor/guzzle/guzzle/composer.json create mode 100644 source/vendor/guzzle/guzzle/docs/Makefile create mode 100644 source/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json create mode 100644 source/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png create mode 100644 source/vendor/guzzle/guzzle/docs/_static/homepage.css create mode 100644 source/vendor/guzzle/guzzle/docs/_static/logo.png create mode 100644 source/vendor/guzzle/guzzle/docs/_static/prettify.css create mode 100644 source/vendor/guzzle/guzzle/docs/_static/prettify.js create mode 100644 source/vendor/guzzle/guzzle/docs/_templates/index.html create mode 100644 source/vendor/guzzle/guzzle/docs/_templates/leftbar.html create mode 100644 source/vendor/guzzle/guzzle/docs/_templates/nav_links.html create mode 100644 source/vendor/guzzle/guzzle/docs/batching/batching.rst create mode 100644 source/vendor/guzzle/guzzle/docs/conf.py create mode 100644 source/vendor/guzzle/guzzle/docs/docs.rst create mode 100644 source/vendor/guzzle/guzzle/docs/getting-started/faq.rst create mode 100644 source/vendor/guzzle/guzzle/docs/getting-started/installation.rst create mode 100644 source/vendor/guzzle/guzzle/docs/getting-started/overview.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/client.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/request.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/response.rst create mode 100644 source/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst create mode 100644 source/vendor/guzzle/guzzle/docs/index.rst create mode 100644 source/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst create mode 100644 source/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc create mode 100644 source/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst create mode 100644 source/vendor/guzzle/guzzle/docs/requirements.txt create mode 100644 source/vendor/guzzle/guzzle/docs/testing/unit-testing.rst create mode 100644 source/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst create mode 100644 source/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst create mode 100644 source/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst create mode 100644 source/vendor/guzzle/guzzle/phar-stub.php create mode 100644 source/vendor/guzzle/guzzle/phing/build.properties.dist create mode 100644 source/vendor/guzzle/guzzle/phing/imports/dependencies.xml create mode 100644 source/vendor/guzzle/guzzle/phing/imports/deploy.xml create mode 100644 source/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php create mode 100644 source/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php create mode 100644 source/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php create mode 100644 source/vendor/guzzle/guzzle/phpunit.xml.dist create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/Version.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Common/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Request.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Parser/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Service/composer.json create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php create mode 100644 source/vendor/guzzle/guzzle/src/Guzzle/Stream/composer.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockMulti.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserProvider.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/MockClient.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json create mode 100644 source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json create mode 100644 source/vendor/guzzle/guzzle/tests/bootstrap.php create mode 100644 source/vendor/kosinix/grafika/.travis.yml create mode 100644 source/vendor/kosinix/grafika/README.md create mode 100644 source/vendor/kosinix/grafika/composer.json create mode 100644 source/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf create mode 100644 source/vendor/kosinix/grafika/fonts/st-heiti-light.ttc create mode 100644 source/vendor/kosinix/grafika/license.txt create mode 100644 source/vendor/kosinix/grafika/phpunit.xml create mode 100644 source/vendor/kosinix/grafika/sami.phar create mode 100644 source/vendor/kosinix/grafika/samiconf.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Color.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/CubicBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/Ellipse.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/Line.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/Polygon.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/QuadraticBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObject/Rectangle.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/DrawingObjectInterface.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/EditorInterface.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/FilterInterface.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/CubicBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Ellipse.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Line.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Polygon.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/QuadraticBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/DrawingObject/Rectangle.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Editor.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Blur.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Brightness.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Colorize.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Contrast.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Dither.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Gamma.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Grayscale.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Invert.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Pixelate.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sharpen.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Filter/Sobel.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifByteStream.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Helper/GifHelper.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/Image.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/AverageHash.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Gd/ImageHash/DifferenceHash.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Grafika.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/ImageInterface.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/ImageType.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/CubicBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Ellipse.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Line.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Polygon.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/QuadraticBezier.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/DrawingObject/Rectangle.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Editor.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Blur.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Brightness.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Colorize.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Contrast.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Dither.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Gamma.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Grayscale.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Invert.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Pixelate.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sharpen.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Filter/Sobel.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/Image.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/ImageHash/AverageHash.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Imagick/ImageHash/DifferenceHash.php create mode 100644 source/vendor/kosinix/grafika/src/Grafika/Position.php create mode 100644 source/vendor/kosinix/grafika/src/autoloader.php create mode 100644 source/vendor/kosinix/grafika/tests/GdEditorTest.php create mode 100644 source/vendor/kosinix/grafika/tests/ImagickEditorTest.php create mode 100644 source/vendor/kosinix/grafika/tests/bootstrap.php create mode 100644 source/vendor/kosinix/grafika/tests/install.txt create mode 100644 source/vendor/lvht/geohash/.gitignore create mode 100644 source/vendor/lvht/geohash/LICENSE create mode 100644 source/vendor/lvht/geohash/README.md create mode 100644 source/vendor/lvht/geohash/composer.json create mode 100644 source/vendor/lvht/geohash/src/GeoHash.php create mode 100644 source/vendor/myclabs/php-enum/.gitattributes create mode 100644 source/vendor/myclabs/php-enum/.gitignore create mode 100644 source/vendor/myclabs/php-enum/LICENSE create mode 100644 source/vendor/myclabs/php-enum/README.md create mode 100644 source/vendor/myclabs/php-enum/composer.json create mode 100644 source/vendor/myclabs/php-enum/src/Enum.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/.gitignore create mode 100644 source/vendor/qcloud/cos-sdk-v5/.travis.yml create mode 100644 source/vendor/qcloud/cos-sdk-v5/LICENSE create mode 100644 source/vendor/qcloud/cos-sdk-v5/README.md create mode 100644 source/vendor/qcloud/cos-sdk-v5/composer.json create mode 100644 source/vendor/qcloud/cos-sdk-v5/phpunit.xml create mode 100644 source/vendor/qcloud/cos-sdk-v5/sample.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/BucketStyleListener.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Client.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Command.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Copy.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketAlreadyExistsException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/BucketNotEmptyException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CosException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/CurlException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/InvalidArgumentException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchBucketException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchKeyException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/NoSuchUploadException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Exception/ServiceResponseException.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionListener.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/ExceptionParser.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Md5Listener.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/MultipartUpload.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Service.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Signature.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/SignatureListener.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/Test.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/Tests/TestHelper.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/TokenListener.php create mode 100644 source/vendor/qcloud/cos-sdk-v5/src/Qcloud/Cos/UploadBodyListener.php create mode 100644 source/vendor/qiniu/php-sdk/.gitignore create mode 100644 source/vendor/qiniu/php-sdk/.scrutinizer.yml create mode 100644 source/vendor/qiniu/php-sdk/.travis.yml create mode 100644 source/vendor/qiniu/php-sdk/CHANGELOG.md create mode 100644 source/vendor/qiniu/php-sdk/CONTRIBUTING.md create mode 100644 source/vendor/qiniu/php-sdk/LICENSE create mode 100644 source/vendor/qiniu/php-sdk/README.md create mode 100644 source/vendor/qiniu/php-sdk/autoload.php create mode 100644 source/vendor/qiniu/php-sdk/composer.json create mode 100644 source/vendor/qiniu/php-sdk/docs/rtc/README.md create mode 100644 source/vendor/qiniu/php-sdk/docs/rtc/example.php create mode 100644 source/vendor/qiniu/php-sdk/examples/README.md create mode 100644 source/vendor/qiniu/php-sdk/examples/cdn_get_bandwidth.php create mode 100644 source/vendor/qiniu/php-sdk/examples/cdn_get_flux.php create mode 100644 source/vendor/qiniu/php-sdk/examples/cdn_get_log_list.php create mode 100644 source/vendor/qiniu/php-sdk/examples/cdn_refresh_urls_dirs.php create mode 100644 source/vendor/qiniu/php-sdk/examples/cdn_timestamp_antileech.php create mode 100644 source/vendor/qiniu/php-sdk/examples/image_url_builder.php create mode 100644 source/vendor/qiniu/php-sdk/examples/persistent_fop_init.php create mode 100644 source/vendor/qiniu/php-sdk/examples/persistent_fop_status.php create mode 100644 source/vendor/qiniu/php-sdk/examples/pfop_mkzip.php create mode 100644 source/vendor/qiniu/php-sdk/examples/pfop_vframe.php create mode 100644 source/vendor/qiniu/php-sdk/examples/pfop_video_avthumb.php create mode 100644 source/vendor/qiniu/php-sdk/examples/pfop_watermark.php create mode 100644 source/vendor/qiniu/php-sdk/examples/php-logo.png create mode 100644 source/vendor/qiniu/php-sdk/examples/prefop.php create mode 100644 source/vendor/qiniu/php-sdk/examples/pulpvideo.php create mode 100644 source/vendor/qiniu/php-sdk/examples/qetag.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_change_mime.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_change_type.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_copy.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_delete.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_delete_after_days.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_move.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_batch_stat.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_bucket_domains.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_buckets.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_change_mime.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_change_status.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_change_type.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_copy.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_delete.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_delete_after_days.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_download_urls.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_fetch.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_move.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_prefetch.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rs_stat.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rsf_list_bucket.php create mode 100644 source/vendor/qiniu/php-sdk/examples/rsf_list_files.php create mode 100644 source/vendor/qiniu/php-sdk/examples/saveas.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_and_callback.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_and_pfop.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_mgr_init.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_multi_demos.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_simple_file.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_tokens.php create mode 100644 source/vendor/qiniu/php-sdk/examples/upload_verify_callback.php create mode 100644 source/vendor/qiniu/php-sdk/phpunit.xml.dist create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Auth.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Cdn/CdnManager.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Config.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Etag.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Http/Client.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Http/Error.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Http/Request.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Http/Response.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Processing/ImageUrlBuilder.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Processing/Operation.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Processing/PersistentFop.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Rtc/AppClient.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Storage/ArgusManager.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Storage/BucketManager.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Storage/FormUploader.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Storage/ResumeUploader.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Storage/UploadManager.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/Zone.php create mode 100644 source/vendor/qiniu/php-sdk/src/Qiniu/functions.php create mode 100644 source/vendor/qiniu/php-sdk/test-env.sh create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/AuthTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Base64Test.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/BucketTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/CdnManagerTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/Crc32Test.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/DownloadTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/EtagTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FopTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/FormUpTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/HttpTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ImageUrlBuilderTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/PfopTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ResumeUpTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/Qiniu/Tests/ZoneTest.php create mode 100644 source/vendor/qiniu/php-sdk/tests/bootstrap.php create mode 100644 source/vendor/symfony/event-dispatcher/.gitignore create mode 100644 source/vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100644 source/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php create mode 100644 source/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 source/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 source/vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100644 source/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 source/vendor/symfony/event-dispatcher/Event.php create mode 100644 source/vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100644 source/vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100644 source/vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100644 source/vendor/symfony/event-dispatcher/GenericEvent.php create mode 100644 source/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 source/vendor/symfony/event-dispatcher/LICENSE create mode 100644 source/vendor/symfony/event-dispatcher/README.md create mode 100644 source/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100644 source/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 source/vendor/symfony/event-dispatcher/composer.json create mode 100644 source/vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100644 source/vendor/topthink/think-installer/.gitignore create mode 100644 source/vendor/topthink/think-installer/composer.json create mode 100644 source/vendor/topthink/think-installer/src/Plugin.php create mode 100644 source/vendor/topthink/think-installer/src/ThinkExtend.php create mode 100644 source/vendor/topthink/think-installer/src/ThinkFramework.php create mode 100644 source/vendor/topthink/think-installer/src/ThinkTesting.php create mode 100644 version.json create mode 100644 web/assets/admin/css/app.css create mode 100644 web/assets/admin/css/login/style.css create mode 100644 web/assets/admin/img/login_bg.jpg create mode 100644 web/assets/admin/img/login_bg1.jpg create mode 100644 web/assets/admin/js/app.js create mode 100644 web/assets/admin/scss/app.scss create mode 100644 web/assets/api/dealer-bg.png create mode 100644 web/assets/common/css/amazeui.datatables.min.css create mode 100644 web/assets/common/css/amazeui.min.css create mode 100644 web/assets/common/css/fullcalendar.min.css create mode 100644 web/assets/common/css/fullcalendar.print.css create mode 100644 web/assets/common/fonts/FontAwesome.otf create mode 100644 web/assets/common/fonts/fontawesome-webfont.eot create mode 100644 web/assets/common/fonts/fontawesome-webfont.ttf create mode 100644 web/assets/common/fonts/fontawesome-webfont.woff create mode 100644 web/assets/common/fonts/fontawesome-webfont.woff2 create mode 100644 web/assets/common/i/favicon.ico create mode 100644 web/assets/common/js/Sortable.min.js create mode 100644 web/assets/common/js/amazeui.min.js create mode 100644 web/assets/common/js/art-template.js create mode 100644 web/assets/common/js/ddsort.js create mode 100644 web/assets/common/js/echarts-walden.js create mode 100644 web/assets/common/js/echarts.min.js create mode 100644 web/assets/common/js/jquery.dad.js create mode 100644 web/assets/common/js/jquery.form.min.js create mode 100644 web/assets/common/js/jquery.min.js create mode 100644 web/assets/common/js/jstree.min.js create mode 100644 web/assets/common/js/vue.min.js create mode 100644 web/assets/common/js/vuedraggable.min.js create mode 100644 web/assets/common/js/webuploader.html5only.js create mode 100644 web/assets/common/plugins/laydate/laydate.js create mode 100644 web/assets/common/plugins/laydate/theme/default/font/iconfont.eot create mode 100644 web/assets/common/plugins/laydate/theme/default/font/iconfont.svg create mode 100644 web/assets/common/plugins/laydate/theme/default/font/iconfont.ttf create mode 100644 web/assets/common/plugins/laydate/theme/default/font/iconfont.woff create mode 100644 web/assets/common/plugins/laydate/theme/default/laydate.css create mode 100644 web/assets/common/plugins/layer/layer.js create mode 100644 web/assets/common/plugins/layer/mobile/layer.js create mode 100644 web/assets/common/plugins/layer/mobile/need/layer.css create mode 100644 web/assets/common/plugins/layer/theme/default/icon-ext.png create mode 100644 web/assets/common/plugins/layer/theme/default/icon.png create mode 100644 web/assets/common/plugins/layer/theme/default/layer.css create mode 100644 web/assets/common/plugins/layer/theme/default/loading-0.gif create mode 100644 web/assets/common/plugins/layer/theme/default/loading-1.gif create mode 100644 web/assets/common/plugins/layer/theme/default/loading-2.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/emotion.css create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/emotion.js create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/0.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/bface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/cface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/fface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/jxface2.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/neweditor-tab-bg.png create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/tface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/wface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/emotion/images/yface.gif create mode 100644 web/assets/common/plugins/umeditor/dialogs/formula/formula.css create mode 100644 web/assets/common/plugins/umeditor/dialogs/formula/formula.html create mode 100644 web/assets/common/plugins/umeditor/dialogs/formula/formula.js create mode 100644 web/assets/common/plugins/umeditor/dialogs/formula/images/formula.png create mode 100644 web/assets/common/plugins/umeditor/dialogs/image/image.css create mode 100644 web/assets/common/plugins/umeditor/dialogs/image/image.js create mode 100644 web/assets/common/plugins/umeditor/dialogs/image/images/close.png create mode 100644 web/assets/common/plugins/umeditor/dialogs/image/images/upload1.png create mode 100644 web/assets/common/plugins/umeditor/dialogs/image/images/upload2.png create mode 100644 web/assets/common/plugins/umeditor/dialogs/link/link.js create mode 100644 web/assets/common/plugins/umeditor/dialogs/map/map.html create mode 100644 web/assets/common/plugins/umeditor/dialogs/map/map.js create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/images/center_focus.jpg create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/images/left_focus.jpg create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/images/none_focus.jpg create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/images/right_focus.jpg create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/video.css create mode 100644 web/assets/common/plugins/umeditor/dialogs/video/video.js create mode 100644 web/assets/common/plugins/umeditor/lang/en/en.js create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/addimage.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/alldeletebtnhoverskin.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/alldeletebtnupskin.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/background.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/button.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/copy.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/deletedisable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/deleteenable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/imglabel.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/listbackground.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/localimage.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/music.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/rotateleftdisable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/rotateleftenable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/rotaterightdisable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/rotaterightenable.png create mode 100644 web/assets/common/plugins/umeditor/lang/en/images/upload.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/images/copy.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/images/imglabel.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/images/localimage.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/images/music.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/images/upload.png create mode 100644 web/assets/common/plugins/umeditor/lang/zh-cn/zh-cn.js create mode 100644 web/assets/common/plugins/umeditor/themes/default/css/umeditor.css create mode 100644 web/assets/common/plugins/umeditor/themes/default/css/umeditor.min.css create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/caret.png create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/close.png create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/icons.gif create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/icons.png create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/ok.gif create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/pop-bg.png create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/spacer.gif create mode 100644 web/assets/common/plugins/umeditor/themes/default/images/videologo.gif create mode 100644 web/assets/common/plugins/umeditor/umeditor.config.js create mode 100644 web/assets/common/plugins/umeditor/umeditor.js create mode 100644 web/assets/common/plugins/umeditor/umeditor.min.js create mode 100644 web/assets/store/css/app.css create mode 100644 web/assets/store/css/diy.css create mode 100644 web/assets/store/css/goods.css create mode 100644 web/assets/store/css/login/style.css create mode 100644 web/assets/store/img/category_tpl/10.png create mode 100644 web/assets/store/img/category_tpl/11.png create mode 100644 web/assets/store/img/category_tpl/20.png create mode 100644 web/assets/store/img/chose.png create mode 100644 web/assets/store/img/dealer/avatar.png create mode 100644 web/assets/store/img/dealer/backdrop.png create mode 100644 web/assets/store/img/dealer/qrcode.png create mode 100644 web/assets/store/img/diy/article/01.png create mode 100644 web/assets/store/img/diy/banner/01.png create mode 100644 web/assets/store/img/diy/circular.png create mode 100644 web/assets/store/img/diy/goods/01.png create mode 100644 web/assets/store/img/diy/navbar/01.png create mode 100644 web/assets/store/img/diy/notice.png create mode 100644 web/assets/store/img/diy/phone-top-black.png create mode 100644 web/assets/store/img/diy/phone-top-white.png create mode 100644 web/assets/store/img/diy/service.png create mode 100644 web/assets/store/img/diy/special.png create mode 100644 web/assets/store/img/diy/video_poster.png create mode 100644 web/assets/store/img/diy/window/01.jpg create mode 100644 web/assets/store/img/diy/window/02.jpg create mode 100644 web/assets/store/img/diy/window/03.jpg create mode 100644 web/assets/store/img/diy/window/04.jpg create mode 100644 web/assets/store/img/login/login_bg.jpg create mode 100644 web/assets/store/img/login/logo.png create mode 100644 web/assets/store/img/statistics/ranking/01.png create mode 100644 web/assets/store/img/statistics/ranking/02.png create mode 100644 web/assets/store/img/statistics/ranking/03.png create mode 100644 web/assets/store/img/statistics/survey/01.png create mode 100644 web/assets/store/img/statistics/survey/02.png create mode 100644 web/assets/store/img/statistics/survey/03.png create mode 100644 web/assets/store/img/statistics/survey/04.png create mode 100644 web/assets/store/img/statistics/survey/05.png create mode 100644 web/assets/store/img/tpl_msg/01.png create mode 100644 web/assets/store/img/tpl_msg/02.png create mode 100644 web/assets/store/img/tpl_msg/03_01.png create mode 100644 web/assets/store/img/tpl_msg/04.png create mode 100644 web/assets/store/js/app.js create mode 100644 web/assets/store/js/delivery.js create mode 100644 web/assets/store/js/diy.js create mode 100644 web/assets/store/js/file.library.js create mode 100644 web/assets/store/js/goods.spec.js create mode 100644 web/assets/store/js/select.data.js create mode 100644 web/assets/store/js/select.region.js create mode 100644 web/assets/store/scss/app.scss create mode 100644 web/assets/store/scss/diy.scss create mode 100644 web/assets/store/scss/goods.scss create mode 100644 web/index.php create mode 100644 web/notice.php create mode 100644 web/temp/.gitignore create mode 100644 web/uploads/.gitignore create mode 100644 安装说明..md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f08e783 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea +composer.lock +*.log + +*.css.map +.sass-cache diff --git a/README.md b/README.md new file mode 100644 index 0000000..291b2c3 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# 萤火小程序商城-商业版(YoShop) + +#### 项目介绍 + +萤火小程序商城,是一款开源的电商系统,为中小企业提供最佳的新零售解决方案。采用稳定的MVC框架开发,执行效率、扩展性、稳定性值得信赖。帮助商家高效快捷的打造一款功能完善的购物小程序,速度快,无需下载安装,共享微信 8亿用户流量,开启新零售! + +#### 项目官网 + +官网地址:https://www.yiovo.com/ + +## 更新日志 + +### v1.1.38 + +修复:订单确认页收货地址提示 +修复:后台拼团商品列表状态筛选 +修复:秒杀订单并发库存问题 +修复:后台编辑门店后坐标不准确 +新增:后台分销中心编辑分销商用户 +优化:删除分销商清空推荐关系 +优化:删除用户时删除上级推荐关系 + +## 系统使用要求 + +## 小程序要求 + +``` +申请微信小程序 +通过微信认证 +申请微信支付商户 +``` + +## 服务器最低要求 + +``` +1核CPU +2G内存 +2M带宽 +``` + +## 系统环境要求 + +``` +PHP版本 >= 5.4 (推荐PHP7.1版本) +MySql版本 >= 5.5 (需支持innodb引擎) +Apache 或 Nginx +服务器支持https +``` \ No newline at end of file diff --git a/doc/database/install.sql b/doc/database/install.sql new file mode 100644 index 0000000..d081295 --- /dev/null +++ b/doc/database/install.sql @@ -0,0 +1,5598 @@ + +SET FOREIGN_KEY_CHECKS=0; + + +CREATE TABLE `yoshop_category` ( + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品分类id', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上级分类id', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分类图片id', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品分类表'; + + +CREATE TABLE `yoshop_comment` ( + `comment_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '评价id', + `score` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '评分 (10好评 20中评 30差评)', + `content` text NOT NULL COMMENT '评价内容', + `is_picture` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为图片评价', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '评价排序', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态(0隐藏 1显示)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`comment_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单评价记录表'; + + +CREATE TABLE `yoshop_comment_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `comment_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '评价id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单评价图片记录表'; + + +CREATE TABLE `yoshop_coupon` ( + `coupon_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '优惠券id', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '优惠券名称', + `color` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券颜色(10蓝 20红 30紫 40黄)', + `coupon_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券类型(10满减券 20折扣券)', + `reduce_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '满减券-减免金额', + `discount` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '折扣券-折扣率(0-100)', + `min_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '最低消费金额', + `expire_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '到期类型(10领取后生效 20固定时间)', + `expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数', + `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-开始时间', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-结束时间', + `apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品)', + `total_num` int(11) NOT NULL DEFAULT '0' COMMENT '发放总数量(-1为不限制)', + `receive_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已领取数量', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`coupon_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='优惠券记录表'; + + +CREATE TABLE `yoshop_coupon_goods` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `coupon_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '优惠券id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='优惠券指定商品记录表'; + + +CREATE TABLE `yoshop_delivery` ( + `delivery_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '模板id', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '模板名称', + `method` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '计费方式(10按件数 20按重量)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序d', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`delivery_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='配送模板主表'; + +INSERT INTO `yoshop_delivery` VALUES ('10001', '全国包邮', '10', '10001', '100', '1535083514', '1535083514'); + + +CREATE TABLE `yoshop_delivery_rule` ( + `rule_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '规则id', + `delivery_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '配送模板id', + `region` text NOT NULL COMMENT '可配送区域(城市id集)', + `first` double unsigned NOT NULL DEFAULT '0' COMMENT '首件(个)/首重(Kg)', + `first_fee` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '运费(元)', + `additional` double unsigned NOT NULL DEFAULT '0' COMMENT '续件/续重', + `additional_fee` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '续费(元)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL COMMENT '创建时间', + PRIMARY KEY (`rule_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='配送模板区域及运费表'; + +INSERT INTO `yoshop_delivery_rule` VALUES ('10001', '10001', '2,20,38,61,76,84,104,124,150,168,180,197,208,221,232,244,250,264,271,278,290,304,319,337,352,362,372,376,389,398,407,422,430,442,449,462,467,481,492,500,508,515,522,530,537,545,553,558,566,574,581,586,597,607,614,619,627,634,640,646,656,675,692,702,711,720,730,748,759,764,775,782,793,802,821,833,842,853,861,871,880,887,896,906,913,920,927,934,948,960,972,980,986,993,1003,1010,1015,1025,1035,1047,1057,1066,1074,1081,1088,1093,1098,1110,1118,1127,1136,1142,1150,1155,1160,1169,1183,1190,1196,1209,1222,1234,1245,1253,1264,1274,1279,1285,1299,1302,1306,1325,1339,1350,1362,1376,1387,1399,1408,1415,1421,1434,1447,1459,1466,1471,1476,1479,1492,1504,1513,1522,1533,1546,1556,1572,1583,1593,1599,1612,1623,1630,1637,1643,1650,1664,1674,1685,1696,1707,1710,1724,1731,1740,1754,1764,1768,1774,1782,1791,1802,1809,1813,1822,1828,1838,1848,1854,1867,1880,1890,1900,1905,1912,1924,1936,1949,1955,1965,1977,1988,1999,2003,2011,2017,2025,2035,2041,2050,2056,2065,2070,2077,2082,2091,2123,2146,2150,2156,2163,2177,2189,2207,2215,2220,2225,2230,2236,2245,2258,2264,2276,2283,2292,2297,2302,2306,2324,2363,2368,2388,2395,2401,2409,2416,2426,2434,2440,2446,2458,2468,2475,2486,2493,2501,2510,2516,2521,2535,2554,2573,2584,2589,2604,2611,2620,2631,2640,2657,2671,2686,2696,2706,2712,2724,2730,2741,2750,2761,2775,2784,2788,2801,2807,2812,2817,2826,2845,2857,2870,2882,2890,2899,2913,2918,2931,2946,2958,2972,2984,2997,3008,3016,3023,3032,3036,3039,3045,3053,3058,3065,3073,3081,3090,3098,3108,3117,3127,3135,3142,3147,3152,3158,3165,3172,3179,3186,3190,3196,3202,3207,3216,3221,3225,3229,3237,3242,3252,3262,3267,3280,3289,3301,3309,3317,3326,3339,3378,3386,3416,3454,3458,3461,3491,3504,3518,3532,3551,3578,3592,3613,3632,3666,3683,3697,3704,3711,3739,3745,3747,3999', '1', '0.00', '0', '0.00', '10001', '1560214092'); + + +CREATE TABLE `yoshop_express` ( + `express_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '物流公司id', + `express_name` varchar(255) NOT NULL DEFAULT '' COMMENT '物流公司名称', + `express_code` varchar(30) NOT NULL DEFAULT '' COMMENT '物流公司代码 (快递100)', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序 (数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`express_id`), + KEY `express_code` (`express_code`) +) ENGINE=InnoDB AUTO_INCREMENT=10010 DEFAULT CHARSET=utf8 COMMENT='物流公司记录表'; + +INSERT INTO `yoshop_express` VALUES ('10001', '顺丰速运', 'shunfeng', '100', '10001', '1535160797', '1535160797'); +INSERT INTO `yoshop_express` VALUES ('10002', '邮政国内', 'yzguonei', '100', '10001', '1535942653', '1535942653'); +INSERT INTO `yoshop_express` VALUES ('10003', '圆通速递', 'yuantong', '100', '10001', '1535942675', '1535942675'); +INSERT INTO `yoshop_express` VALUES ('10004', '申通快递', 'shentong', '100', '10001', '1535942694', '1535942694'); +INSERT INTO `yoshop_express` VALUES ('10005', '韵达快递', 'yunda', '100', '10001', '1535942711', '1535942711'); +INSERT INTO `yoshop_express` VALUES ('10006', '百世快递', 'huitongkuaidi', '100', '10001', '1535942743', '1535942743'); +INSERT INTO `yoshop_express` VALUES ('10007', '中通快递', 'zhongtong', '100', '10001', '1535942764', '1535942764'); +INSERT INTO `yoshop_express` VALUES ('10008', '天天快递', 'tiantian', '100', '10001', '1535942783', '1535942783'); +INSERT INTO `yoshop_express` VALUES ('10009', '宅急送', 'zhaijisong', '100', '10001', '1535942798', '1535942798'); + + +CREATE TABLE `yoshop_goods` ( + `goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点', + `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品分类id', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格(10单规格 20多规格)', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `content` longtext NOT NULL COMMENT '商品详情', + `sales_initial` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '初始销量', + `sales_actual` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际销量', + `goods_sort` int(11) unsigned NOT NULL DEFAULT '100' COMMENT '商品排序(数字越小越靠前)', + `delivery_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '配送模板id', + `is_points_gift` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否开启积分赠送(1开启 0关闭)', + `is_points_discount` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否允许使用积分抵扣(1允许 0不允许)', + `is_enable_grade` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否开启会员折扣(1开启 0关闭)', + `is_alone_grade` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '会员折扣设置(0默认等级折扣 1单独设置折扣)', + `alone_grade_equity` text COMMENT '单独设置折扣的配置', + `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)', + `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)', + `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `goods_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '商品状态(10上架 20下架)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_id`), + KEY `category_id` (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品记录表'; + + +CREATE TABLE `yoshop_goods_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `image_id` int(11) NOT NULL COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品图片记录表'; + + +CREATE TABLE `yoshop_goods_sku` ( + `goods_sku_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品规格id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '商品sku记录索引 (由规格id组成)', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格图片id', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `stock_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '当前库存数量', + `goods_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品销量', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_sku_id`), + UNIQUE KEY `sku_idx` (`goods_id`,`spec_sku_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品规格表'; + + +CREATE TABLE `yoshop_goods_spec_rel` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格组id', + `spec_value_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格值id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品与规格值关系记录表'; + + +CREATE TABLE `yoshop_order` ( + `order_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品总金额(不含优惠折扣)', + `order_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单金额(含优惠折扣)', + `coupon_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '优惠券id', + `coupon_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券抵扣金额', + `points_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '积分抵扣金额', + `points_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '积分抵扣数量', + `pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款金额(包含运费)', + `update_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '后台修改的订单金额(差价)', + `buyer_remark` varchar(255) NOT NULL DEFAULT '' COMMENT '买家留言', + `pay_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '支付方式(10余额支付 20微信支付)', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '付款状态(10未付款 20已付款)', + `pay_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间', + `delivery_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '配送方式(10快递配送 20上门自提)', + `extract_shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '自提门店id', + `extract_clerk_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '核销店员id', + `express_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '运费金额', + `express_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '物流公司id', + `express_company` varchar(50) NOT NULL DEFAULT '' COMMENT '物流公司', + `express_no` varchar(50) NOT NULL DEFAULT '' COMMENT '物流单号', + `delivery_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '发货状态(10未发货 20已发货)', + `delivery_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '发货时间', + `receipt_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '收货状态(10未收货 20已收货)', + `receipt_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '收货时间', + `order_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单状态(10进行中 20取消 21待取消 30已完成)', + `points_bonus` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '赠送的积分数量', + `is_settled` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单是否已结算(0未结算 1已结算)', + `transaction_id` varchar(30) NOT NULL DEFAULT '' COMMENT '微信支付交易号', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `order_source` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单来源(10普通订单 20砍价订单)', + `order_source_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '来源记录id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_id`), + UNIQUE KEY `order_no` (`order_no`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单记录表'; + + +CREATE TABLE `yoshop_order_address` ( + `order_address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '地址id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在区id', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_address_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单收货地址记录表'; + + +CREATE TABLE `yoshop_order_goods` ( + `order_goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品封面图id', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '规格类型(10单规格 20多规格)', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品sku标识', + `goods_sku_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格id', + `goods_attr` varchar(500) NOT NULL DEFAULT '' COMMENT '商品规格信息', + `content` longtext NOT NULL COMMENT '商品详情', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格(单价)', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `is_user_grade` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否存在会员等级折扣', + `grade_ratio` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '会员折扣比例(0-10)', + `grade_goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '会员折扣的商品单价', + `grade_total_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '会员折扣的总额差', + `coupon_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券折扣金额', + `points_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '积分金额', + `points_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '积分抵扣数量', + `points_bonus` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '赠送的积分数量', + `total_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '购买数量', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品总价(数量×单价)', + `total_pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款价(折扣和优惠后)', + `is_ind_dealer` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否开启单独分销(0关闭 1开启)', + `dealer_money_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '分销佣金类型(10百分比 20固定金额)', + `first_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_goods_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单商品记录表'; + + +CREATE TABLE `yoshop_region` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `pid` int(11) DEFAULT NULL COMMENT '父id', + `shortname` varchar(100) DEFAULT NULL COMMENT '简称', + `name` varchar(100) DEFAULT NULL COMMENT '名称', + `merger_name` varchar(255) DEFAULT NULL COMMENT '全称', + `level` tinyint(4) unsigned DEFAULT '0' COMMENT '层级 1 2 3 省市区县', + `pinyin` varchar(100) DEFAULT NULL COMMENT '拼音', + `code` varchar(100) DEFAULT NULL COMMENT '长途区号', + `zip_code` varchar(100) DEFAULT NULL COMMENT '邮编', + `first` varchar(50) DEFAULT NULL COMMENT '首字母', + `lng` varchar(100) DEFAULT NULL COMMENT '经度', + `lat` varchar(100) DEFAULT NULL COMMENT '纬度', + PRIMARY KEY (`id`), + KEY `name,level` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8; + +INSERT INTO `yoshop_region` VALUES ('1', '0', '北京', '北京市', '中国,北京', '1', 'beijing', '', '', 'B', '116.405285', '39.904989'); +INSERT INTO `yoshop_region` VALUES ('2', '1', '北京', '北京市', '中国,北京,北京市', '2', 'beijing', '010', '100000', 'B', '116.405285', '39.904989'); +INSERT INTO `yoshop_region` VALUES ('3', '2', '东城', '东城区', '中国,北京,北京市,东城区', '3', 'dongcheng', '010', '100010', 'D', '116.41005', '39.93157'); +INSERT INTO `yoshop_region` VALUES ('4', '2', '西城', '西城区', '中国,北京,北京市,西城区', '3', 'xicheng', '010', '100032', 'X', '116.36003', '39.9305'); +INSERT INTO `yoshop_region` VALUES ('5', '2', '朝阳', '朝阳区', '中国,北京,北京市,朝阳区', '3', 'chaoyang', '010', '100020', 'C', '116.48548', '39.9484'); +INSERT INTO `yoshop_region` VALUES ('6', '2', '丰台', '丰台区', '中国,北京,北京市,丰台区', '3', 'fengtai', '010', '100071', 'F', '116.28625', '39.8585'); +INSERT INTO `yoshop_region` VALUES ('7', '2', '石景山', '石景山区', '中国,北京,北京市,石景山区', '3', 'shijingshan', '010', '100043', 'S', '116.2229', '39.90564'); +INSERT INTO `yoshop_region` VALUES ('8', '2', '海淀', '海淀区', '中国,北京,北京市,海淀区', '3', 'haidian', '010', '100089', 'H', '116.29812', '39.95931'); +INSERT INTO `yoshop_region` VALUES ('9', '2', '门头沟', '门头沟区', '中国,北京,北京市,门头沟区', '3', 'mentougou', '010', '102300', 'M', '116.10137', '39.94043'); +INSERT INTO `yoshop_region` VALUES ('10', '2', '房山', '房山区', '中国,北京,北京市,房山区', '3', 'fangshan', '010', '102488', 'F', '116.14257', '39.74786'); +INSERT INTO `yoshop_region` VALUES ('11', '2', '通州', '通州区', '中国,北京,北京市,通州区', '3', 'tongzhou', '010', '101149', 'T', '116.65716', '39.90966'); +INSERT INTO `yoshop_region` VALUES ('12', '2', '顺义', '顺义区', '中国,北京,北京市,顺义区', '3', 'shunyi', '010', '101300', 'S', '116.65417', '40.1302'); +INSERT INTO `yoshop_region` VALUES ('13', '2', '昌平', '昌平区', '中国,北京,北京市,昌平区', '3', 'changping', '010', '102200', 'C', '116.2312', '40.22072'); +INSERT INTO `yoshop_region` VALUES ('14', '2', '大兴', '大兴区', '中国,北京,北京市,大兴区', '3', 'daxing', '010', '102600', 'D', '116.34149', '39.72668'); +INSERT INTO `yoshop_region` VALUES ('15', '2', '怀柔', '怀柔区', '中国,北京,北京市,怀柔区', '3', 'huairou', '010', '101400', 'H', '116.63168', '40.31602'); +INSERT INTO `yoshop_region` VALUES ('16', '2', '平谷', '平谷区', '中国,北京,北京市,平谷区', '3', 'pinggu', '010', '101200', 'P', '117.12133', '40.14056'); +INSERT INTO `yoshop_region` VALUES ('17', '2', '密云', '密云县', '中国,北京,北京市,密云县', '3', 'miyun', '010', '101500', 'M', '116.84295', '40.37618'); +INSERT INTO `yoshop_region` VALUES ('18', '2', '延庆', '延庆县', '中国,北京,北京市,延庆县', '3', 'yanqing', '010', '102100', 'Y', '115.97494', '40.45672'); +INSERT INTO `yoshop_region` VALUES ('19', '0', '天津', '天津市', '中国,天津', '1', 'tianjin', '', '', 'T', '117.190182', '39.125596'); +INSERT INTO `yoshop_region` VALUES ('20', '19', '天津', '天津市', '中国,天津,天津市', '2', 'tianjin', '022', '300000', 'T', '117.190182', '39.125596'); +INSERT INTO `yoshop_region` VALUES ('21', '20', '和平', '和平区', '中国,天津,天津市,和平区', '3', 'heping', '022', '300041', 'H', '117.21456', '39.11718'); +INSERT INTO `yoshop_region` VALUES ('22', '20', '河东', '河东区', '中国,天津,天津市,河东区', '3', 'hedong', '022', '300171', 'H', '117.22562', '39.12318'); +INSERT INTO `yoshop_region` VALUES ('23', '20', '河西', '河西区', '中国,天津,天津市,河西区', '3', 'hexi', '022', '300202', 'H', '117.22327', '39.10959'); +INSERT INTO `yoshop_region` VALUES ('24', '20', '南开', '南开区', '中国,天津,天津市,南开区', '3', 'nankai', '022', '300110', 'N', '117.15074', '39.13821'); +INSERT INTO `yoshop_region` VALUES ('25', '20', '河北', '河北区', '中国,天津,天津市,河北区', '3', 'hebei', '022', '300143', 'H', '117.19697', '39.14816'); +INSERT INTO `yoshop_region` VALUES ('26', '20', '红桥', '红桥区', '中国,天津,天津市,红桥区', '3', 'hongqiao', '022', '300131', 'H', '117.15145', '39.16715'); +INSERT INTO `yoshop_region` VALUES ('27', '20', '东丽', '东丽区', '中国,天津,天津市,东丽区', '3', 'dongli', '022', '300300', 'D', '117.31436', '39.0863'); +INSERT INTO `yoshop_region` VALUES ('28', '20', '西青', '西青区', '中国,天津,天津市,西青区', '3', 'xiqing', '022', '300380', 'X', '117.00927', '39.14123'); +INSERT INTO `yoshop_region` VALUES ('29', '20', '津南', '津南区', '中国,天津,天津市,津南区', '3', 'jinnan', '022', '300350', 'J', '117.38537', '38.99139'); +INSERT INTO `yoshop_region` VALUES ('30', '20', '北辰', '北辰区', '中国,天津,天津市,北辰区', '3', 'beichen', '022', '300400', 'B', '117.13217', '39.22131'); +INSERT INTO `yoshop_region` VALUES ('31', '20', '武清', '武清区', '中国,天津,天津市,武清区', '3', 'wuqing', '022', '301700', 'W', '117.04443', '39.38415'); +INSERT INTO `yoshop_region` VALUES ('32', '20', '宝坻', '宝坻区', '中国,天津,天津市,宝坻区', '3', 'baodi', '022', '301800', 'B', '117.3103', '39.71761'); +INSERT INTO `yoshop_region` VALUES ('33', '20', '滨海新区', '滨海新区', '中国,天津,天津市,滨海新区', '3', 'binhaixinqu', '022', '300451', 'B', '117.70162', '39.02668'); +INSERT INTO `yoshop_region` VALUES ('34', '20', '宁河', '宁河县', '中国,天津,天津市,宁河县', '3', 'ninghe', '022', '301500', 'N', '117.8255', '39.33048'); +INSERT INTO `yoshop_region` VALUES ('35', '20', '静海', '静海县', '中国,天津,天津市,静海县', '3', 'jinghai', '022', '301600', 'J', '116.97436', '38.94582'); +INSERT INTO `yoshop_region` VALUES ('36', '20', '蓟县', '蓟县', '中国,天津,天津市,蓟县', '3', 'jixian', '022', '301900', 'J', '117.40799', '40.04567'); +INSERT INTO `yoshop_region` VALUES ('37', '0', '河北', '河北省', '中国,河北省', '1', 'hebei', '', '', 'H', '114.502461', '38.045474'); +INSERT INTO `yoshop_region` VALUES ('38', '37', '石家庄', '石家庄市', '中国,河北省,石家庄市', '2', 'shijiazhuang', '0311', '050011', 'S', '114.502461', '38.045474'); +INSERT INTO `yoshop_region` VALUES ('39', '38', '长安', '长安区', '中国,河北省,石家庄市,长安区', '3', 'chang\'an', '0311', '050011', 'C', '114.53906', '38.03665'); +INSERT INTO `yoshop_region` VALUES ('40', '38', '桥西', '桥西区', '中国,河北省,石家庄市,桥西区', '3', 'qiaoxi', '0311', '050091', 'Q', '114.46977', '38.03221'); +INSERT INTO `yoshop_region` VALUES ('41', '38', '新华', '新华区', '中国,河北省,石家庄市,新华区', '3', 'xinhua', '0311', '050051', 'X', '114.46326', '38.05088'); +INSERT INTO `yoshop_region` VALUES ('42', '38', '井陉矿区', '井陉矿区', '中国,河北省,石家庄市,井陉矿区', '3', 'jingxingkuangqu', '0311', '050100', 'J', '114.06518', '38.06705'); +INSERT INTO `yoshop_region` VALUES ('43', '38', '裕华', '裕华区', '中国,河北省,石家庄市,裕华区', '3', 'yuhua', '0311', '050031', 'Y', '114.53115', '38.00604'); +INSERT INTO `yoshop_region` VALUES ('44', '38', '藁城', '藁城区', '中国,河北省,石家庄市,藁城区', '3', 'gaocheng', '0311', '052160', null, '114.84671', '38.02162'); +INSERT INTO `yoshop_region` VALUES ('45', '38', '鹿泉', '鹿泉区', '中国,河北省,石家庄市,鹿泉区', '3', 'luquan', '0311', '050200', 'L', '114.31347', '38.08782'); +INSERT INTO `yoshop_region` VALUES ('46', '38', '栾城', '栾城区', '中国,河北省,石家庄市,栾城区', '3', 'luancheng', '0311', '051430', null, '114.64834', '37.90022'); +INSERT INTO `yoshop_region` VALUES ('47', '38', '井陉', '井陉县', '中国,河北省,石家庄市,井陉县', '3', 'jingxing', '0311', '050300', 'J', '114.14257', '38.03688'); +INSERT INTO `yoshop_region` VALUES ('48', '38', '正定', '正定县', '中国,河北省,石家庄市,正定县', '3', 'zhengding', '0311', '050800', 'Z', '114.57296', '38.14445'); +INSERT INTO `yoshop_region` VALUES ('49', '38', '行唐', '行唐县', '中国,河北省,石家庄市,行唐县', '3', 'xingtang', '0311', '050600', 'X', '114.55316', '38.43654'); +INSERT INTO `yoshop_region` VALUES ('50', '38', '灵寿', '灵寿县', '中国,河北省,石家庄市,灵寿县', '3', 'lingshou', '0311', '050500', 'L', '114.38259', '38.30845'); +INSERT INTO `yoshop_region` VALUES ('51', '38', '高邑', '高邑县', '中国,河北省,石家庄市,高邑县', '3', 'gaoyi', '0311', '051330', 'G', '114.61142', '37.61556'); +INSERT INTO `yoshop_region` VALUES ('52', '38', '深泽', '深泽县', '中国,河北省,石家庄市,深泽县', '3', 'shenze', '0311', '052560', 'S', '115.20358', '38.18353'); +INSERT INTO `yoshop_region` VALUES ('53', '38', '赞皇', '赞皇县', '中国,河北省,石家庄市,赞皇县', '3', 'zanhuang', '0311', '051230', 'Z', '114.38775', '37.66135'); +INSERT INTO `yoshop_region` VALUES ('54', '38', '无极', '无极县', '中国,河北省,石家庄市,无极县', '3', 'wuji', '0311', '052460', 'W', '114.97509', '38.17653'); +INSERT INTO `yoshop_region` VALUES ('55', '38', '平山', '平山县', '中国,河北省,石家庄市,平山县', '3', 'pingshan', '0311', '050400', 'P', '114.186', '38.25994'); +INSERT INTO `yoshop_region` VALUES ('56', '38', '元氏', '元氏县', '中国,河北省,石家庄市,元氏县', '3', 'yuanshi', '0311', '051130', 'Y', '114.52539', '37.76668'); +INSERT INTO `yoshop_region` VALUES ('57', '38', '赵县', '赵县', '中国,河北省,石家庄市,赵县', '3', 'zhaoxian', '0311', '051530', 'Z', '114.77612', '37.75628'); +INSERT INTO `yoshop_region` VALUES ('58', '38', '辛集', '辛集市', '中国,河北省,石家庄市,辛集市', '3', 'xinji', '0311', '052360', 'X', '115.20626', '37.94079'); +INSERT INTO `yoshop_region` VALUES ('59', '38', '晋州', '晋州市', '中国,河北省,石家庄市,晋州市', '3', 'jinzhou', '0311', '052260', 'J', '115.04348', '38.03135'); +INSERT INTO `yoshop_region` VALUES ('60', '38', '新乐', '新乐市', '中国,河北省,石家庄市,新乐市', '3', 'xinle', '0311', '050700', 'X', '114.68985', '38.34417'); +INSERT INTO `yoshop_region` VALUES ('61', '37', '唐山', '唐山市', '中国,河北省,唐山市', '2', 'tangshan', '0315', '063000', 'T', '118.175393', '39.635113'); +INSERT INTO `yoshop_region` VALUES ('62', '61', '路南', '路南区', '中国,河北省,唐山市,路南区', '3', 'lunan', '0315', '063000', 'L', '118.15431', '39.62505'); +INSERT INTO `yoshop_region` VALUES ('63', '61', '路北', '路北区', '中国,河北省,唐山市,路北区', '3', 'lubei', '0315', '063000', 'L', '118.20079', '39.62436'); +INSERT INTO `yoshop_region` VALUES ('64', '61', '古冶', '古冶区', '中国,河北省,唐山市,古冶区', '3', 'guye', '0315', '063100', 'G', '118.45803', '39.71993'); +INSERT INTO `yoshop_region` VALUES ('65', '61', '开平', '开平区', '中国,河北省,唐山市,开平区', '3', 'kaiping', '0315', '063021', 'K', '118.26171', '39.67128'); +INSERT INTO `yoshop_region` VALUES ('66', '61', '丰南', '丰南区', '中国,河北省,唐山市,丰南区', '3', 'fengnan', '0315', '063300', 'F', '118.11282', '39.56483'); +INSERT INTO `yoshop_region` VALUES ('67', '61', '丰润', '丰润区', '中国,河北省,唐山市,丰润区', '3', 'fengrun', '0315', '064000', 'F', '118.12976', '39.8244'); +INSERT INTO `yoshop_region` VALUES ('68', '61', '曹妃甸', '曹妃甸区', '中国,河北省,唐山市,曹妃甸区', '3', 'caofeidian', '0315', '063200', 'C', '118.460379', '39.273070'); +INSERT INTO `yoshop_region` VALUES ('69', '61', '滦县', '滦县', '中国,河北省,唐山市,滦县', '3', 'luanxian', '0315', '063700', 'L', '118.70346', '39.74056'); +INSERT INTO `yoshop_region` VALUES ('70', '61', '滦南', '滦南县', '中国,河北省,唐山市,滦南县', '3', 'luannan', '0315', '063500', 'L', '118.6741', '39.5039'); +INSERT INTO `yoshop_region` VALUES ('71', '61', '乐亭', '乐亭县', '中国,河北省,唐山市,乐亭县', '3', 'laoting', '0315', '063600', 'L', '118.9125', '39.42561'); +INSERT INTO `yoshop_region` VALUES ('72', '61', '迁西', '迁西县', '中国,河北省,唐山市,迁西县', '3', 'qianxi', '0315', '064300', 'Q', '118.31616', '40.14587'); +INSERT INTO `yoshop_region` VALUES ('73', '61', '玉田', '玉田县', '中国,河北省,唐山市,玉田县', '3', 'yutian', '0315', '064100', 'Y', '117.7388', '39.90049'); +INSERT INTO `yoshop_region` VALUES ('74', '61', '遵化', '遵化市', '中国,河北省,唐山市,遵化市', '3', 'zunhua', '0315', '064200', 'Z', '117.96444', '40.18741'); +INSERT INTO `yoshop_region` VALUES ('75', '61', '迁安', '迁安市', '中国,河北省,唐山市,迁安市', '3', 'qian\'an', '0315', '064400', 'Q', '118.70068', '39.99833'); +INSERT INTO `yoshop_region` VALUES ('76', '37', '秦皇岛', '秦皇岛市', '中国,河北省,秦皇岛市', '2', 'qinhuangdao', '0335', '066000', 'Q', '119.586579', '39.942531'); +INSERT INTO `yoshop_region` VALUES ('77', '76', '海港', '海港区', '中国,河北省,秦皇岛市,海港区', '3', 'haigang', '0335', '066000', 'H', '119.61046', '39.9345'); +INSERT INTO `yoshop_region` VALUES ('78', '76', '山海关', '山海关区', '中国,河北省,秦皇岛市,山海关区', '3', 'shanhaiguan', '0335', '066200', 'S', '119.77563', '39.97869'); +INSERT INTO `yoshop_region` VALUES ('79', '76', '北戴河', '北戴河区', '中国,河北省,秦皇岛市,北戴河区', '3', 'beidaihe', '0335', '066100', 'B', '119.48388', '39.83408'); +INSERT INTO `yoshop_region` VALUES ('80', '76', '青龙', '青龙满族自治县', '中国,河北省,秦皇岛市,青龙满族自治县', '3', 'qinglong', '0335', '066500', 'Q', '118.95242', '40.40743'); +INSERT INTO `yoshop_region` VALUES ('81', '76', '昌黎', '昌黎县', '中国,河北省,秦皇岛市,昌黎县', '3', 'changli', '0335', '066600', 'C', '119.16595', '39.70884'); +INSERT INTO `yoshop_region` VALUES ('82', '76', '抚宁', '抚宁县', '中国,河北省,秦皇岛市,抚宁县', '3', 'funing', '0335', '066300', 'F', '119.24487', '39.87538'); +INSERT INTO `yoshop_region` VALUES ('83', '76', '卢龙', '卢龙县', '中国,河北省,秦皇岛市,卢龙县', '3', 'lulong', '0335', '066400', 'L', '118.89288', '39.89176'); +INSERT INTO `yoshop_region` VALUES ('84', '37', '邯郸', '邯郸市', '中国,河北省,邯郸市', '2', 'handan', '0310', '056002', 'H', '114.490686', '36.612273'); +INSERT INTO `yoshop_region` VALUES ('85', '84', '邯山', '邯山区', '中国,河北省,邯郸市,邯山区', '3', 'hanshan', '0310', '056001', 'H', '114.48375', '36.60006'); +INSERT INTO `yoshop_region` VALUES ('86', '84', '丛台', '丛台区', '中国,河北省,邯郸市,丛台区', '3', 'congtai', '0310', '056002', 'C', '114.49343', '36.61847'); +INSERT INTO `yoshop_region` VALUES ('87', '84', '复兴', '复兴区', '中国,河北省,邯郸市,复兴区', '3', 'fuxing', '0310', '056003', 'F', '114.45928', '36.61134'); +INSERT INTO `yoshop_region` VALUES ('88', '84', '峰峰矿区', '峰峰矿区', '中国,河北省,邯郸市,峰峰矿区', '3', 'fengfengkuangqu', '0310', '056200', 'F', '114.21148', '36.41937'); +INSERT INTO `yoshop_region` VALUES ('89', '84', '邯郸', '邯郸县', '中国,河北省,邯郸市,邯郸县', '3', 'handan', '0310', '056101', 'H', '114.53103', '36.59385'); +INSERT INTO `yoshop_region` VALUES ('90', '84', '临漳', '临漳县', '中国,河北省,邯郸市,临漳县', '3', 'linzhang', '0310', '056600', 'L', '114.6195', '36.33461'); +INSERT INTO `yoshop_region` VALUES ('91', '84', '成安', '成安县', '中国,河北省,邯郸市,成安县', '3', 'cheng\'an', '0310', '056700', 'C', '114.66995', '36.44411'); +INSERT INTO `yoshop_region` VALUES ('92', '84', '大名', '大名县', '中国,河北省,邯郸市,大名县', '3', 'daming', '0310', '056900', 'D', '115.15362', '36.27994'); +INSERT INTO `yoshop_region` VALUES ('93', '84', '涉县', '涉县', '中国,河北省,邯郸市,涉县', '3', 'shexian', '0310', '056400', 'S', '113.69183', '36.58072'); +INSERT INTO `yoshop_region` VALUES ('94', '84', '磁县', '磁县', '中国,河北省,邯郸市,磁县', '3', 'cixian', '0310', '056500', 'C', '114.37387', '36.37392'); +INSERT INTO `yoshop_region` VALUES ('95', '84', '肥乡', '肥乡县', '中国,河北省,邯郸市,肥乡县', '3', 'feixiang', '0310', '057550', 'F', '114.79998', '36.54807'); +INSERT INTO `yoshop_region` VALUES ('96', '84', '永年', '永年县', '中国,河北省,邯郸市,永年县', '3', 'yongnian', '0310', '057150', 'Y', '114.48925', '36.78356'); +INSERT INTO `yoshop_region` VALUES ('97', '84', '邱县', '邱县', '中国,河北省,邯郸市,邱县', '3', 'qiuxian', '0310', '057450', 'Q', '115.17407', '36.82082'); +INSERT INTO `yoshop_region` VALUES ('98', '84', '鸡泽', '鸡泽县', '中国,河北省,邯郸市,鸡泽县', '3', 'jize', '0310', '057350', 'J', '114.8742', '36.92374'); +INSERT INTO `yoshop_region` VALUES ('99', '84', '广平', '广平县', '中国,河北省,邯郸市,广平县', '3', 'guangping', '0310', '057650', 'G', '114.94653', '36.48046'); +INSERT INTO `yoshop_region` VALUES ('100', '84', '馆陶', '馆陶县', '中国,河北省,邯郸市,馆陶县', '3', 'guantao', '0310', '057750', 'G', '115.29913', '36.53719'); +INSERT INTO `yoshop_region` VALUES ('101', '84', '魏县', '魏县', '中国,河北省,邯郸市,魏县', '3', 'weixian', '0310', '056800', 'W', '114.93518', '36.36171'); +INSERT INTO `yoshop_region` VALUES ('102', '84', '曲周', '曲周县', '中国,河北省,邯郸市,曲周县', '3', 'quzhou', '0310', '057250', 'Q', '114.95196', '36.77671'); +INSERT INTO `yoshop_region` VALUES ('103', '84', '武安', '武安市', '中国,河北省,邯郸市,武安市', '3', 'wu\'an', '0310', '056300', 'W', '114.20153', '36.69281'); +INSERT INTO `yoshop_region` VALUES ('104', '37', '邢台', '邢台市', '中国,河北省,邢台市', '2', 'xingtai', '0319', '054001', 'X', '114.508851', '37.0682'); +INSERT INTO `yoshop_region` VALUES ('105', '104', '桥东', '桥东区', '中国,河北省,邢台市,桥东区', '3', 'qiaodong', '0319', '054001', 'Q', '114.50725', '37.06801'); +INSERT INTO `yoshop_region` VALUES ('106', '104', '桥西', '桥西区', '中国,河北省,邢台市,桥西区', '3', 'qiaoxi', '0319', '054000', 'Q', '114.46803', '37.05984'); +INSERT INTO `yoshop_region` VALUES ('107', '104', '邢台', '邢台县', '中国,河北省,邢台市,邢台县', '3', 'xingtai', '0319', '054001', 'X', '114.56575', '37.0456'); +INSERT INTO `yoshop_region` VALUES ('108', '104', '临城', '临城县', '中国,河北省,邢台市,临城县', '3', 'lincheng', '0319', '054300', 'L', '114.50387', '37.43977'); +INSERT INTO `yoshop_region` VALUES ('109', '104', '内丘', '内丘县', '中国,河北省,邢台市,内丘县', '3', 'neiqiu', '0319', '054200', 'N', '114.51212', '37.28671'); +INSERT INTO `yoshop_region` VALUES ('110', '104', '柏乡', '柏乡县', '中国,河北省,邢台市,柏乡县', '3', 'baixiang', '0319', '055450', 'B', '114.69332', '37.48242'); +INSERT INTO `yoshop_region` VALUES ('111', '104', '隆尧', '隆尧县', '中国,河北省,邢台市,隆尧县', '3', 'longyao', '0319', '055350', 'L', '114.77615', '37.35351'); +INSERT INTO `yoshop_region` VALUES ('112', '104', '任县', '任县', '中国,河北省,邢台市,任县', '3', 'renxian', '0319', '055150', 'R', '114.6842', '37.12575'); +INSERT INTO `yoshop_region` VALUES ('113', '104', '南和', '南和县', '中国,河北省,邢台市,南和县', '3', 'nanhe', '0319', '054400', 'N', '114.68371', '37.00488'); +INSERT INTO `yoshop_region` VALUES ('114', '104', '宁晋', '宁晋县', '中国,河北省,邢台市,宁晋县', '3', 'ningjin', '0319', '055550', 'N', '114.92117', '37.61696'); +INSERT INTO `yoshop_region` VALUES ('115', '104', '巨鹿', '巨鹿县', '中国,河北省,邢台市,巨鹿县', '3', 'julu', '0319', '055250', 'J', '115.03524', '37.21801'); +INSERT INTO `yoshop_region` VALUES ('116', '104', '新河', '新河县', '中国,河北省,邢台市,新河县', '3', 'xinhe', '0319', '055650', 'X', '115.24987', '37.52718'); +INSERT INTO `yoshop_region` VALUES ('117', '104', '广宗', '广宗县', '中国,河北省,邢台市,广宗县', '3', 'guangzong', '0319', '054600', 'G', '115.14254', '37.0746'); +INSERT INTO `yoshop_region` VALUES ('118', '104', '平乡', '平乡县', '中国,河北省,邢台市,平乡县', '3', 'pingxiang', '0319', '054500', 'P', '115.03002', '37.06317'); +INSERT INTO `yoshop_region` VALUES ('119', '104', '威县', '威县', '中国,河北省,邢台市,威县', '3', 'weixian', '0319', '054700', 'W', '115.2637', '36.9768'); +INSERT INTO `yoshop_region` VALUES ('120', '104', '清河', '清河县', '中国,河北省,邢台市,清河县', '3', 'qinghe', '0319', '054800', 'Q', '115.66479', '37.07122'); +INSERT INTO `yoshop_region` VALUES ('121', '104', '临西', '临西县', '中国,河北省,邢台市,临西县', '3', 'linxi', '0319', '054900', 'L', '115.50097', '36.87078'); +INSERT INTO `yoshop_region` VALUES ('122', '104', '南宫', '南宫市', '中国,河北省,邢台市,南宫市', '3', 'nangong', '0319', '055750', 'N', '115.39068', '37.35799'); +INSERT INTO `yoshop_region` VALUES ('123', '104', '沙河', '沙河市', '中国,河北省,邢台市,沙河市', '3', 'shahe', '0319', '054100', 'S', '114.4981', '36.8577'); +INSERT INTO `yoshop_region` VALUES ('124', '37', '保定', '保定市', '中国,河北省,保定市', '2', 'baoding', '0312', '071052', 'B', '115.482331', '38.867657'); +INSERT INTO `yoshop_region` VALUES ('125', '124', '新市', '新市区', '中国,河北省,保定市,新市区', '3', 'xinshi', '0312', '071051', 'X', '115.4587', '38.87751'); +INSERT INTO `yoshop_region` VALUES ('126', '124', '北市', '北市区', '中国,河北省,保定市,北市区', '3', 'beishi', '0312', '071000', 'B', '115.49715', '38.88322'); +INSERT INTO `yoshop_region` VALUES ('127', '124', '南市', '南市区', '中国,河北省,保定市,南市区', '3', 'nanshi', '0312', '071001', 'N', '115.52859', '38.85455'); +INSERT INTO `yoshop_region` VALUES ('128', '124', '满城', '满城县', '中国,河北省,保定市,满城县', '3', 'mancheng', '0312', '072150', 'M', '115.32296', '38.94972'); +INSERT INTO `yoshop_region` VALUES ('129', '124', '清苑', '清苑县', '中国,河北省,保定市,清苑县', '3', 'qingyuan', '0312', '071100', 'Q', '115.49267', '38.76709'); +INSERT INTO `yoshop_region` VALUES ('130', '124', '涞水', '涞水县', '中国,河北省,保定市,涞水县', '3', 'laishui', '0312', '074100', null, '115.71517', '39.39404'); +INSERT INTO `yoshop_region` VALUES ('131', '124', '阜平', '阜平县', '中国,河北省,保定市,阜平县', '3', 'fuping', '0312', '073200', 'F', '114.19683', '38.84763'); +INSERT INTO `yoshop_region` VALUES ('132', '124', '徐水', '徐水县', '中国,河北省,保定市,徐水县', '3', 'xushui', '0312', '072550', 'X', '115.65829', '39.02099'); +INSERT INTO `yoshop_region` VALUES ('133', '124', '定兴', '定兴县', '中国,河北省,保定市,定兴县', '3', 'dingxing', '0312', '072650', 'D', '115.80786', '39.26312'); +INSERT INTO `yoshop_region` VALUES ('134', '124', '唐县', '唐县', '中国,河北省,保定市,唐县', '3', 'tangxian', '0312', '072350', 'T', '114.98516', '38.74513'); +INSERT INTO `yoshop_region` VALUES ('135', '124', '高阳', '高阳县', '中国,河北省,保定市,高阳县', '3', 'gaoyang', '0312', '071500', 'G', '115.7788', '38.70003'); +INSERT INTO `yoshop_region` VALUES ('136', '124', '容城', '容城县', '中国,河北省,保定市,容城县', '3', 'rongcheng', '0312', '071700', 'R', '115.87158', '39.0535'); +INSERT INTO `yoshop_region` VALUES ('137', '124', '涞源', '涞源县', '中国,河北省,保定市,涞源县', '3', 'laiyuan', '0312', '074300', null, '114.69128', '39.35388'); +INSERT INTO `yoshop_region` VALUES ('138', '124', '望都', '望都县', '中国,河北省,保定市,望都县', '3', 'wangdu', '0312', '072450', 'W', '115.1567', '38.70996'); +INSERT INTO `yoshop_region` VALUES ('139', '124', '安新', '安新县', '中国,河北省,保定市,安新县', '3', 'anxin', '0312', '071600', 'A', '115.93557', '38.93532'); +INSERT INTO `yoshop_region` VALUES ('140', '124', '易县', '易县', '中国,河北省,保定市,易县', '3', 'yixian', '0312', '074200', 'Y', '115.4981', '39.34885'); +INSERT INTO `yoshop_region` VALUES ('141', '124', '曲阳', '曲阳县', '中国,河北省,保定市,曲阳县', '3', 'quyang', '0312', '073100', 'Q', '114.70123', '38.62154'); +INSERT INTO `yoshop_region` VALUES ('142', '124', '蠡县', '蠡县', '中国,河北省,保定市,蠡县', '3', 'lixian', '0312', '071400', null, '115.57717', '38.48974'); +INSERT INTO `yoshop_region` VALUES ('143', '124', '顺平', '顺平县', '中国,河北省,保定市,顺平县', '3', 'shunping', '0312', '072250', 'S', '115.1347', '38.83854'); +INSERT INTO `yoshop_region` VALUES ('144', '124', '博野', '博野县', '中国,河北省,保定市,博野县', '3', 'boye', '0312', '071300', 'B', '115.47033', '38.4564'); +INSERT INTO `yoshop_region` VALUES ('145', '124', '雄县', '雄县', '中国,河北省,保定市,雄县', '3', 'xiongxian', '0312', '071800', 'X', '116.10873', '38.99442'); +INSERT INTO `yoshop_region` VALUES ('146', '124', '涿州', '涿州市', '中国,河北省,保定市,涿州市', '3', 'zhuozhou', '0312', '072750', null, '115.98062', '39.48622'); +INSERT INTO `yoshop_region` VALUES ('147', '124', '定州', '定州市', '中国,河北省,保定市,定州市', '3', 'dingzhou', '0312', '073000', 'D', '114.9902', '38.51623'); +INSERT INTO `yoshop_region` VALUES ('148', '124', '安国', '安国市', '中国,河北省,保定市,安国市', '3', 'anguo', '0312', '071200', 'A', '115.32321', '38.41391'); +INSERT INTO `yoshop_region` VALUES ('149', '124', '高碑店', '高碑店市', '中国,河北省,保定市,高碑店市', '3', 'gaobeidian', '0312', '074000', 'G', '115.87368', '39.32655'); +INSERT INTO `yoshop_region` VALUES ('150', '37', '张家口', '张家口市', '中国,河北省,张家口市', '2', 'zhangjiakou', '0313', '075000', 'Z', '114.884091', '40.811901'); +INSERT INTO `yoshop_region` VALUES ('151', '150', '桥东', '桥东区', '中国,河北省,张家口市,桥东区', '3', 'qiaodong', '0313', '075000', 'Q', '114.8943', '40.78844'); +INSERT INTO `yoshop_region` VALUES ('152', '150', '桥西', '桥西区', '中国,河北省,张家口市,桥西区', '3', 'qiaoxi', '0313', '075061', 'Q', '114.86962', '40.81945'); +INSERT INTO `yoshop_region` VALUES ('153', '150', '宣化', '宣化区', '中国,河北省,张家口市,宣化区', '3', 'xuanhua', '0313', '075100', 'X', '115.06543', '40.60957'); +INSERT INTO `yoshop_region` VALUES ('154', '150', '下花园', '下花园区', '中国,河北省,张家口市,下花园区', '3', 'xiahuayuan', '0313', '075300', 'X', '115.28744', '40.50236'); +INSERT INTO `yoshop_region` VALUES ('155', '150', '宣化', '宣化县', '中国,河北省,张家口市,宣化县', '3', 'xuanhua', '0313', '075100', 'X', '115.15497', '40.56618'); +INSERT INTO `yoshop_region` VALUES ('156', '150', '张北', '张北县', '中国,河北省,张家口市,张北县', '3', 'zhangbei', '0313', '076450', 'Z', '114.71432', '41.15977'); +INSERT INTO `yoshop_region` VALUES ('157', '150', '康保', '康保县', '中国,河北省,张家口市,康保县', '3', 'kangbao', '0313', '076650', 'K', '114.60031', '41.85225'); +INSERT INTO `yoshop_region` VALUES ('158', '150', '沽源', '沽源县', '中国,河北省,张家口市,沽源县', '3', 'guyuan', '0313', '076550', 'G', '115.68859', '41.66959'); +INSERT INTO `yoshop_region` VALUES ('159', '150', '尚义', '尚义县', '中国,河北省,张家口市,尚义县', '3', 'shangyi', '0313', '076750', 'S', '113.97134', '41.07782'); +INSERT INTO `yoshop_region` VALUES ('160', '150', '蔚县', '蔚县', '中国,河北省,张家口市,蔚县', '3', 'yuxian', '0313', '075700', 'W', '114.58892', '39.84067'); +INSERT INTO `yoshop_region` VALUES ('161', '150', '阳原', '阳原县', '中国,河北省,张家口市,阳原县', '3', 'yangyuan', '0313', '075800', 'Y', '114.15051', '40.10361'); +INSERT INTO `yoshop_region` VALUES ('162', '150', '怀安', '怀安县', '中国,河北省,张家口市,怀安县', '3', 'huai\'an', '0313', '076150', 'H', '114.38559', '40.67425'); +INSERT INTO `yoshop_region` VALUES ('163', '150', '万全', '万全县', '中国,河北省,张家口市,万全县', '3', 'wanquan', '0313', '076250', 'W', '114.7405', '40.76694'); +INSERT INTO `yoshop_region` VALUES ('164', '150', '怀来', '怀来县', '中国,河北省,张家口市,怀来县', '3', 'huailai', '0313', '075400', 'H', '115.51773', '40.41536'); +INSERT INTO `yoshop_region` VALUES ('165', '150', '涿鹿', '涿鹿县', '中国,河北省,张家口市,涿鹿县', '3', 'zhuolu', '0313', '075600', null, '115.22403', '40.37636'); +INSERT INTO `yoshop_region` VALUES ('166', '150', '赤城', '赤城县', '中国,河北省,张家口市,赤城县', '3', 'chicheng', '0313', '075500', 'C', '115.83187', '40.91438'); +INSERT INTO `yoshop_region` VALUES ('167', '150', '崇礼', '崇礼县', '中国,河北省,张家口市,崇礼县', '3', 'chongli', '0313', '076350', 'C', '115.27993', '40.97519'); +INSERT INTO `yoshop_region` VALUES ('168', '37', '承德', '承德市', '中国,河北省,承德市', '2', 'chengde', '0314', '067000', 'C', '117.939152', '40.976204'); +INSERT INTO `yoshop_region` VALUES ('169', '168', '双桥', '双桥区', '中国,河北省,承德市,双桥区', '3', 'shuangqiao', '0314', '067000', 'S', '117.9432', '40.97466'); +INSERT INTO `yoshop_region` VALUES ('170', '168', '双滦', '双滦区', '中国,河北省,承德市,双滦区', '3', 'shuangluan', '0314', '067001', 'S', '117.74487', '40.95375'); +INSERT INTO `yoshop_region` VALUES ('171', '168', '鹰手营子矿区', '鹰手营子矿区', '中国,河北省,承德市,鹰手营子矿区', '3', 'yingshouyingzikuangqu', '0314', '067200', 'Y', '117.65985', '40.54744'); +INSERT INTO `yoshop_region` VALUES ('172', '168', '承德', '承德县', '中国,河北省,承德市,承德县', '3', 'chengde', '0314', '067400', 'C', '118.17639', '40.76985'); +INSERT INTO `yoshop_region` VALUES ('173', '168', '兴隆', '兴隆县', '中国,河北省,承德市,兴隆县', '3', 'xinglong', '0314', '067300', 'X', '117.50073', '40.41709'); +INSERT INTO `yoshop_region` VALUES ('174', '168', '平泉', '平泉县', '中国,河北省,承德市,平泉县', '3', 'pingquan', '0314', '067500', 'P', '118.70196', '41.01839'); +INSERT INTO `yoshop_region` VALUES ('175', '168', '滦平', '滦平县', '中国,河北省,承德市,滦平县', '3', 'luanping', '0314', '068250', 'L', '117.33276', '40.94148'); +INSERT INTO `yoshop_region` VALUES ('176', '168', '隆化', '隆化县', '中国,河北省,承德市,隆化县', '3', 'longhua', '0314', '068150', 'L', '117.7297', '41.31412'); +INSERT INTO `yoshop_region` VALUES ('177', '168', '丰宁', '丰宁满族自治县', '中国,河北省,承德市,丰宁满族自治县', '3', 'fengning', '0314', '068350', 'F', '116.6492', '41.20481'); +INSERT INTO `yoshop_region` VALUES ('178', '168', '宽城', '宽城满族自治县', '中国,河北省,承德市,宽城满族自治县', '3', 'kuancheng', '0314', '067600', 'K', '118.49176', '40.60829'); +INSERT INTO `yoshop_region` VALUES ('179', '168', '围场', '围场满族蒙古族自治县', '中国,河北省,承德市,围场满族蒙古族自治县', '3', 'weichang', '0314', '068450', 'W', '117.7601', '41.94368'); +INSERT INTO `yoshop_region` VALUES ('180', '37', '沧州', '沧州市', '中国,河北省,沧州市', '2', 'cangzhou', '0317', '061001', 'C', '116.857461', '38.310582'); +INSERT INTO `yoshop_region` VALUES ('181', '180', '新华', '新华区', '中国,河北省,沧州市,新华区', '3', 'xinhua', '0317', '061000', 'X', '116.86643', '38.31438'); +INSERT INTO `yoshop_region` VALUES ('182', '180', '运河', '运河区', '中国,河北省,沧州市,运河区', '3', 'yunhe', '0317', '061001', 'Y', '116.85706', '38.31352'); +INSERT INTO `yoshop_region` VALUES ('183', '180', '沧县', '沧县', '中国,河北省,沧州市,沧县', '3', 'cangxian', '0317', '061000', 'C', '116.87817', '38.29361'); +INSERT INTO `yoshop_region` VALUES ('184', '180', '青县', '青县', '中国,河北省,沧州市,青县', '3', 'qingxian', '0317', '062650', 'Q', '116.80316', '38.58345'); +INSERT INTO `yoshop_region` VALUES ('185', '180', '东光', '东光县', '中国,河北省,沧州市,东光县', '3', 'dongguang', '0317', '061600', 'D', '116.53668', '37.8857'); +INSERT INTO `yoshop_region` VALUES ('186', '180', '海兴', '海兴县', '中国,河北省,沧州市,海兴县', '3', 'haixing', '0317', '061200', 'H', '117.49758', '38.13958'); +INSERT INTO `yoshop_region` VALUES ('187', '180', '盐山', '盐山县', '中国,河北省,沧州市,盐山县', '3', 'yanshan', '0317', '061300', 'Y', '117.23092', '38.05647'); +INSERT INTO `yoshop_region` VALUES ('188', '180', '肃宁', '肃宁县', '中国,河北省,沧州市,肃宁县', '3', 'suning', '0317', '062350', 'S', '115.82971', '38.42272'); +INSERT INTO `yoshop_region` VALUES ('189', '180', '南皮', '南皮县', '中国,河北省,沧州市,南皮县', '3', 'nanpi', '0317', '061500', 'N', '116.70224', '38.04109'); +INSERT INTO `yoshop_region` VALUES ('190', '180', '吴桥', '吴桥县', '中国,河北省,沧州市,吴桥县', '3', 'wuqiao', '0317', '061800', 'W', '116.3847', '37.62546'); +INSERT INTO `yoshop_region` VALUES ('191', '180', '献县', '献县', '中国,河北省,沧州市,献县', '3', 'xianxian', '0317', '062250', 'X', '116.12695', '38.19228'); +INSERT INTO `yoshop_region` VALUES ('192', '180', '孟村', '孟村回族自治县', '中国,河北省,沧州市,孟村回族自治县', '3', 'mengcun', '0317', '061400', 'M', '117.10412', '38.05338'); +INSERT INTO `yoshop_region` VALUES ('193', '180', '泊头', '泊头市', '中国,河北省,沧州市,泊头市', '3', 'botou', '0317', '062150', 'B', '116.57824', '38.08359'); +INSERT INTO `yoshop_region` VALUES ('194', '180', '任丘', '任丘市', '中国,河北省,沧州市,任丘市', '3', 'renqiu', '0317', '062550', 'R', '116.1033', '38.71124'); +INSERT INTO `yoshop_region` VALUES ('195', '180', '黄骅', '黄骅市', '中国,河北省,沧州市,黄骅市', '3', 'huanghua', '0317', '061100', 'H', '117.33883', '38.3706'); +INSERT INTO `yoshop_region` VALUES ('196', '180', '河间', '河间市', '中国,河北省,沧州市,河间市', '3', 'hejian', '0317', '062450', 'H', '116.0993', '38.44549'); +INSERT INTO `yoshop_region` VALUES ('197', '37', '廊坊', '廊坊市', '中国,河北省,廊坊市', '2', 'langfang', '0316', '065000', 'L', '116.713873', '39.529244'); +INSERT INTO `yoshop_region` VALUES ('198', '197', '安次', '安次区', '中国,河北省,廊坊市,安次区', '3', 'anci', '0316', '065000', 'A', '116.70308', '39.52057'); +INSERT INTO `yoshop_region` VALUES ('199', '197', '广阳', '广阳区', '中国,河北省,廊坊市,广阳区', '3', 'guangyang', '0316', '065000', 'G', '116.71069', '39.52278'); +INSERT INTO `yoshop_region` VALUES ('200', '197', '固安', '固安县', '中国,河北省,廊坊市,固安县', '3', 'gu\'an', '0316', '065500', 'G', '116.29916', '39.43833'); +INSERT INTO `yoshop_region` VALUES ('201', '197', '永清', '永清县', '中国,河北省,廊坊市,永清县', '3', 'yongqing', '0316', '065600', 'Y', '116.50091', '39.32069'); +INSERT INTO `yoshop_region` VALUES ('202', '197', '香河', '香河县', '中国,河北省,廊坊市,香河县', '3', 'xianghe', '0316', '065400', 'X', '117.00634', '39.76133'); +INSERT INTO `yoshop_region` VALUES ('203', '197', '大城', '大城县', '中国,河北省,廊坊市,大城县', '3', 'daicheng', '0316', '065900', 'D', '116.65353', '38.70534'); +INSERT INTO `yoshop_region` VALUES ('204', '197', '文安', '文安县', '中国,河北省,廊坊市,文安县', '3', 'wen\'an', '0316', '065800', 'W', '116.45846', '38.87325'); +INSERT INTO `yoshop_region` VALUES ('205', '197', '大厂', '大厂回族自治县', '中国,河北省,廊坊市,大厂回族自治县', '3', 'dachang', '0316', '065300', 'D', '116.98916', '39.88649'); +INSERT INTO `yoshop_region` VALUES ('206', '197', '霸州', '霸州市', '中国,河北省,廊坊市,霸州市', '3', 'bazhou', '0316', '065700', 'B', '116.39154', '39.12569'); +INSERT INTO `yoshop_region` VALUES ('207', '197', '三河', '三河市', '中国,河北省,廊坊市,三河市', '3', 'sanhe', '0316', '065200', 'S', '117.07229', '39.98358'); +INSERT INTO `yoshop_region` VALUES ('208', '37', '衡水', '衡水市', '中国,河北省,衡水市', '2', 'hengshui', '0318', '053000', 'H', '115.665993', '37.735097'); +INSERT INTO `yoshop_region` VALUES ('209', '208', '桃城', '桃城区', '中国,河北省,衡水市,桃城区', '3', 'taocheng', '0318', '053000', 'T', '115.67529', '37.73499'); +INSERT INTO `yoshop_region` VALUES ('210', '208', '枣强', '枣强县', '中国,河北省,衡水市,枣强县', '3', 'zaoqiang', '0318', '053100', 'Z', '115.72576', '37.51027'); +INSERT INTO `yoshop_region` VALUES ('211', '208', '武邑', '武邑县', '中国,河北省,衡水市,武邑县', '3', 'wuyi', '0318', '053400', 'W', '115.88748', '37.80181'); +INSERT INTO `yoshop_region` VALUES ('212', '208', '武强', '武强县', '中国,河北省,衡水市,武强县', '3', 'wuqiang', '0318', '053300', 'W', '115.98226', '38.04138'); +INSERT INTO `yoshop_region` VALUES ('213', '208', '饶阳', '饶阳县', '中国,河北省,衡水市,饶阳县', '3', 'raoyang', '0318', '053900', 'R', '115.72558', '38.23529'); +INSERT INTO `yoshop_region` VALUES ('214', '208', '安平', '安平县', '中国,河北省,衡水市,安平县', '3', 'anping', '0318', '053600', 'A', '115.51876', '38.23388'); +INSERT INTO `yoshop_region` VALUES ('215', '208', '故城', '故城县', '中国,河北省,衡水市,故城县', '3', 'gucheng', '0318', '053800', 'G', '115.97076', '37.34773'); +INSERT INTO `yoshop_region` VALUES ('216', '208', '景县', '景县', '中国,河北省,衡水市,景县', '3', 'jingxian', '0318', '053500', 'J', '116.26904', '37.6926'); +INSERT INTO `yoshop_region` VALUES ('217', '208', '阜城', '阜城县', '中国,河北省,衡水市,阜城县', '3', 'fucheng', '0318', '053700', 'F', '116.14431', '37.86881'); +INSERT INTO `yoshop_region` VALUES ('218', '208', '冀州', '冀州市', '中国,河北省,衡水市,冀州市', '3', 'jizhou', '0318', '053200', 'J', '115.57934', '37.55082'); +INSERT INTO `yoshop_region` VALUES ('219', '208', '深州', '深州市', '中国,河北省,衡水市,深州市', '3', 'shenzhou', '0318', '053800', 'S', '115.55993', '38.00109'); +INSERT INTO `yoshop_region` VALUES ('220', '0', '山西', '山西省', '中国,山西省', '1', 'shanxi', '', '', 'S', '112.549248', '37.857014'); +INSERT INTO `yoshop_region` VALUES ('221', '220', '太原', '太原市', '中国,山西省,太原市', '2', 'taiyuan', '0351', '030082', 'T', '112.549248', '37.857014'); +INSERT INTO `yoshop_region` VALUES ('222', '221', '小店', '小店区', '中国,山西省,太原市,小店区', '3', 'xiaodian', '0351', '030032', 'X', '112.56878', '37.73565'); +INSERT INTO `yoshop_region` VALUES ('223', '221', '迎泽', '迎泽区', '中国,山西省,太原市,迎泽区', '3', 'yingze', '0351', '030002', 'Y', '112.56338', '37.86326'); +INSERT INTO `yoshop_region` VALUES ('224', '221', '杏花岭', '杏花岭区', '中国,山西省,太原市,杏花岭区', '3', 'xinghualing', '0351', '030009', 'X', '112.56237', '37.88429'); +INSERT INTO `yoshop_region` VALUES ('225', '221', '尖草坪', '尖草坪区', '中国,山西省,太原市,尖草坪区', '3', 'jiancaoping', '0351', '030023', 'J', '112.48709', '37.94193'); +INSERT INTO `yoshop_region` VALUES ('226', '221', '万柏林', '万柏林区', '中国,山西省,太原市,万柏林区', '3', 'wanbailin', '0351', '030024', 'W', '112.51553', '37.85923'); +INSERT INTO `yoshop_region` VALUES ('227', '221', '晋源', '晋源区', '中国,山西省,太原市,晋源区', '3', 'jinyuan', '0351', '030025', 'J', '112.47985', '37.72479'); +INSERT INTO `yoshop_region` VALUES ('228', '221', '清徐', '清徐县', '中国,山西省,太原市,清徐县', '3', 'qingxu', '0351', '030400', 'Q', '112.35888', '37.60758'); +INSERT INTO `yoshop_region` VALUES ('229', '221', '阳曲', '阳曲县', '中国,山西省,太原市,阳曲县', '3', 'yangqu', '0351', '030100', 'Y', '112.67861', '38.05989'); +INSERT INTO `yoshop_region` VALUES ('230', '221', '娄烦', '娄烦县', '中国,山西省,太原市,娄烦县', '3', 'loufan', '0351', '030300', 'L', '111.79473', '38.06689'); +INSERT INTO `yoshop_region` VALUES ('231', '221', '古交', '古交市', '中国,山西省,太原市,古交市', '3', 'gujiao', '0351', '030200', 'G', '112.16918', '37.90983'); +INSERT INTO `yoshop_region` VALUES ('232', '220', '大同', '大同市', '中国,山西省,大同市', '2', 'datong', '0352', '037008', 'D', '113.295259', '40.09031'); +INSERT INTO `yoshop_region` VALUES ('233', '232', '城区', '城区', '中国,山西省,大同市,城区', '3', 'chengqu', '0352', '037008', 'C', '113.298', '40.07566'); +INSERT INTO `yoshop_region` VALUES ('234', '232', '矿区', '矿区', '中国,山西省,大同市,矿区', '3', 'kuangqu', '0352', '037003', 'K', '113.1772', '40.03685'); +INSERT INTO `yoshop_region` VALUES ('235', '232', '南郊', '南郊区', '中国,山西省,大同市,南郊区', '3', 'nanjiao', '0352', '037001', 'N', '113.14947', '40.00539'); +INSERT INTO `yoshop_region` VALUES ('236', '232', '新荣', '新荣区', '中国,山西省,大同市,新荣区', '3', 'xinrong', '0352', '037002', 'X', '113.13504', '40.25618'); +INSERT INTO `yoshop_region` VALUES ('237', '232', '阳高', '阳高县', '中国,山西省,大同市,阳高县', '3', 'yanggao', '0352', '038100', 'Y', '113.75012', '40.36256'); +INSERT INTO `yoshop_region` VALUES ('238', '232', '天镇', '天镇县', '中国,山西省,大同市,天镇县', '3', 'tianzhen', '0352', '038200', 'T', '114.0931', '40.42299'); +INSERT INTO `yoshop_region` VALUES ('239', '232', '广灵', '广灵县', '中国,山西省,大同市,广灵县', '3', 'guangling', '0352', '037500', 'G', '114.28204', '39.76082'); +INSERT INTO `yoshop_region` VALUES ('240', '232', '灵丘', '灵丘县', '中国,山西省,大同市,灵丘县', '3', 'lingqiu', '0352', '034400', 'L', '114.23672', '39.44043'); +INSERT INTO `yoshop_region` VALUES ('241', '232', '浑源', '浑源县', '中国,山西省,大同市,浑源县', '3', 'hunyuan', '0352', '037400', 'H', '113.69552', '39.69962'); +INSERT INTO `yoshop_region` VALUES ('242', '232', '左云', '左云县', '中国,山西省,大同市,左云县', '3', 'zuoyun', '0352', '037100', 'Z', '112.70266', '40.01336'); +INSERT INTO `yoshop_region` VALUES ('243', '232', '大同', '大同县', '中国,山西省,大同市,大同县', '3', 'datong', '0352', '037300', 'D', '113.61212', '40.04012'); +INSERT INTO `yoshop_region` VALUES ('244', '220', '阳泉', '阳泉市', '中国,山西省,阳泉市', '2', 'yangquan', '0353', '045000', 'Y', '113.583285', '37.861188'); +INSERT INTO `yoshop_region` VALUES ('245', '244', '城区', '城区', '中国,山西省,阳泉市,城区', '3', 'chengqu', '0353', '045000', 'C', '113.60069', '37.8474'); +INSERT INTO `yoshop_region` VALUES ('246', '244', '矿区', '矿区', '中国,山西省,阳泉市,矿区', '3', 'kuangqu', '0353', '045000', 'K', '113.55677', '37.86895'); +INSERT INTO `yoshop_region` VALUES ('247', '244', '郊区', '郊区', '中国,山西省,阳泉市,郊区', '3', 'jiaoqu', '0353', '045011', 'J', '113.58539', '37.94139'); +INSERT INTO `yoshop_region` VALUES ('248', '244', '平定', '平定县', '中国,山西省,阳泉市,平定县', '3', 'pingding', '0353', '045200', 'P', '113.65789', '37.78601'); +INSERT INTO `yoshop_region` VALUES ('249', '244', '盂县', '盂县', '中国,山西省,阳泉市,盂县', '3', 'yuxian', '0353', '045100', 'Y', '113.41235', '38.08579'); +INSERT INTO `yoshop_region` VALUES ('250', '220', '长治', '长治市', '中国,山西省,长治市', '2', 'changzhi', '0355', '046000', 'C', '113.113556', '36.191112'); +INSERT INTO `yoshop_region` VALUES ('251', '250', '城区', '城区', '中国,山西省,长治市,城区', '3', 'chengqu', '0355', '046011', 'C', '113.12308', '36.20351'); +INSERT INTO `yoshop_region` VALUES ('252', '250', '郊区', '郊区', '中国,山西省,长治市,郊区', '3', 'jiaoqu', '0355', '046011', 'J', '113.12653', '36.19918'); +INSERT INTO `yoshop_region` VALUES ('253', '250', '长治', '长治县', '中国,山西省,长治市,长治县', '3', 'changzhi', '0355', '047100', 'C', '113.04791', '36.04722'); +INSERT INTO `yoshop_region` VALUES ('254', '250', '襄垣', '襄垣县', '中国,山西省,长治市,襄垣县', '3', 'xiangyuan', '0355', '046200', 'X', '113.05157', '36.53527'); +INSERT INTO `yoshop_region` VALUES ('255', '250', '屯留', '屯留县', '中国,山西省,长治市,屯留县', '3', 'tunliu', '0355', '046100', 'T', '112.89196', '36.31579'); +INSERT INTO `yoshop_region` VALUES ('256', '250', '平顺', '平顺县', '中国,山西省,长治市,平顺县', '3', 'pingshun', '0355', '047400', 'P', '113.43603', '36.20005'); +INSERT INTO `yoshop_region` VALUES ('257', '250', '黎城', '黎城县', '中国,山西省,长治市,黎城县', '3', 'licheng', '0355', '047600', 'L', '113.38766', '36.50301'); +INSERT INTO `yoshop_region` VALUES ('258', '250', '壶关', '壶关县', '中国,山西省,长治市,壶关县', '3', 'huguan', '0355', '047300', 'H', '113.207', '36.11301'); +INSERT INTO `yoshop_region` VALUES ('259', '250', '长子', '长子县', '中国,山西省,长治市,长子县', '3', 'zhangzi', '0355', '046600', 'C', '112.87731', '36.12125'); +INSERT INTO `yoshop_region` VALUES ('260', '250', '武乡', '武乡县', '中国,山西省,长治市,武乡县', '3', 'wuxiang', '0355', '046300', 'W', '112.86343', '36.83687'); +INSERT INTO `yoshop_region` VALUES ('261', '250', '沁县', '沁县', '中国,山西省,长治市,沁县', '3', 'qinxian', '0355', '046400', 'Q', '112.69863', '36.75628'); +INSERT INTO `yoshop_region` VALUES ('262', '250', '沁源', '沁源县', '中国,山西省,长治市,沁源县', '3', 'qinyuan', '0355', '046500', 'Q', '112.33758', '36.50008'); +INSERT INTO `yoshop_region` VALUES ('263', '250', '潞城', '潞城市', '中国,山西省,长治市,潞城市', '3', 'lucheng', '0355', '047500', 'L', '113.22888', '36.33414'); +INSERT INTO `yoshop_region` VALUES ('264', '220', '晋城', '晋城市', '中国,山西省,晋城市', '2', 'jincheng', '0356', '048000', 'J', '112.851274', '35.497553'); +INSERT INTO `yoshop_region` VALUES ('265', '264', '城区', '城区', '中国,山西省,晋城市,城区', '3', 'chengqu', '0356', '048000', 'C', '112.85319', '35.50175'); +INSERT INTO `yoshop_region` VALUES ('266', '264', '沁水', '沁水县', '中国,山西省,晋城市,沁水县', '3', 'qinshui', '0356', '048200', 'Q', '112.1871', '35.69102'); +INSERT INTO `yoshop_region` VALUES ('267', '264', '阳城', '阳城县', '中国,山西省,晋城市,阳城县', '3', 'yangcheng', '0356', '048100', 'Y', '112.41485', '35.48614'); +INSERT INTO `yoshop_region` VALUES ('268', '264', '陵川', '陵川县', '中国,山西省,晋城市,陵川县', '3', 'lingchuan', '0356', '048300', 'L', '113.2806', '35.77532'); +INSERT INTO `yoshop_region` VALUES ('269', '264', '泽州', '泽州县', '中国,山西省,晋城市,泽州县', '3', 'zezhou', '0356', '048012', 'Z', '112.83947', '35.50789'); +INSERT INTO `yoshop_region` VALUES ('270', '264', '高平', '高平市', '中国,山西省,晋城市,高平市', '3', 'gaoping', '0356', '048400', 'G', '112.92288', '35.79705'); +INSERT INTO `yoshop_region` VALUES ('271', '220', '朔州', '朔州市', '中国,山西省,朔州市', '2', 'shuozhou', '0349', '038500', 'S', '112.433387', '39.331261'); +INSERT INTO `yoshop_region` VALUES ('272', '271', '朔城', '朔城区', '中国,山西省,朔州市,朔城区', '3', 'shuocheng', '0349', '036000', 'S', '112.43189', '39.31982'); +INSERT INTO `yoshop_region` VALUES ('273', '271', '平鲁', '平鲁区', '中国,山西省,朔州市,平鲁区', '3', 'pinglu', '0349', '038600', 'P', '112.28833', '39.51155'); +INSERT INTO `yoshop_region` VALUES ('274', '271', '山阴', '山阴县', '中国,山西省,朔州市,山阴县', '3', 'shanyin', '0349', '036900', 'S', '112.81662', '39.52697'); +INSERT INTO `yoshop_region` VALUES ('275', '271', '应县', '应县', '中国,山西省,朔州市,应县', '3', 'yingxian', '0349', '037600', 'Y', '113.19052', '39.55279'); +INSERT INTO `yoshop_region` VALUES ('276', '271', '右玉', '右玉县', '中国,山西省,朔州市,右玉县', '3', 'youyu', '0349', '037200', 'Y', '112.46902', '39.99011'); +INSERT INTO `yoshop_region` VALUES ('277', '271', '怀仁', '怀仁县', '中国,山西省,朔州市,怀仁县', '3', 'huairen', '0349', '038300', 'H', '113.10009', '39.82806'); +INSERT INTO `yoshop_region` VALUES ('278', '220', '晋中', '晋中市', '中国,山西省,晋中市', '2', 'jinzhong', '0354', '030600', 'J', '112.736465', '37.696495'); +INSERT INTO `yoshop_region` VALUES ('279', '278', '榆次', '榆次区', '中国,山西省,晋中市,榆次区', '3', 'yuci', '0354', '030600', 'Y', '112.70788', '37.6978'); +INSERT INTO `yoshop_region` VALUES ('280', '278', '榆社', '榆社县', '中国,山西省,晋中市,榆社县', '3', 'yushe', '0354', '031800', 'Y', '112.97558', '37.0721'); +INSERT INTO `yoshop_region` VALUES ('281', '278', '左权', '左权县', '中国,山西省,晋中市,左权县', '3', 'zuoquan', '0354', '032600', 'Z', '113.37918', '37.08235'); +INSERT INTO `yoshop_region` VALUES ('282', '278', '和顺', '和顺县', '中国,山西省,晋中市,和顺县', '3', 'heshun', '0354', '032700', 'H', '113.56988', '37.32963'); +INSERT INTO `yoshop_region` VALUES ('283', '278', '昔阳', '昔阳县', '中国,山西省,晋中市,昔阳县', '3', 'xiyang', '0354', '045300', 'X', '113.70517', '37.61863'); +INSERT INTO `yoshop_region` VALUES ('284', '278', '寿阳', '寿阳县', '中国,山西省,晋中市,寿阳县', '3', 'shouyang', '0354', '045400', 'S', '113.17495', '37.88899'); +INSERT INTO `yoshop_region` VALUES ('285', '278', '太谷', '太谷县', '中国,山西省,晋中市,太谷县', '3', 'taigu', '0354', '030800', 'T', '112.55246', '37.42161'); +INSERT INTO `yoshop_region` VALUES ('286', '278', '祁县', '祁县', '中国,山西省,晋中市,祁县', '3', 'qixian', '0354', '030900', 'Q', '112.33358', '37.3579'); +INSERT INTO `yoshop_region` VALUES ('287', '278', '平遥', '平遥县', '中国,山西省,晋中市,平遥县', '3', 'pingyao', '0354', '031100', 'P', '112.17553', '37.1892'); +INSERT INTO `yoshop_region` VALUES ('288', '278', '灵石', '灵石县', '中国,山西省,晋中市,灵石县', '3', 'lingshi', '0354', '031300', 'L', '111.7774', '36.84814'); +INSERT INTO `yoshop_region` VALUES ('289', '278', '介休', '介休市', '中国,山西省,晋中市,介休市', '3', 'jiexiu', '0354', '032000', 'J', '111.91824', '37.02771'); +INSERT INTO `yoshop_region` VALUES ('290', '220', '运城', '运城市', '中国,山西省,运城市', '2', 'yuncheng', '0359', '044000', 'Y', '111.003957', '35.022778'); +INSERT INTO `yoshop_region` VALUES ('291', '290', '盐湖', '盐湖区', '中国,山西省,运城市,盐湖区', '3', 'yanhu', '0359', '044000', 'Y', '110.99827', '35.0151'); +INSERT INTO `yoshop_region` VALUES ('292', '290', '临猗', '临猗县', '中国,山西省,运城市,临猗县', '3', 'linyi', '0359', '044100', 'L', '110.77432', '35.14455'); +INSERT INTO `yoshop_region` VALUES ('293', '290', '万荣', '万荣县', '中国,山西省,运城市,万荣县', '3', 'wanrong', '0359', '044200', 'W', '110.83657', '35.41556'); +INSERT INTO `yoshop_region` VALUES ('294', '290', '闻喜', '闻喜县', '中国,山西省,运城市,闻喜县', '3', 'wenxi', '0359', '043800', 'W', '111.22265', '35.35553'); +INSERT INTO `yoshop_region` VALUES ('295', '290', '稷山', '稷山县', '中国,山西省,运城市,稷山县', '3', 'jishan', '0359', '043200', null, '110.97924', '35.59993'); +INSERT INTO `yoshop_region` VALUES ('296', '290', '新绛', '新绛县', '中国,山西省,运城市,新绛县', '3', 'xinjiang', '0359', '043100', 'X', '111.22509', '35.61566'); +INSERT INTO `yoshop_region` VALUES ('297', '290', '绛县', '绛县', '中国,山西省,运城市,绛县', '3', 'jiangxian', '0359', '043600', null, '111.56668', '35.49096'); +INSERT INTO `yoshop_region` VALUES ('298', '290', '垣曲', '垣曲县', '中国,山西省,运城市,垣曲县', '3', 'yuanqu', '0359', '043700', 'Y', '111.67166', '35.29923'); +INSERT INTO `yoshop_region` VALUES ('299', '290', '夏县', '夏县', '中国,山西省,运城市,夏县', '3', 'xiaxian', '0359', '044400', 'X', '111.21966', '35.14121'); +INSERT INTO `yoshop_region` VALUES ('300', '290', '平陆', '平陆县', '中国,山西省,运城市,平陆县', '3', 'pinglu', '0359', '044300', 'P', '111.21704', '34.83772'); +INSERT INTO `yoshop_region` VALUES ('301', '290', '芮城', '芮城县', '中国,山西省,运城市,芮城县', '3', 'ruicheng', '0359', '044600', null, '110.69455', '34.69384'); +INSERT INTO `yoshop_region` VALUES ('302', '290', '永济', '永济市', '中国,山西省,运城市,永济市', '3', 'yongji', '0359', '044500', 'Y', '110.44537', '34.86556'); +INSERT INTO `yoshop_region` VALUES ('303', '290', '河津', '河津市', '中国,山西省,运城市,河津市', '3', 'hejin', '0359', '043300', 'H', '110.7116', '35.59478'); +INSERT INTO `yoshop_region` VALUES ('304', '220', '忻州', '忻州市', '中国,山西省,忻州市', '2', 'xinzhou', '0350', '034000', 'X', '112.733538', '38.41769'); +INSERT INTO `yoshop_region` VALUES ('305', '304', '忻府', '忻府区', '中国,山西省,忻州市,忻府区', '3', 'xinfu', '0350', '034000', 'X', '112.74603', '38.40414'); +INSERT INTO `yoshop_region` VALUES ('306', '304', '定襄', '定襄县', '中国,山西省,忻州市,定襄县', '3', 'dingxiang', '0350', '035400', 'D', '112.95733', '38.47387'); +INSERT INTO `yoshop_region` VALUES ('307', '304', '五台', '五台县', '中国,山西省,忻州市,五台县', '3', 'wutai', '0350', '035500', 'W', '113.25256', '38.72774'); +INSERT INTO `yoshop_region` VALUES ('308', '304', '代县', '代县', '中国,山西省,忻州市,代县', '3', 'daixian', '0350', '034200', 'D', '112.95913', '39.06717'); +INSERT INTO `yoshop_region` VALUES ('309', '304', '繁峙', '繁峙县', '中国,山西省,忻州市,繁峙县', '3', 'fanshi', '0350', '034300', 'F', '113.26303', '39.18886'); +INSERT INTO `yoshop_region` VALUES ('310', '304', '宁武', '宁武县', '中国,山西省,忻州市,宁武县', '3', 'ningwu', '0350', '036700', 'N', '112.30423', '39.00211'); +INSERT INTO `yoshop_region` VALUES ('311', '304', '静乐', '静乐县', '中国,山西省,忻州市,静乐县', '3', 'jingle', '0350', '035100', 'J', '111.94158', '38.3602'); +INSERT INTO `yoshop_region` VALUES ('312', '304', '神池', '神池县', '中国,山西省,忻州市,神池县', '3', 'shenchi', '0350', '036100', 'S', '112.20541', '39.09'); +INSERT INTO `yoshop_region` VALUES ('313', '304', '五寨', '五寨县', '中国,山西省,忻州市,五寨县', '3', 'wuzhai', '0350', '036200', 'W', '111.8489', '38.90757'); +INSERT INTO `yoshop_region` VALUES ('314', '304', '岢岚', '岢岚县', '中国,山西省,忻州市,岢岚县', '3', 'kelan', '0350', '036300', null, '111.57388', '38.70452'); +INSERT INTO `yoshop_region` VALUES ('315', '304', '河曲', '河曲县', '中国,山西省,忻州市,河曲县', '3', 'hequ', '0350', '036500', 'H', '111.13821', '39.38439'); +INSERT INTO `yoshop_region` VALUES ('316', '304', '保德', '保德县', '中国,山西省,忻州市,保德县', '3', 'baode', '0350', '036600', 'B', '111.08656', '39.02248'); +INSERT INTO `yoshop_region` VALUES ('317', '304', '偏关', '偏关县', '中国,山西省,忻州市,偏关县', '3', 'pianguan', '0350', '036400', 'P', '111.50863', '39.43609'); +INSERT INTO `yoshop_region` VALUES ('318', '304', '原平', '原平市', '中国,山西省,忻州市,原平市', '3', 'yuanping', '0350', '034100', 'Y', '112.70584', '38.73181'); +INSERT INTO `yoshop_region` VALUES ('319', '220', '临汾', '临汾市', '中国,山西省,临汾市', '2', 'linfen', '0357', '041000', 'L', '111.517973', '36.08415'); +INSERT INTO `yoshop_region` VALUES ('320', '319', '尧都', '尧都区', '中国,山西省,临汾市,尧都区', '3', 'yaodu', '0357', '041000', 'Y', '111.5787', '36.08298'); +INSERT INTO `yoshop_region` VALUES ('321', '319', '曲沃', '曲沃县', '中国,山西省,临汾市,曲沃县', '3', 'quwo', '0357', '043400', 'Q', '111.47525', '35.64119'); +INSERT INTO `yoshop_region` VALUES ('322', '319', '翼城', '翼城县', '中国,山西省,临汾市,翼城县', '3', 'yicheng', '0357', '043500', 'Y', '111.7181', '35.73881'); +INSERT INTO `yoshop_region` VALUES ('323', '319', '襄汾', '襄汾县', '中国,山西省,临汾市,襄汾县', '3', 'xiangfen', '0357', '041500', 'X', '111.44204', '35.87711'); +INSERT INTO `yoshop_region` VALUES ('324', '319', '洪洞', '洪洞县', '中国,山西省,临汾市,洪洞县', '3', 'hongtong', '0357', '041600', 'H', '111.67501', '36.25425'); +INSERT INTO `yoshop_region` VALUES ('325', '319', '古县', '古县', '中国,山西省,临汾市,古县', '3', 'guxian', '0357', '042400', 'G', '111.92041', '36.26688'); +INSERT INTO `yoshop_region` VALUES ('326', '319', '安泽', '安泽县', '中国,山西省,临汾市,安泽县', '3', 'anze', '0357', '042500', 'A', '112.24981', '36.14803'); +INSERT INTO `yoshop_region` VALUES ('327', '319', '浮山', '浮山县', '中国,山西省,临汾市,浮山县', '3', 'fushan', '0357', '042600', 'F', '111.84744', '35.96854'); +INSERT INTO `yoshop_region` VALUES ('328', '319', '吉县', '吉县', '中国,山西省,临汾市,吉县', '3', 'jixian', '0357', '042200', 'J', '110.68148', '36.09873'); +INSERT INTO `yoshop_region` VALUES ('329', '319', '乡宁', '乡宁县', '中国,山西省,临汾市,乡宁县', '3', 'xiangning', '0357', '042100', 'X', '110.84652', '35.97072'); +INSERT INTO `yoshop_region` VALUES ('330', '319', '大宁', '大宁县', '中国,山西省,临汾市,大宁县', '3', 'daning', '0357', '042300', 'D', '110.75216', '36.46624'); +INSERT INTO `yoshop_region` VALUES ('331', '319', '隰县', '隰县', '中国,山西省,临汾市,隰县', '3', 'xixian', '0357', '041300', null, '110.93881', '36.69258'); +INSERT INTO `yoshop_region` VALUES ('332', '319', '永和', '永和县', '中国,山西省,临汾市,永和县', '3', 'yonghe', '0357', '041400', 'Y', '110.63168', '36.7584'); +INSERT INTO `yoshop_region` VALUES ('333', '319', '蒲县', '蒲县', '中国,山西省,临汾市,蒲县', '3', 'puxian', '0357', '041200', 'P', '111.09674', '36.41243'); +INSERT INTO `yoshop_region` VALUES ('334', '319', '汾西', '汾西县', '中国,山西省,临汾市,汾西县', '3', 'fenxi', '0357', '031500', 'F', '111.56811', '36.65063'); +INSERT INTO `yoshop_region` VALUES ('335', '319', '侯马', '侯马市', '中国,山西省,临汾市,侯马市', '3', 'houma', '0357', '043000', 'H', '111.37207', '35.61903'); +INSERT INTO `yoshop_region` VALUES ('336', '319', '霍州', '霍州市', '中国,山西省,临汾市,霍州市', '3', 'huozhou', '0357', '031400', 'H', '111.755', '36.5638'); +INSERT INTO `yoshop_region` VALUES ('337', '220', '吕梁', '吕梁市', '中国,山西省,吕梁市', '2', 'lvliang', '0358', '033000', 'L', '111.134335', '37.524366'); +INSERT INTO `yoshop_region` VALUES ('338', '337', '离石', '离石区', '中国,山西省,吕梁市,离石区', '3', 'lishi', '0358', '033000', 'L', '111.15059', '37.5177'); +INSERT INTO `yoshop_region` VALUES ('339', '337', '文水', '文水县', '中国,山西省,吕梁市,文水县', '3', 'wenshui', '0358', '032100', 'W', '112.02829', '37.43841'); +INSERT INTO `yoshop_region` VALUES ('340', '337', '交城', '交城县', '中国,山西省,吕梁市,交城县', '3', 'jiaocheng', '0358', '030500', 'J', '112.1585', '37.5512'); +INSERT INTO `yoshop_region` VALUES ('341', '337', '兴县', '兴县', '中国,山西省,吕梁市,兴县', '3', 'xingxian', '0358', '033600', 'X', '111.12692', '38.46321'); +INSERT INTO `yoshop_region` VALUES ('342', '337', '临县', '临县', '中国,山西省,吕梁市,临县', '3', 'linxian', '0358', '033200', 'L', '110.99282', '37.95271'); +INSERT INTO `yoshop_region` VALUES ('343', '337', '柳林', '柳林县', '中国,山西省,吕梁市,柳林县', '3', 'liulin', '0358', '033300', 'L', '110.88922', '37.42932'); +INSERT INTO `yoshop_region` VALUES ('344', '337', '石楼', '石楼县', '中国,山西省,吕梁市,石楼县', '3', 'shilou', '0358', '032500', 'S', '110.8352', '36.99731'); +INSERT INTO `yoshop_region` VALUES ('345', '337', '岚县', '岚县', '中国,山西省,吕梁市,岚县', '3', 'lanxian', '0358', '033500', null, '111.67627', '38.27874'); +INSERT INTO `yoshop_region` VALUES ('346', '337', '方山', '方山县', '中国,山西省,吕梁市,方山县', '3', 'fangshan', '0358', '033100', 'F', '111.24011', '37.88979'); +INSERT INTO `yoshop_region` VALUES ('347', '337', '中阳', '中阳县', '中国,山西省,吕梁市,中阳县', '3', 'zhongyang', '0358', '033400', 'Z', '111.1795', '37.35715'); +INSERT INTO `yoshop_region` VALUES ('348', '337', '交口', '交口县', '中国,山西省,吕梁市,交口县', '3', 'jiaokou', '0358', '032400', 'J', '111.18103', '36.98213'); +INSERT INTO `yoshop_region` VALUES ('349', '337', '孝义', '孝义市', '中国,山西省,吕梁市,孝义市', '3', 'xiaoyi', '0358', '032300', 'X', '111.77362', '37.14414'); +INSERT INTO `yoshop_region` VALUES ('350', '337', '汾阳', '汾阳市', '中国,山西省,吕梁市,汾阳市', '3', 'fenyang', '0358', '032200', 'F', '111.7882', '37.26605'); +INSERT INTO `yoshop_region` VALUES ('351', '0', '内蒙古', '内蒙古自治区', '中国,内蒙古自治区', '1', 'innermongolia', '', '', 'N', '111.670801', '40.818311'); +INSERT INTO `yoshop_region` VALUES ('352', '351', '呼和浩特', '呼和浩特市', '中国,内蒙古自治区,呼和浩特市', '2', 'hohhot', '0471', '010000', 'H', '111.670801', '40.818311'); +INSERT INTO `yoshop_region` VALUES ('353', '352', '新城', '新城区', '中国,内蒙古自治区,呼和浩特市,新城区', '3', 'xincheng', '0471', '010050', 'X', '111.66554', '40.85828'); +INSERT INTO `yoshop_region` VALUES ('354', '352', '回民', '回民区', '中国,内蒙古自治区,呼和浩特市,回民区', '3', 'huimin', '0471', '010030', 'H', '111.62402', '40.80827'); +INSERT INTO `yoshop_region` VALUES ('355', '352', '玉泉', '玉泉区', '中国,内蒙古自治区,呼和浩特市,玉泉区', '3', 'yuquan', '0471', '010020', 'Y', '111.67456', '40.75227'); +INSERT INTO `yoshop_region` VALUES ('356', '352', '赛罕', '赛罕区', '中国,内蒙古自治区,呼和浩特市,赛罕区', '3', 'saihan', '0471', '010020', 'S', '111.70224', '40.79207'); +INSERT INTO `yoshop_region` VALUES ('357', '352', '土默特左旗', '土默特左旗', '中国,内蒙古自治区,呼和浩特市,土默特左旗', '3', 'tumotezuoqi', '0471', '010100', 'T', '111.14898', '40.72229'); +INSERT INTO `yoshop_region` VALUES ('358', '352', '托克托', '托克托县', '中国,内蒙古自治区,呼和浩特市,托克托县', '3', 'tuoketuo', '0471', '010200', 'T', '111.19101', '40.27492'); +INSERT INTO `yoshop_region` VALUES ('359', '352', '和林格尔', '和林格尔县', '中国,内蒙古自治区,呼和浩特市,和林格尔县', '3', 'helingeer', '0471', '011500', 'H', '111.82205', '40.37892'); +INSERT INTO `yoshop_region` VALUES ('360', '352', '清水河', '清水河县', '中国,内蒙古自治区,呼和浩特市,清水河县', '3', 'qingshuihe', '0471', '011600', 'Q', '111.68316', '39.9097'); +INSERT INTO `yoshop_region` VALUES ('361', '352', '武川', '武川县', '中国,内蒙古自治区,呼和浩特市,武川县', '3', 'wuchuan', '0471', '011700', 'W', '111.45785', '41.09289'); +INSERT INTO `yoshop_region` VALUES ('362', '351', '包头', '包头市', '中国,内蒙古自治区,包头市', '2', 'baotou', '0472', '014025', 'B', '109.840405', '40.658168'); +INSERT INTO `yoshop_region` VALUES ('363', '362', '东河', '东河区', '中国,内蒙古自治区,包头市,东河区', '3', 'donghe', '0472', '014040', 'D', '110.0462', '40.58237'); +INSERT INTO `yoshop_region` VALUES ('364', '362', '昆都仑', '昆都仑区', '中国,内蒙古自治区,包头市,昆都仑区', '3', 'kundulun', '0472', '014010', 'K', '109.83862', '40.64175'); +INSERT INTO `yoshop_region` VALUES ('365', '362', '青山', '青山区', '中国,内蒙古自治区,包头市,青山区', '3', 'qingshan', '0472', '014030', 'Q', '109.90131', '40.64329'); +INSERT INTO `yoshop_region` VALUES ('366', '362', '石拐', '石拐区', '中国,内蒙古自治区,包头市,石拐区', '3', 'shiguai', '0472', '014070', 'S', '110.27322', '40.67297'); +INSERT INTO `yoshop_region` VALUES ('367', '362', '白云鄂博矿区', '白云鄂博矿区', '中国,内蒙古自治区,包头市,白云鄂博矿区', '3', 'baiyunebokuangqu', '0472', '014080', 'B', '109.97367', '41.76968'); +INSERT INTO `yoshop_region` VALUES ('368', '362', '九原', '九原区', '中国,内蒙古自治区,包头市,九原区', '3', 'jiuyuan', '0472', '014060', 'J', '109.96496', '40.60554'); +INSERT INTO `yoshop_region` VALUES ('369', '362', '土默特右旗', '土默特右旗', '中国,内蒙古自治区,包头市,土默特右旗', '3', 'tumoteyouqi', '0472', '014100', 'T', '110.52417', '40.5688'); +INSERT INTO `yoshop_region` VALUES ('370', '362', '固阳', '固阳县', '中国,内蒙古自治区,包头市,固阳县', '3', 'guyang', '0472', '014200', 'G', '110.06372', '41.01851'); +INSERT INTO `yoshop_region` VALUES ('371', '362', '达茂旗', '达尔罕茂明安联合旗', '中国,内蒙古自治区,包头市,达尔罕茂明安联合旗', '3', 'damaoqi', '0472', '014500', 'D', '110.43258', '41.69875'); +INSERT INTO `yoshop_region` VALUES ('372', '351', '乌海', '乌海市', '中国,内蒙古自治区,乌海市', '2', 'wuhai', '0473', '016000', 'W', '106.825563', '39.673734'); +INSERT INTO `yoshop_region` VALUES ('373', '372', '海勃湾', '海勃湾区', '中国,内蒙古自治区,乌海市,海勃湾区', '3', 'haibowan', '0473', '016000', 'H', '106.8222', '39.66955'); +INSERT INTO `yoshop_region` VALUES ('374', '372', '海南', '海南区', '中国,内蒙古自治区,乌海市,海南区', '3', 'hainan', '0473', '016030', 'H', '106.88656', '39.44128'); +INSERT INTO `yoshop_region` VALUES ('375', '372', '乌达', '乌达区', '中国,内蒙古自治区,乌海市,乌达区', '3', 'wuda', '0473', '016040', 'W', '106.72723', '39.505'); +INSERT INTO `yoshop_region` VALUES ('376', '351', '赤峰', '赤峰市', '中国,内蒙古自治区,赤峰市', '2', 'chifeng', '0476', '024000', 'C', '118.956806', '42.275317'); +INSERT INTO `yoshop_region` VALUES ('377', '376', '红山', '红山区', '中国,内蒙古自治区,赤峰市,红山区', '3', 'hongshan', '0476', '024020', 'H', '118.95755', '42.24312'); +INSERT INTO `yoshop_region` VALUES ('378', '376', '元宝山', '元宝山区', '中国,内蒙古自治区,赤峰市,元宝山区', '3', 'yuanbaoshan', '0476', '024076', 'Y', '119.28921', '42.04005'); +INSERT INTO `yoshop_region` VALUES ('379', '376', '松山', '松山区', '中国,内蒙古自治区,赤峰市,松山区', '3', 'songshan', '0476', '024005', 'S', '118.9328', '42.28613'); +INSERT INTO `yoshop_region` VALUES ('380', '376', '阿鲁科尔沁旗', '阿鲁科尔沁旗', '中国,内蒙古自治区,赤峰市,阿鲁科尔沁旗', '3', 'alukeerqinqi', '0476', '025550', 'A', '120.06527', '43.87988'); +INSERT INTO `yoshop_region` VALUES ('381', '376', '巴林左旗', '巴林左旗', '中国,内蒙古自治区,赤峰市,巴林左旗', '3', 'balinzuoqi', '0476', '025450', 'B', '119.38012', '43.97031'); +INSERT INTO `yoshop_region` VALUES ('382', '376', '巴林右旗', '巴林右旗', '中国,内蒙古自治区,赤峰市,巴林右旗', '3', 'balinyouqi', '0476', '025150', 'B', '118.66461', '43.53387'); +INSERT INTO `yoshop_region` VALUES ('383', '376', '林西', '林西县', '中国,内蒙古自治区,赤峰市,林西县', '3', 'linxi', '0476', '025250', 'L', '118.04733', '43.61165'); +INSERT INTO `yoshop_region` VALUES ('384', '376', '克什克腾旗', '克什克腾旗', '中国,内蒙古自治区,赤峰市,克什克腾旗', '3', 'keshiketengqi', '0476', '025350', 'K', '117.54562', '43.26501'); +INSERT INTO `yoshop_region` VALUES ('385', '376', '翁牛特旗', '翁牛特旗', '中国,内蒙古自治区,赤峰市,翁牛特旗', '3', 'wengniuteqi', '0476', '024500', 'W', '119.03042', '42.93147'); +INSERT INTO `yoshop_region` VALUES ('386', '376', '喀喇沁旗', '喀喇沁旗', '中国,内蒙古自治区,赤峰市,喀喇沁旗', '3', 'kalaqinqi', '0476', '024400', 'K', '118.70144', '41.92917'); +INSERT INTO `yoshop_region` VALUES ('387', '376', '宁城', '宁城县', '中国,内蒙古自治区,赤峰市,宁城县', '3', 'ningcheng', '0476', '024200', 'N', '119.34375', '41.59661'); +INSERT INTO `yoshop_region` VALUES ('388', '376', '敖汉旗', '敖汉旗', '中国,内蒙古自治区,赤峰市,敖汉旗', '3', 'aohanqi', '0476', '024300', 'A', '119.92163', '42.29071'); +INSERT INTO `yoshop_region` VALUES ('389', '351', '通辽', '通辽市', '中国,内蒙古自治区,通辽市', '2', 'tongliao', '0475', '028000', 'T', '122.263119', '43.617429'); +INSERT INTO `yoshop_region` VALUES ('390', '389', '科尔沁', '科尔沁区', '中国,内蒙古自治区,通辽市,科尔沁区', '3', 'keerqin', '0475', '028000', 'K', '122.25573', '43.62257'); +INSERT INTO `yoshop_region` VALUES ('391', '389', '科尔沁左翼中旗', '科尔沁左翼中旗', '中国,内蒙古自治区,通辽市,科尔沁左翼中旗', '3', 'keerqinzuoyizhongqi', '0475', '029300', 'K', '123.31912', '44.13014'); +INSERT INTO `yoshop_region` VALUES ('392', '389', '科尔沁左翼后旗', '科尔沁左翼后旗', '中国,内蒙古自治区,通辽市,科尔沁左翼后旗', '3', 'keerqinzuoyihouqi', '0475', '028100', 'K', '122.35745', '42.94897'); +INSERT INTO `yoshop_region` VALUES ('393', '389', '开鲁', '开鲁县', '中国,内蒙古自治区,通辽市,开鲁县', '3', 'kailu', '0475', '028400', 'K', '121.31884', '43.60003'); +INSERT INTO `yoshop_region` VALUES ('394', '389', '库伦旗', '库伦旗', '中国,内蒙古自治区,通辽市,库伦旗', '3', 'kulunqi', '0475', '028200', 'K', '121.776', '42.72998'); +INSERT INTO `yoshop_region` VALUES ('395', '389', '奈曼旗', '奈曼旗', '中国,内蒙古自治区,通辽市,奈曼旗', '3', 'naimanqi', '0475', '028300', 'N', '120.66348', '42.84527'); +INSERT INTO `yoshop_region` VALUES ('396', '389', '扎鲁特旗', '扎鲁特旗', '中国,内蒙古自治区,通辽市,扎鲁特旗', '3', 'zhaluteqi', '0475', '029100', 'Z', '120.91507', '44.55592'); +INSERT INTO `yoshop_region` VALUES ('397', '389', '霍林郭勒', '霍林郭勒市', '中国,内蒙古自治区,通辽市,霍林郭勒市', '3', 'huolinguole', '0475', '029200', 'H', '119.65429', '45.53454'); +INSERT INTO `yoshop_region` VALUES ('398', '351', '鄂尔多斯', '鄂尔多斯市', '中国,内蒙古自治区,鄂尔多斯市', '2', 'ordos', '0477', '017004', 'E', '109.99029', '39.817179'); +INSERT INTO `yoshop_region` VALUES ('399', '398', '东胜', '东胜区', '中国,内蒙古自治区,鄂尔多斯市,东胜区', '3', 'dongsheng', '0477', '017000', 'D', '109.96289', '39.82236'); +INSERT INTO `yoshop_region` VALUES ('400', '398', '达拉特旗', '达拉特旗', '中国,内蒙古自治区,鄂尔多斯市,达拉特旗', '3', 'dalateqi', '0477', '014300', 'D', '110.03317', '40.4001'); +INSERT INTO `yoshop_region` VALUES ('401', '398', '准格尔旗', '准格尔旗', '中国,内蒙古自治区,鄂尔多斯市,准格尔旗', '3', 'zhungeerqi', '0477', '017100', 'Z', '111.23645', '39.86783'); +INSERT INTO `yoshop_region` VALUES ('402', '398', '鄂托克前旗', '鄂托克前旗', '中国,内蒙古自治区,鄂尔多斯市,鄂托克前旗', '3', 'etuokeqianqi', '0477', '016200', 'E', '107.48403', '38.18396'); +INSERT INTO `yoshop_region` VALUES ('403', '398', '鄂托克旗', '鄂托克旗', '中国,内蒙古自治区,鄂尔多斯市,鄂托克旗', '3', 'etuokeqi', '0477', '016100', 'E', '107.98226', '39.09456'); +INSERT INTO `yoshop_region` VALUES ('404', '398', '杭锦旗', '杭锦旗', '中国,内蒙古自治区,鄂尔多斯市,杭锦旗', '3', 'hangjinqi', '0477', '017400', 'H', '108.72934', '39.84023'); +INSERT INTO `yoshop_region` VALUES ('405', '398', '乌审旗', '乌审旗', '中国,内蒙古自治区,鄂尔多斯市,乌审旗', '3', 'wushenqi', '0477', '017300', 'W', '108.8461', '38.59092'); +INSERT INTO `yoshop_region` VALUES ('406', '398', '伊金霍洛旗', '伊金霍洛旗', '中国,内蒙古自治区,鄂尔多斯市,伊金霍洛旗', '3', 'yijinhuoluoqi', '0477', '017200', 'Y', '109.74908', '39.57393'); +INSERT INTO `yoshop_region` VALUES ('407', '351', '呼伦贝尔', '呼伦贝尔市', '中国,内蒙古自治区,呼伦贝尔市', '2', 'hulunber', '0470', '021008', 'H', '119.758168', '49.215333'); +INSERT INTO `yoshop_region` VALUES ('408', '407', '海拉尔', '海拉尔区', '中国,内蒙古自治区,呼伦贝尔市,海拉尔区', '3', 'hailaer', '0470', '021000', 'H', '119.7364', '49.2122'); +INSERT INTO `yoshop_region` VALUES ('409', '407', '扎赉诺尔', '扎赉诺尔区', '中国,内蒙古自治区,呼伦贝尔市,扎赉诺尔区', '3', 'zhalainuoer', '0470', '021410', 'Z', '117.792702', '49.486943'); +INSERT INTO `yoshop_region` VALUES ('410', '407', '阿荣旗', '阿荣旗', '中国,内蒙古自治区,呼伦贝尔市,阿荣旗', '3', 'arongqi', '0470', '162750', 'A', '123.45941', '48.12581'); +INSERT INTO `yoshop_region` VALUES ('411', '407', '莫旗', '莫力达瓦达斡尔族自治旗', '中国,内蒙古自治区,呼伦贝尔市,莫力达瓦达斡尔族自治旗', '3', 'moqi', '0470', '162850', 'M', '124.51498', '48.48055'); +INSERT INTO `yoshop_region` VALUES ('412', '407', '鄂伦春', '鄂伦春自治旗', '中国,内蒙古自治区,呼伦贝尔市,鄂伦春自治旗', '3', 'elunchun', '0470', '165450', 'E', '123.72604', '50.59777'); +INSERT INTO `yoshop_region` VALUES ('413', '407', '鄂温', '鄂温克族自治旗', '中国,内蒙古自治区,呼伦贝尔市,鄂温克族自治旗', '3', 'ewen', '0470', '021100', 'E', '119.7565', '49.14284'); +INSERT INTO `yoshop_region` VALUES ('414', '407', '陈巴尔虎旗', '陈巴尔虎旗', '中国,内蒙古自治区,呼伦贝尔市,陈巴尔虎旗', '3', 'chenbaerhuqi', '0470', '021500', 'C', '119.42434', '49.32684'); +INSERT INTO `yoshop_region` VALUES ('415', '407', '新巴尔虎左旗', '新巴尔虎左旗', '中国,内蒙古自治区,呼伦贝尔市,新巴尔虎左旗', '3', 'xinbaerhuzuoqi', '0470', '021200', 'X', '118.26989', '48.21842'); +INSERT INTO `yoshop_region` VALUES ('416', '407', '新巴尔虎右旗', '新巴尔虎右旗', '中国,内蒙古自治区,呼伦贝尔市,新巴尔虎右旗', '3', 'xinbaerhuyouqi', '0470', '021300', 'X', '116.82366', '48.66473'); +INSERT INTO `yoshop_region` VALUES ('417', '407', '满洲里', '满洲里市', '中国,内蒙古自治区,呼伦贝尔市,满洲里市', '3', 'manzhouli', '0470', '021400', 'M', '117.47946', '49.58272'); +INSERT INTO `yoshop_region` VALUES ('418', '407', '牙克石', '牙克石市', '中国,内蒙古自治区,呼伦贝尔市,牙克石市', '3', 'yakeshi', '0470', '022150', 'Y', '120.7117', '49.2856'); +INSERT INTO `yoshop_region` VALUES ('419', '407', '扎兰屯', '扎兰屯市', '中国,内蒙古自治区,呼伦贝尔市,扎兰屯市', '3', 'zhalantun', '0470', '162650', 'Z', '122.73757', '48.01363'); +INSERT INTO `yoshop_region` VALUES ('420', '407', '额尔古纳', '额尔古纳市', '中国,内蒙古自治区,呼伦贝尔市,额尔古纳市', '3', 'eerguna', '0470', '022250', 'E', '120.19094', '50.24249'); +INSERT INTO `yoshop_region` VALUES ('421', '407', '根河', '根河市', '中国,内蒙古自治区,呼伦贝尔市,根河市', '3', 'genhe', '0470', '022350', 'G', '121.52197', '50.77996'); +INSERT INTO `yoshop_region` VALUES ('422', '351', '巴彦淖尔', '巴彦淖尔市', '中国,内蒙古自治区,巴彦淖尔市', '2', 'bayannur', '0478', '015001', 'B', '107.416959', '40.757402'); +INSERT INTO `yoshop_region` VALUES ('423', '422', '临河', '临河区', '中国,内蒙古自治区,巴彦淖尔市,临河区', '3', 'linhe', '0478', '015001', 'L', '107.42668', '40.75827'); +INSERT INTO `yoshop_region` VALUES ('424', '422', '五原', '五原县', '中国,内蒙古自治区,巴彦淖尔市,五原县', '3', 'wuyuan', '0478', '015100', 'W', '108.26916', '41.09631'); +INSERT INTO `yoshop_region` VALUES ('425', '422', '磴口', '磴口县', '中国,内蒙古自治区,巴彦淖尔市,磴口县', '3', 'dengkou', '0478', '015200', null, '107.00936', '40.33062'); +INSERT INTO `yoshop_region` VALUES ('426', '422', '乌拉特前旗', '乌拉特前旗', '中国,内蒙古自治区,巴彦淖尔市,乌拉特前旗', '3', 'wulateqianqi', '0478', '014400', 'W', '108.65219', '40.73649'); +INSERT INTO `yoshop_region` VALUES ('427', '422', '乌拉特中旗', '乌拉特中旗', '中国,内蒙古自治区,巴彦淖尔市,乌拉特中旗', '3', 'wulatezhongqi', '0478', '015300', 'W', '108.52587', '41.56789'); +INSERT INTO `yoshop_region` VALUES ('428', '422', '乌拉特后旗', '乌拉特后旗', '中国,内蒙古自治区,巴彦淖尔市,乌拉特后旗', '3', 'wulatehouqi', '0478', '015500', 'W', '106.98971', '41.43151'); +INSERT INTO `yoshop_region` VALUES ('429', '422', '杭锦后旗', '杭锦后旗', '中国,内蒙古自治区,巴彦淖尔市,杭锦后旗', '3', 'hangjinhouqi', '0478', '015400', 'H', '107.15133', '40.88627'); +INSERT INTO `yoshop_region` VALUES ('430', '351', '乌兰察布', '乌兰察布市', '中国,内蒙古自治区,乌兰察布市', '2', 'ulanqab', '0474', '012000', 'W', '113.114543', '41.034126'); +INSERT INTO `yoshop_region` VALUES ('431', '430', '集宁', '集宁区', '中国,内蒙古自治区,乌兰察布市,集宁区', '3', 'jining', '0474', '012000', 'J', '113.11452', '41.0353'); +INSERT INTO `yoshop_region` VALUES ('432', '430', '卓资', '卓资县', '中国,内蒙古自治区,乌兰察布市,卓资县', '3', 'zhuozi', '0474', '012300', 'Z', '112.57757', '40.89414'); +INSERT INTO `yoshop_region` VALUES ('433', '430', '化德', '化德县', '中国,内蒙古自治区,乌兰察布市,化德县', '3', 'huade', '0474', '013350', 'H', '114.01071', '41.90433'); +INSERT INTO `yoshop_region` VALUES ('434', '430', '商都', '商都县', '中国,内蒙古自治区,乌兰察布市,商都县', '3', 'shangdu', '0474', '013450', 'S', '113.57772', '41.56213'); +INSERT INTO `yoshop_region` VALUES ('435', '430', '兴和', '兴和县', '中国,内蒙古自治区,乌兰察布市,兴和县', '3', 'xinghe', '0474', '013650', 'X', '113.83395', '40.87186'); +INSERT INTO `yoshop_region` VALUES ('436', '430', '凉城', '凉城县', '中国,内蒙古自治区,乌兰察布市,凉城县', '3', 'liangcheng', '0474', '013750', 'L', '112.49569', '40.53346'); +INSERT INTO `yoshop_region` VALUES ('437', '430', '察右前旗', '察哈尔右翼前旗', '中国,内蒙古自治区,乌兰察布市,察哈尔右翼前旗', '3', 'chayouqianqi', '0474', '012200', 'C', '113.22131', '40.7788'); +INSERT INTO `yoshop_region` VALUES ('438', '430', '察右中旗', '察哈尔右翼中旗', '中国,内蒙古自治区,乌兰察布市,察哈尔右翼中旗', '3', 'chayouzhongqi', '0474', '013550', 'C', '112.63537', '41.27742'); +INSERT INTO `yoshop_region` VALUES ('439', '430', '察右后旗', '察哈尔右翼后旗', '中国,内蒙古自治区,乌兰察布市,察哈尔右翼后旗', '3', 'chayouhouqi', '0474', '012400', 'C', '113.19216', '41.43554'); +INSERT INTO `yoshop_region` VALUES ('440', '430', '四子王旗', '四子王旗', '中国,内蒙古自治区,乌兰察布市,四子王旗', '3', 'siziwangqi', '0474', '011800', 'S', '111.70654', '41.53312'); +INSERT INTO `yoshop_region` VALUES ('441', '430', '丰镇', '丰镇市', '中国,内蒙古自治区,乌兰察布市,丰镇市', '3', 'fengzhen', '0474', '012100', 'F', '113.10983', '40.4369'); +INSERT INTO `yoshop_region` VALUES ('442', '351', '兴安盟', '兴安盟', '中国,内蒙古自治区,兴安盟', '2', 'hinggan', '0482', '137401', 'X', '122.070317', '46.076268'); +INSERT INTO `yoshop_region` VALUES ('443', '442', '乌兰浩特', '乌兰浩特市', '中国,内蒙古自治区,兴安盟,乌兰浩特市', '3', 'wulanhaote', '0482', '137401', 'W', '122.06378', '46.06235'); +INSERT INTO `yoshop_region` VALUES ('444', '442', '阿尔山', '阿尔山市', '中国,内蒙古自治区,兴安盟,阿尔山市', '3', 'aershan', '0482', '137800', 'A', '119.94317', '47.17716'); +INSERT INTO `yoshop_region` VALUES ('445', '442', '科右前旗', '科尔沁右翼前旗', '中国,内蒙古自治区,兴安盟,科尔沁右翼前旗', '3', 'keyouqianqi', '0482', '137423', 'K', '121.95269', '46.0795'); +INSERT INTO `yoshop_region` VALUES ('446', '442', '科右中旗', '科尔沁右翼中旗', '中国,内蒙古自治区,兴安盟,科尔沁右翼中旗', '3', 'keyouzhongqi', '0482', '029400', 'K', '121.46807', '45.05605'); +INSERT INTO `yoshop_region` VALUES ('447', '442', '扎赉特旗', '扎赉特旗', '中国,内蒙古自治区,兴安盟,扎赉特旗', '3', 'zhalaiteqi', '0482', '137600', 'Z', '122.91229', '46.7267'); +INSERT INTO `yoshop_region` VALUES ('448', '442', '突泉', '突泉县', '中国,内蒙古自治区,兴安盟,突泉县', '3', 'tuquan', '0482', '137500', 'T', '121.59396', '45.38187'); +INSERT INTO `yoshop_region` VALUES ('449', '351', '锡林郭勒盟', '锡林郭勒盟', '中国,内蒙古自治区,锡林郭勒盟', '2', 'xilingol', '0479', '026000', 'X', '116.090996', '43.944018'); +INSERT INTO `yoshop_region` VALUES ('450', '449', '二连浩特', '二连浩特市', '中国,内蒙古自治区,锡林郭勒盟,二连浩特市', '3', 'erlianhaote', '0479', '011100', 'E', '111.98297', '43.65303'); +INSERT INTO `yoshop_region` VALUES ('451', '449', '锡林浩特', '锡林浩特市', '中国,内蒙古自治区,锡林郭勒盟,锡林浩特市', '3', 'xilinhaote', '0479', '026021', 'X', '116.08603', '43.93341'); +INSERT INTO `yoshop_region` VALUES ('452', '449', '阿巴嘎旗', '阿巴嘎旗', '中国,内蒙古自治区,锡林郭勒盟,阿巴嘎旗', '3', 'abagaqi', '0479', '011400', 'A', '114.96826', '44.02174'); +INSERT INTO `yoshop_region` VALUES ('453', '449', '苏尼特左旗', '苏尼特左旗', '中国,内蒙古自治区,锡林郭勒盟,苏尼特左旗', '3', 'sunitezuoqi', '0479', '011300', 'S', '113.6506', '43.85687'); +INSERT INTO `yoshop_region` VALUES ('454', '449', '苏尼特右旗', '苏尼特右旗', '中国,内蒙古自治区,锡林郭勒盟,苏尼特右旗', '3', 'suniteyouqi', '0479', '011200', 'S', '112.65741', '42.7469'); +INSERT INTO `yoshop_region` VALUES ('455', '449', '东乌旗', '东乌珠穆沁旗', '中国,内蒙古自治区,锡林郭勒盟,东乌珠穆沁旗', '3', 'dongwuqi', '0479', '026300', 'D', '116.97293', '45.51108'); +INSERT INTO `yoshop_region` VALUES ('456', '449', '西乌旗', '西乌珠穆沁旗', '中国,内蒙古自治区,锡林郭勒盟,西乌珠穆沁旗', '3', 'xiwuqi', '0479', '026200', 'X', '117.60983', '44.59623'); +INSERT INTO `yoshop_region` VALUES ('457', '449', '太仆寺旗', '太仆寺旗', '中国,内蒙古自治区,锡林郭勒盟,太仆寺旗', '3', 'taipusiqi', '0479', '027000', 'T', '115.28302', '41.87727'); +INSERT INTO `yoshop_region` VALUES ('458', '449', '镶黄旗', '镶黄旗', '中国,内蒙古自治区,锡林郭勒盟,镶黄旗', '3', 'xianghuangqi', '0479', '013250', 'X', '113.84472', '42.23927'); +INSERT INTO `yoshop_region` VALUES ('459', '449', '正镶白旗', '正镶白旗', '中国,内蒙古自治区,锡林郭勒盟,正镶白旗', '3', 'zhengxiangbaiqi', '0479', '013800', 'Z', '115.00067', '42.30712'); +INSERT INTO `yoshop_region` VALUES ('460', '449', '正蓝旗', '正蓝旗', '中国,内蒙古自治区,锡林郭勒盟,正蓝旗', '3', 'zhenglanqi', '0479', '027200', 'Z', '116.00363', '42.25229'); +INSERT INTO `yoshop_region` VALUES ('461', '449', '多伦', '多伦县', '中国,内蒙古自治区,锡林郭勒盟,多伦县', '3', 'duolun', '0479', '027300', 'D', '116.48565', '42.203'); +INSERT INTO `yoshop_region` VALUES ('462', '351', '阿拉善盟', '阿拉善盟', '中国,内蒙古自治区,阿拉善盟', '2', 'alxa', '0483', '750306', 'A', '105.706422', '38.844814'); +INSERT INTO `yoshop_region` VALUES ('463', '462', '阿拉善左旗', '阿拉善左旗', '中国,内蒙古自治区,阿拉善盟,阿拉善左旗', '3', 'alashanzuoqi', '0483', '750306', 'A', '105.67532', '38.8293'); +INSERT INTO `yoshop_region` VALUES ('464', '462', '阿拉善右旗', '阿拉善右旗', '中国,内蒙古自治区,阿拉善盟,阿拉善右旗', '3', 'alashanyouqi', '0483', '737300', 'A', '101.66705', '39.21533'); +INSERT INTO `yoshop_region` VALUES ('465', '462', '额济纳旗', '额济纳旗', '中国,内蒙古自治区,阿拉善盟,额济纳旗', '3', 'ejinaqi', '0483', '735400', 'E', '101.06887', '41.96755'); +INSERT INTO `yoshop_region` VALUES ('466', '0', '辽宁', '辽宁省', '中国,辽宁省', '1', 'liaoning', '', '', 'L', '123.429096', '41.796767'); +INSERT INTO `yoshop_region` VALUES ('467', '466', '沈阳', '沈阳市', '中国,辽宁省,沈阳市', '2', 'shenyang', '024', '110013', 'S', '123.429096', '41.796767'); +INSERT INTO `yoshop_region` VALUES ('468', '467', '和平', '和平区', '中国,辽宁省,沈阳市,和平区', '3', 'heping', '024', '110001', 'H', '123.4204', '41.78997'); +INSERT INTO `yoshop_region` VALUES ('469', '467', '沈河', '沈河区', '中国,辽宁省,沈阳市,沈河区', '3', 'shenhe', '024', '110011', 'S', '123.45871', '41.79625'); +INSERT INTO `yoshop_region` VALUES ('470', '467', '大东', '大东区', '中国,辽宁省,沈阳市,大东区', '3', 'dadong', '024', '110041', 'D', '123.46997', '41.80539'); +INSERT INTO `yoshop_region` VALUES ('471', '467', '皇姑', '皇姑区', '中国,辽宁省,沈阳市,皇姑区', '3', 'huanggu', '024', '110031', 'H', '123.42527', '41.82035'); +INSERT INTO `yoshop_region` VALUES ('472', '467', '铁西', '铁西区', '中国,辽宁省,沈阳市,铁西区', '3', 'tiexi', '024', '110021', 'T', '123.37675', '41.80269'); +INSERT INTO `yoshop_region` VALUES ('473', '467', '苏家屯', '苏家屯区', '中国,辽宁省,沈阳市,苏家屯区', '3', 'sujiatun', '024', '110101', 'S', '123.34405', '41.66475'); +INSERT INTO `yoshop_region` VALUES ('474', '467', '浑南', '浑南区', '中国,辽宁省,沈阳市,浑南区', '3', 'hunnan', '024', '110015', 'H', '123.457707', '41.719450'); +INSERT INTO `yoshop_region` VALUES ('475', '467', '沈北新区', '沈北新区', '中国,辽宁省,沈阳市,沈北新区', '3', 'shenbeixinqu', '024', '110121', 'S', '123.52658', '42.05297'); +INSERT INTO `yoshop_region` VALUES ('476', '467', '于洪', '于洪区', '中国,辽宁省,沈阳市,于洪区', '3', 'yuhong', '024', '110141', 'Y', '123.30807', '41.794'); +INSERT INTO `yoshop_region` VALUES ('477', '467', '辽中', '辽中县', '中国,辽宁省,沈阳市,辽中县', '3', 'liaozhong', '024', '110200', 'L', '122.72659', '41.51302'); +INSERT INTO `yoshop_region` VALUES ('478', '467', '康平', '康平县', '中国,辽宁省,沈阳市,康平县', '3', 'kangping', '024', '110500', 'K', '123.35446', '42.75081'); +INSERT INTO `yoshop_region` VALUES ('479', '467', '法库', '法库县', '中国,辽宁省,沈阳市,法库县', '3', 'faku', '024', '110400', 'F', '123.41214', '42.50608'); +INSERT INTO `yoshop_region` VALUES ('480', '467', '新民', '新民市', '中国,辽宁省,沈阳市,新民市', '3', 'xinmin', '024', '110300', 'X', '122.82867', '41.99847'); +INSERT INTO `yoshop_region` VALUES ('481', '466', '大连', '大连市', '中国,辽宁省,大连市', '2', 'dalian', '0411', '116011', 'D', '121.618622', '38.91459'); +INSERT INTO `yoshop_region` VALUES ('482', '481', '中山', '中山区', '中国,辽宁省,大连市,中山区', '3', 'zhongshan', '0411', '116001', 'Z', '121.64465', '38.91859'); +INSERT INTO `yoshop_region` VALUES ('483', '481', '西岗', '西岗区', '中国,辽宁省,大连市,西岗区', '3', 'xigang', '0411', '116011', 'X', '121.61238', '38.91469'); +INSERT INTO `yoshop_region` VALUES ('484', '481', '沙河口', '沙河口区', '中国,辽宁省,大连市,沙河口区', '3', 'shahekou', '0411', '116021', 'S', '121.58017', '38.90536'); +INSERT INTO `yoshop_region` VALUES ('485', '481', '甘井子', '甘井子区', '中国,辽宁省,大连市,甘井子区', '3', 'ganjingzi', '0411', '116033', 'G', '121.56567', '38.95017'); +INSERT INTO `yoshop_region` VALUES ('486', '481', '旅顺口', '旅顺口区', '中国,辽宁省,大连市,旅顺口区', '3', 'lvshunkou', '0411', '116041', 'L', '121.26202', '38.85125'); +INSERT INTO `yoshop_region` VALUES ('487', '481', '金州', '金州区', '中国,辽宁省,大连市,金州区', '3', 'jinzhou', '0411', '116100', 'J', '121.71893', '39.1004'); +INSERT INTO `yoshop_region` VALUES ('488', '481', '长海', '长海县', '中国,辽宁省,大连市,长海县', '3', 'changhai', '0411', '116500', 'C', '122.58859', '39.27274'); +INSERT INTO `yoshop_region` VALUES ('489', '481', '瓦房店', '瓦房店市', '中国,辽宁省,大连市,瓦房店市', '3', 'wafangdian', '0411', '116300', 'W', '121.98104', '39.62843'); +INSERT INTO `yoshop_region` VALUES ('490', '481', '普兰店', '普兰店市', '中国,辽宁省,大连市,普兰店市', '3', 'pulandian', '0411', '116200', 'P', '121.96316', '39.39465'); +INSERT INTO `yoshop_region` VALUES ('491', '481', '庄河', '庄河市', '中国,辽宁省,大连市,庄河市', '3', 'zhuanghe', '0411', '116400', 'Z', '122.96725', '39.68815'); +INSERT INTO `yoshop_region` VALUES ('492', '466', '鞍山', '鞍山市', '中国,辽宁省,鞍山市', '2', 'anshan', '0412', '114001', 'A', '122.995632', '41.110626'); +INSERT INTO `yoshop_region` VALUES ('493', '492', '铁东', '铁东区', '中国,辽宁省,鞍山市,铁东区', '3', 'tiedong', '0412', '114001', 'T', '122.99085', '41.08975'); +INSERT INTO `yoshop_region` VALUES ('494', '492', '铁西', '铁西区', '中国,辽宁省,鞍山市,铁西区', '3', 'tiexi', '0413', '114013', 'T', '122.96967', '41.11977'); +INSERT INTO `yoshop_region` VALUES ('495', '492', '立山', '立山区', '中国,辽宁省,鞍山市,立山区', '3', 'lishan', '0414', '114031', 'L', '123.02948', '41.15008'); +INSERT INTO `yoshop_region` VALUES ('496', '492', '千山', '千山区', '中国,辽宁省,鞍山市,千山区', '3', 'qianshan', '0415', '114041', 'Q', '122.96048', '41.07507'); +INSERT INTO `yoshop_region` VALUES ('497', '492', '台安', '台安县', '中国,辽宁省,鞍山市,台安县', '3', 'tai\'an', '0417', '114100', 'T', '122.43585', '41.41265'); +INSERT INTO `yoshop_region` VALUES ('498', '492', '岫岩', '岫岩满族自治县', '中国,辽宁省,鞍山市,岫岩满族自治县', '3', 'xiuyan', '0418', '114300', null, '123.28875', '40.27996'); +INSERT INTO `yoshop_region` VALUES ('499', '492', '海城', '海城市', '中国,辽宁省,鞍山市,海城市', '3', 'haicheng', '0416', '114200', 'H', '122.68457', '40.88142'); +INSERT INTO `yoshop_region` VALUES ('500', '466', '抚顺', '抚顺市', '中国,辽宁省,抚顺市', '2', 'fushun', '024', '113008', 'F', '123.921109', '41.875956'); +INSERT INTO `yoshop_region` VALUES ('501', '500', '新抚', '新抚区', '中国,辽宁省,抚顺市,新抚区', '3', 'xinfu', '024', '113008', 'X', '123.91264', '41.86205'); +INSERT INTO `yoshop_region` VALUES ('502', '500', '东洲', '东洲区', '中国,辽宁省,抚顺市,东洲区', '3', 'dongzhou', '024', '113003', 'D', '124.03759', '41.8519'); +INSERT INTO `yoshop_region` VALUES ('503', '500', '望花', '望花区', '中国,辽宁省,抚顺市,望花区', '3', 'wanghua', '024', '113001', 'W', '123.78283', '41.85532'); +INSERT INTO `yoshop_region` VALUES ('504', '500', '顺城', '顺城区', '中国,辽宁省,抚顺市,顺城区', '3', 'shuncheng', '024', '113006', 'S', '123.94506', '41.88321'); +INSERT INTO `yoshop_region` VALUES ('505', '500', '抚顺', '抚顺县', '中国,辽宁省,抚顺市,抚顺县', '3', 'fushun', '024', '113006', 'F', '124.17755', '41.71217'); +INSERT INTO `yoshop_region` VALUES ('506', '500', '新宾', '新宾满族自治县', '中国,辽宁省,抚顺市,新宾满族自治县', '3', 'xinbin', '024', '113200', 'X', '125.04049', '41.73409'); +INSERT INTO `yoshop_region` VALUES ('507', '500', '清原', '清原满族自治县', '中国,辽宁省,抚顺市,清原满族自治县', '3', 'qingyuan', '024', '113300', 'Q', '124.92807', '42.10221'); +INSERT INTO `yoshop_region` VALUES ('508', '466', '本溪', '本溪市', '中国,辽宁省,本溪市', '2', 'benxi', '0414', '117000', 'B', '123.770519', '41.297909'); +INSERT INTO `yoshop_region` VALUES ('509', '508', '平山', '平山区', '中国,辽宁省,本溪市,平山区', '3', 'pingshan', '0414', '117000', 'P', '123.76892', '41.2997'); +INSERT INTO `yoshop_region` VALUES ('510', '508', '溪湖', '溪湖区', '中国,辽宁省,本溪市,溪湖区', '3', 'xihu', '0414', '117002', 'X', '123.76764', '41.32921'); +INSERT INTO `yoshop_region` VALUES ('511', '508', '明山', '明山区', '中国,辽宁省,本溪市,明山区', '3', 'mingshan', '0414', '117021', 'M', '123.81746', '41.30827'); +INSERT INTO `yoshop_region` VALUES ('512', '508', '南芬', '南芬区', '中国,辽宁省,本溪市,南芬区', '3', 'nanfen', '0414', '117014', 'N', '123.74523', '41.1006'); +INSERT INTO `yoshop_region` VALUES ('513', '508', '本溪', '本溪满族自治县', '中国,辽宁省,本溪市,本溪满族自治县', '3', 'benxi', '0414', '117100', 'B', '124.12741', '41.30059'); +INSERT INTO `yoshop_region` VALUES ('514', '508', '桓仁', '桓仁满族自治县', '中国,辽宁省,本溪市,桓仁满族自治县', '3', 'huanren', '0414', '117200', 'H', '125.36062', '41.26798'); +INSERT INTO `yoshop_region` VALUES ('515', '466', '丹东', '丹东市', '中国,辽宁省,丹东市', '2', 'dandong', '0415', '118000', 'D', '124.383044', '40.124296'); +INSERT INTO `yoshop_region` VALUES ('516', '515', '元宝', '元宝区', '中国,辽宁省,丹东市,元宝区', '3', 'yuanbao', '0415', '118000', 'Y', '124.39575', '40.13651'); +INSERT INTO `yoshop_region` VALUES ('517', '515', '振兴', '振兴区', '中国,辽宁省,丹东市,振兴区', '3', 'zhenxing', '0415', '118002', 'Z', '124.36035', '40.10489'); +INSERT INTO `yoshop_region` VALUES ('518', '515', '振安', '振安区', '中国,辽宁省,丹东市,振安区', '3', 'zhen\'an', '0415', '118001', 'Z', '124.42816', '40.15826'); +INSERT INTO `yoshop_region` VALUES ('519', '515', '宽甸', '宽甸满族自治县', '中国,辽宁省,丹东市,宽甸满族自治县', '3', 'kuandian', '0415', '118200', 'K', '124.78247', '40.73187'); +INSERT INTO `yoshop_region` VALUES ('520', '515', '东港', '东港市', '中国,辽宁省,丹东市,东港市', '3', 'donggang', '0415', '118300', 'D', '124.16287', '39.86256'); +INSERT INTO `yoshop_region` VALUES ('521', '515', '凤城', '凤城市', '中国,辽宁省,丹东市,凤城市', '3', 'fengcheng', '0415', '118100', 'F', '124.06671', '40.45302'); +INSERT INTO `yoshop_region` VALUES ('522', '466', '锦州', '锦州市', '中国,辽宁省,锦州市', '2', 'jinzhou', '0416', '121000', 'J', '121.135742', '41.119269'); +INSERT INTO `yoshop_region` VALUES ('523', '522', '古塔', '古塔区', '中国,辽宁省,锦州市,古塔区', '3', 'guta', '0416', '121001', 'G', '121.12832', '41.11725'); +INSERT INTO `yoshop_region` VALUES ('524', '522', '凌河', '凌河区', '中国,辽宁省,锦州市,凌河区', '3', 'linghe', '0416', '121000', 'L', '121.15089', '41.11496'); +INSERT INTO `yoshop_region` VALUES ('525', '522', '太和', '太和区', '中国,辽宁省,锦州市,太和区', '3', 'taihe', '0416', '121011', 'T', '121.10354', '41.10929'); +INSERT INTO `yoshop_region` VALUES ('526', '522', '黑山', '黑山县', '中国,辽宁省,锦州市,黑山县', '3', 'heishan', '0416', '121400', 'H', '122.12081', '41.69417'); +INSERT INTO `yoshop_region` VALUES ('527', '522', '义县', '义县', '中国,辽宁省,锦州市,义县', '3', 'yixian', '0416', '121100', 'Y', '121.24035', '41.53458'); +INSERT INTO `yoshop_region` VALUES ('528', '522', '凌海', '凌海市', '中国,辽宁省,锦州市,凌海市', '3', 'linghai', '0416', '121200', 'L', '121.35705', '41.1737'); +INSERT INTO `yoshop_region` VALUES ('529', '522', '北镇', '北镇市', '中国,辽宁省,锦州市,北镇市', '3', 'beizhen', '0416', '121300', 'B', '121.79858', '41.59537'); +INSERT INTO `yoshop_region` VALUES ('530', '466', '营口', '营口市', '中国,辽宁省,营口市', '2', 'yingkou', '0417', '115003', 'Y', '122.235151', '40.667432'); +INSERT INTO `yoshop_region` VALUES ('531', '530', '站前', '站前区', '中国,辽宁省,营口市,站前区', '3', 'zhanqian', '0417', '115002', 'Z', '122.25896', '40.67266'); +INSERT INTO `yoshop_region` VALUES ('532', '530', '西市', '西市区', '中国,辽宁省,营口市,西市区', '3', 'xishi', '0417', '115004', 'X', '122.20641', '40.6664'); +INSERT INTO `yoshop_region` VALUES ('533', '530', '鲅鱼圈', '鲅鱼圈区', '中国,辽宁省,营口市,鲅鱼圈区', '3', 'bayuquan', '0417', '115007', null, '122.13266', '40.26865'); +INSERT INTO `yoshop_region` VALUES ('534', '530', '老边', '老边区', '中国,辽宁省,营口市,老边区', '3', 'laobian', '0417', '115005', 'L', '122.37996', '40.6803'); +INSERT INTO `yoshop_region` VALUES ('535', '530', '盖州', '盖州市', '中国,辽宁省,营口市,盖州市', '3', 'gaizhou', '0417', '115200', 'G', '122.35464', '40.40446'); +INSERT INTO `yoshop_region` VALUES ('536', '530', '大石桥', '大石桥市', '中国,辽宁省,营口市,大石桥市', '3', 'dashiqiao', '0417', '115100', 'D', '122.50927', '40.64567'); +INSERT INTO `yoshop_region` VALUES ('537', '466', '阜新', '阜新市', '中国,辽宁省,阜新市', '2', 'fuxin', '0418', '123000', 'F', '121.648962', '42.011796'); +INSERT INTO `yoshop_region` VALUES ('538', '537', '海州', '海州区', '中国,辽宁省,阜新市,海州区', '3', 'haizhou', '0418', '123000', 'H', '121.65626', '42.01336'); +INSERT INTO `yoshop_region` VALUES ('539', '537', '新邱', '新邱区', '中国,辽宁省,阜新市,新邱区', '3', 'xinqiu', '0418', '123005', 'X', '121.79251', '42.09181'); +INSERT INTO `yoshop_region` VALUES ('540', '537', '太平', '太平区', '中国,辽宁省,阜新市,太平区', '3', 'taiping', '0418', '123003', 'T', '121.67865', '42.01065'); +INSERT INTO `yoshop_region` VALUES ('541', '537', '清河门', '清河门区', '中国,辽宁省,阜新市,清河门区', '3', 'qinghemen', '0418', '123006', 'Q', '121.4161', '41.78309'); +INSERT INTO `yoshop_region` VALUES ('542', '537', '细河', '细河区', '中国,辽宁省,阜新市,细河区', '3', 'xihe', '0418', '123000', 'X', '121.68013', '42.02533'); +INSERT INTO `yoshop_region` VALUES ('543', '537', '阜新', '阜新蒙古族自治县', '中国,辽宁省,阜新市,阜新蒙古族自治县', '3', 'fuxin', '0418', '123100', 'F', '121.75787', '42.0651'); +INSERT INTO `yoshop_region` VALUES ('544', '537', '彰武', '彰武县', '中国,辽宁省,阜新市,彰武县', '3', 'zhangwu', '0418', '123200', 'Z', '122.54022', '42.38625'); +INSERT INTO `yoshop_region` VALUES ('545', '466', '辽阳', '辽阳市', '中国,辽宁省,辽阳市', '2', 'liaoyang', '0419', '111000', 'L', '123.18152', '41.269402'); +INSERT INTO `yoshop_region` VALUES ('546', '545', '白塔', '白塔区', '中国,辽宁省,辽阳市,白塔区', '3', 'baita', '0419', '111000', 'B', '123.1747', '41.27025'); +INSERT INTO `yoshop_region` VALUES ('547', '545', '文圣', '文圣区', '中国,辽宁省,辽阳市,文圣区', '3', 'wensheng', '0419', '111000', 'W', '123.18521', '41.26267'); +INSERT INTO `yoshop_region` VALUES ('548', '545', '宏伟', '宏伟区', '中国,辽宁省,辽阳市,宏伟区', '3', 'hongwei', '0419', '111003', 'H', '123.1929', '41.21852'); +INSERT INTO `yoshop_region` VALUES ('549', '545', '弓长岭', '弓长岭区', '中国,辽宁省,辽阳市,弓长岭区', '3', 'gongchangling', '0419', '111008', 'G', '123.41963', '41.15181'); +INSERT INTO `yoshop_region` VALUES ('550', '545', '太子河', '太子河区', '中国,辽宁省,辽阳市,太子河区', '3', 'taizihe', '0419', '111000', 'T', '123.18182', '41.25337'); +INSERT INTO `yoshop_region` VALUES ('551', '545', '辽阳', '辽阳县', '中国,辽宁省,辽阳市,辽阳县', '3', 'liaoyang', '0419', '111200', 'L', '123.10574', '41.20542'); +INSERT INTO `yoshop_region` VALUES ('552', '545', '灯塔', '灯塔市', '中国,辽宁省,辽阳市,灯塔市', '3', 'dengta', '0419', '111300', 'D', '123.33926', '41.42612'); +INSERT INTO `yoshop_region` VALUES ('553', '466', '盘锦', '盘锦市', '中国,辽宁省,盘锦市', '2', 'panjin', '0427', '124010', 'P', '122.06957', '41.124484'); +INSERT INTO `yoshop_region` VALUES ('554', '553', '双台子', '双台子区', '中国,辽宁省,盘锦市,双台子区', '3', 'shuangtaizi', '0427', '124000', 'S', '122.06011', '41.1906'); +INSERT INTO `yoshop_region` VALUES ('555', '553', '兴隆台', '兴隆台区', '中国,辽宁省,盘锦市,兴隆台区', '3', 'xinglongtai', '0427', '124010', 'X', '122.07529', '41.12402'); +INSERT INTO `yoshop_region` VALUES ('556', '553', '大洼', '大洼县', '中国,辽宁省,盘锦市,大洼县', '3', 'dawa', '0427', '124200', 'D', '122.08239', '41.00244'); +INSERT INTO `yoshop_region` VALUES ('557', '553', '盘山', '盘山县', '中国,辽宁省,盘锦市,盘山县', '3', 'panshan', '0427', '124000', 'P', '121.99777', '41.23805'); +INSERT INTO `yoshop_region` VALUES ('558', '466', '铁岭', '铁岭市', '中国,辽宁省,铁岭市', '2', 'tieling', '024', '112000', 'T', '123.844279', '42.290585'); +INSERT INTO `yoshop_region` VALUES ('559', '558', '银州', '银州区', '中国,辽宁省,铁岭市,银州区', '3', 'yinzhou', '024', '112000', 'Y', '123.8573', '42.29507'); +INSERT INTO `yoshop_region` VALUES ('560', '558', '清河', '清河区', '中国,辽宁省,铁岭市,清河区', '3', 'qinghe', '024', '112003', 'Q', '124.15911', '42.54679'); +INSERT INTO `yoshop_region` VALUES ('561', '558', '铁岭', '铁岭县', '中国,辽宁省,铁岭市,铁岭县', '3', 'tieling', '024', '112000', 'T', '123.77325', '42.22498'); +INSERT INTO `yoshop_region` VALUES ('562', '558', '西丰', '西丰县', '中国,辽宁省,铁岭市,西丰县', '3', 'xifeng', '024', '112400', 'X', '124.7304', '42.73756'); +INSERT INTO `yoshop_region` VALUES ('563', '558', '昌图', '昌图县', '中国,辽宁省,铁岭市,昌图县', '3', 'changtu', '024', '112500', 'C', '124.11206', '42.78428'); +INSERT INTO `yoshop_region` VALUES ('564', '558', '调兵山', '调兵山市', '中国,辽宁省,铁岭市,调兵山市', '3', 'diaobingshan', '024', '112700', 'D', '123.56689', '42.4675'); +INSERT INTO `yoshop_region` VALUES ('565', '558', '开原', '开原市', '中国,辽宁省,铁岭市,开原市', '3', 'kaiyuan', '024', '112300', 'K', '124.03945', '42.54585'); +INSERT INTO `yoshop_region` VALUES ('566', '466', '朝阳', '朝阳市', '中国,辽宁省,朝阳市', '2', 'chaoyang', '0421', '122000', 'C', '120.451176', '41.576758'); +INSERT INTO `yoshop_region` VALUES ('567', '566', '双塔', '双塔区', '中国,辽宁省,朝阳市,双塔区', '3', 'shuangta', '0421', '122000', 'S', '120.45385', '41.566'); +INSERT INTO `yoshop_region` VALUES ('568', '566', '龙城', '龙城区', '中国,辽宁省,朝阳市,龙城区', '3', 'longcheng', '0421', '122000', 'L', '120.43719', '41.59264'); +INSERT INTO `yoshop_region` VALUES ('569', '566', '朝阳', '朝阳县', '中国,辽宁省,朝阳市,朝阳县', '3', 'chaoyang', '0421', '122000', 'C', '120.17401', '41.4324'); +INSERT INTO `yoshop_region` VALUES ('570', '566', '建平', '建平县', '中国,辽宁省,朝阳市,建平县', '3', 'jianping', '0421', '122400', 'J', '119.64392', '41.40315'); +INSERT INTO `yoshop_region` VALUES ('571', '566', '喀喇沁左翼', '喀喇沁左翼蒙古族自治县', '中国,辽宁省,朝阳市,喀喇沁左翼蒙古族自治县', '3', 'kalaqinzuoyi', '0421', '122300', 'K', '119.74185', '41.12801'); +INSERT INTO `yoshop_region` VALUES ('572', '566', '北票', '北票市', '中国,辽宁省,朝阳市,北票市', '3', 'beipiao', '0421', '122100', 'B', '120.76977', '41.80196'); +INSERT INTO `yoshop_region` VALUES ('573', '566', '凌源', '凌源市', '中国,辽宁省,朝阳市,凌源市', '3', 'lingyuan', '0421', '122500', 'L', '119.40148', '41.24558'); +INSERT INTO `yoshop_region` VALUES ('574', '466', '葫芦岛', '葫芦岛市', '中国,辽宁省,葫芦岛市', '2', 'huludao', '0429', '125000', 'H', '120.856394', '40.755572'); +INSERT INTO `yoshop_region` VALUES ('575', '574', '连山', '连山区', '中国,辽宁省,葫芦岛市,连山区', '3', 'lianshan', '0429', '125001', 'L', '120.86393', '40.75554'); +INSERT INTO `yoshop_region` VALUES ('576', '574', '龙港', '龙港区', '中国,辽宁省,葫芦岛市,龙港区', '3', 'longgang', '0429', '125003', 'L', '120.94866', '40.71919'); +INSERT INTO `yoshop_region` VALUES ('577', '574', '南票', '南票区', '中国,辽宁省,葫芦岛市,南票区', '3', 'nanpiao', '0429', '125027', 'N', '120.74978', '41.10707'); +INSERT INTO `yoshop_region` VALUES ('578', '574', '绥中', '绥中县', '中国,辽宁省,葫芦岛市,绥中县', '3', 'suizhong', '0429', '125200', 'S', '120.34451', '40.32552'); +INSERT INTO `yoshop_region` VALUES ('579', '574', '建昌', '建昌县', '中国,辽宁省,葫芦岛市,建昌县', '3', 'jianchang', '0429', '125300', 'J', '119.8377', '40.82448'); +INSERT INTO `yoshop_region` VALUES ('580', '574', '兴城', '兴城市', '中国,辽宁省,葫芦岛市,兴城市', '3', 'xingcheng', '0429', '125100', 'X', '120.72537', '40.61492'); +INSERT INTO `yoshop_region` VALUES ('581', '466', '金普新区', '金普新区', '中国,辽宁省,金普新区', '2', 'jinpuxinqu', '0411', '116100', 'J', '121.789627', '39.055451'); +INSERT INTO `yoshop_region` VALUES ('582', '581', '金州新区', '金州新区', '中国,辽宁省,金普新区,金州新区', '3', 'jinzhouxinqu', '0411', '116100', 'J', '121.784821', '39.052252'); +INSERT INTO `yoshop_region` VALUES ('583', '581', '普湾新区', '普湾新区', '中国,辽宁省,金普新区,普湾新区', '3', 'puwanxinqu', '0411', '116200', 'P', '121.812812', '39.330093'); +INSERT INTO `yoshop_region` VALUES ('584', '581', '保税区', '保税区', '中国,辽宁省,金普新区,保税区', '3', 'baoshuiqu', '0411', '116100', 'B', '121.94289', '39.224614'); +INSERT INTO `yoshop_region` VALUES ('585', '0', '吉林', '吉林省', '中国,吉林省', '1', 'jilin', '', '', 'J', '125.3245', '43.886841'); +INSERT INTO `yoshop_region` VALUES ('586', '585', '长春', '长春市', '中国,吉林省,长春市', '2', 'changchun', '0431', '130022', 'C', '125.3245', '43.886841'); +INSERT INTO `yoshop_region` VALUES ('587', '586', '南关', '南关区', '中国,吉林省,长春市,南关区', '3', 'nanguan', '0431', '130022', 'N', '125.35035', '43.86401'); +INSERT INTO `yoshop_region` VALUES ('588', '586', '宽城', '宽城区', '中国,吉林省,长春市,宽城区', '3', 'kuancheng', '0431', '130051', 'K', '125.32635', '43.90182'); +INSERT INTO `yoshop_region` VALUES ('589', '586', '朝阳', '朝阳区', '中国,吉林省,长春市,朝阳区', '3', 'chaoyang', '0431', '130012', 'C', '125.2883', '43.83339'); +INSERT INTO `yoshop_region` VALUES ('590', '586', '二道', '二道区', '中国,吉林省,长春市,二道区', '3', 'erdao', '0431', '130031', 'E', '125.37429', '43.86501'); +INSERT INTO `yoshop_region` VALUES ('591', '586', '绿园', '绿园区', '中国,吉林省,长春市,绿园区', '3', 'lvyuan', '0431', '130062', 'L', '125.25582', '43.88045'); +INSERT INTO `yoshop_region` VALUES ('592', '586', '双阳', '双阳区', '中国,吉林省,长春市,双阳区', '3', 'shuangyang', '0431', '130600', 'S', '125.65631', '43.52803'); +INSERT INTO `yoshop_region` VALUES ('593', '586', '九台', '九台区', '中国,吉林省,长春市,九台区', '3', 'jiutai', '0431', '130500', 'J', '125.8395', '44.15163'); +INSERT INTO `yoshop_region` VALUES ('594', '586', '农安', '农安县', '中国,吉林省,长春市,农安县', '3', 'nong\'an', '0431', '130200', 'N', '125.18481', '44.43265'); +INSERT INTO `yoshop_region` VALUES ('595', '586', '榆树', '榆树市', '中国,吉林省,长春市,榆树市', '3', 'yushu', '0431', '130400', 'Y', '126.55688', '44.82523'); +INSERT INTO `yoshop_region` VALUES ('596', '586', '德惠', '德惠市', '中国,吉林省,长春市,德惠市', '3', 'dehui', '0431', '130300', 'D', '125.70538', '44.53719'); +INSERT INTO `yoshop_region` VALUES ('597', '585', '吉林', '吉林市', '中国,吉林省,吉林市', '2', 'jilin', '0432', '132011', 'J', '126.55302', '43.843577'); +INSERT INTO `yoshop_region` VALUES ('598', '597', '昌邑', '昌邑区', '中国,吉林省,吉林市,昌邑区', '3', 'changyi', '0432', '132002', 'C', '126.57424', '43.88183'); +INSERT INTO `yoshop_region` VALUES ('599', '597', '龙潭', '龙潭区', '中国,吉林省,吉林市,龙潭区', '3', 'longtan', '0432', '132021', 'L', '126.56213', '43.91054'); +INSERT INTO `yoshop_region` VALUES ('600', '597', '船营', '船营区', '中国,吉林省,吉林市,船营区', '3', 'chuanying', '0432', '132011', 'C', '126.54096', '43.83344'); +INSERT INTO `yoshop_region` VALUES ('601', '597', '丰满', '丰满区', '中国,吉林省,吉林市,丰满区', '3', 'fengman', '0432', '132013', 'F', '126.56237', '43.82236'); +INSERT INTO `yoshop_region` VALUES ('602', '597', '永吉', '永吉县', '中国,吉林省,吉林市,永吉县', '3', 'yongji', '0432', '132200', 'Y', '126.4963', '43.67197'); +INSERT INTO `yoshop_region` VALUES ('603', '597', '蛟河', '蛟河市', '中国,吉林省,吉林市,蛟河市', '3', 'jiaohe', '0432', '132500', null, '127.34426', '43.72696'); +INSERT INTO `yoshop_region` VALUES ('604', '597', '桦甸', '桦甸市', '中国,吉林省,吉林市,桦甸市', '3', 'huadian', '0432', '132400', null, '126.74624', '42.97206'); +INSERT INTO `yoshop_region` VALUES ('605', '597', '舒兰', '舒兰市', '中国,吉林省,吉林市,舒兰市', '3', 'shulan', '0432', '132600', 'S', '126.9653', '44.40582'); +INSERT INTO `yoshop_region` VALUES ('606', '597', '磐石', '磐石市', '中国,吉林省,吉林市,磐石市', '3', 'panshi', '0432', '132300', 'P', '126.0625', '42.94628'); +INSERT INTO `yoshop_region` VALUES ('607', '585', '四平', '四平市', '中国,吉林省,四平市', '2', 'siping', '0434', '136000', 'S', '124.370785', '43.170344'); +INSERT INTO `yoshop_region` VALUES ('608', '607', '铁西', '铁西区', '中国,吉林省,四平市,铁西区', '3', 'tiexi', '0434', '136000', 'T', '124.37369', '43.17456'); +INSERT INTO `yoshop_region` VALUES ('609', '607', '铁东', '铁东区', '中国,吉林省,四平市,铁东区', '3', 'tiedong', '0434', '136001', 'T', '124.40976', '43.16241'); +INSERT INTO `yoshop_region` VALUES ('610', '607', '梨树', '梨树县', '中国,吉林省,四平市,梨树县', '3', 'lishu', '0434', '136500', 'L', '124.33563', '43.30717'); +INSERT INTO `yoshop_region` VALUES ('611', '607', '伊通', '伊通满族自治县', '中国,吉林省,四平市,伊通满族自治县', '3', 'yitong', '0434', '130700', 'Y', '125.30596', '43.34434'); +INSERT INTO `yoshop_region` VALUES ('612', '607', '公主岭', '公主岭市', '中国,吉林省,四平市,公主岭市', '3', 'gongzhuling', '0434', '136100', 'G', '124.82266', '43.50453'); +INSERT INTO `yoshop_region` VALUES ('613', '607', '双辽', '双辽市', '中国,吉林省,四平市,双辽市', '3', 'shuangliao', '0434', '136400', 'S', '123.50106', '43.52099'); +INSERT INTO `yoshop_region` VALUES ('614', '585', '辽源', '辽源市', '中国,吉林省,辽源市', '2', 'liaoyuan', '0437', '136200', 'L', '125.145349', '42.902692'); +INSERT INTO `yoshop_region` VALUES ('615', '614', '龙山', '龙山区', '中国,吉林省,辽源市,龙山区', '3', 'longshan', '0437', '136200', 'L', '125.13641', '42.89714'); +INSERT INTO `yoshop_region` VALUES ('616', '614', '西安', '西安区', '中国,吉林省,辽源市,西安区', '3', 'xi\'an', '0437', '136201', 'X', '125.14904', '42.927'); +INSERT INTO `yoshop_region` VALUES ('617', '614', '东丰', '东丰县', '中国,吉林省,辽源市,东丰县', '3', 'dongfeng', '0437', '136300', 'D', '125.53244', '42.6783'); +INSERT INTO `yoshop_region` VALUES ('618', '614', '东辽', '东辽县', '中国,吉林省,辽源市,东辽县', '3', 'dongliao', '0437', '136600', 'D', '124.98596', '42.92492'); +INSERT INTO `yoshop_region` VALUES ('619', '585', '通化', '通化市', '中国,吉林省,通化市', '2', 'tonghua', '0435', '134001', 'T', '125.936501', '41.721177'); +INSERT INTO `yoshop_region` VALUES ('620', '619', '东昌', '东昌区', '中国,吉林省,通化市,东昌区', '3', 'dongchang', '0435', '134001', 'D', '125.9551', '41.72849'); +INSERT INTO `yoshop_region` VALUES ('621', '619', '二道江', '二道江区', '中国,吉林省,通化市,二道江区', '3', 'erdaojiang', '0435', '134003', 'E', '126.04257', '41.7741'); +INSERT INTO `yoshop_region` VALUES ('622', '619', '通化', '通化县', '中国,吉林省,通化市,通化县', '3', 'tonghua', '0435', '134100', 'T', '125.75936', '41.67928'); +INSERT INTO `yoshop_region` VALUES ('623', '619', '辉南', '辉南县', '中国,吉林省,通化市,辉南县', '3', 'huinan', '0435', '135100', 'H', '126.04684', '42.68497'); +INSERT INTO `yoshop_region` VALUES ('624', '619', '柳河', '柳河县', '中国,吉林省,通化市,柳河县', '3', 'liuhe', '0435', '135300', 'L', '125.74475', '42.28468'); +INSERT INTO `yoshop_region` VALUES ('625', '619', '梅河口', '梅河口市', '中国,吉林省,通化市,梅河口市', '3', 'meihekou', '0435', '135000', 'M', '125.71041', '42.53828'); +INSERT INTO `yoshop_region` VALUES ('626', '619', '集安', '集安市', '中国,吉林省,通化市,集安市', '3', 'ji\'an', '0435', '134200', 'J', '126.18829', '41.12268'); +INSERT INTO `yoshop_region` VALUES ('627', '585', '白山', '白山市', '中国,吉林省,白山市', '2', 'baishan', '0439', '134300', 'B', '126.427839', '41.942505'); +INSERT INTO `yoshop_region` VALUES ('628', '627', '浑江', '浑江区', '中国,吉林省,白山市,浑江区', '3', 'hunjiang', '0439', '134300', 'H', '126.422342', '41.945656'); +INSERT INTO `yoshop_region` VALUES ('629', '627', '江源', '江源区', '中国,吉林省,白山市,江源区', '3', 'jiangyuan', '0439', '134700', 'J', '126.59079', '42.05664'); +INSERT INTO `yoshop_region` VALUES ('630', '627', '抚松', '抚松县', '中国,吉林省,白山市,抚松县', '3', 'fusong', '0439', '134500', 'F', '127.2803', '42.34198'); +INSERT INTO `yoshop_region` VALUES ('631', '627', '靖宇', '靖宇县', '中国,吉林省,白山市,靖宇县', '3', 'jingyu', '0439', '135200', 'J', '126.81308', '42.38863'); +INSERT INTO `yoshop_region` VALUES ('632', '627', '长白', '长白朝鲜族自治县', '中国,吉林省,白山市,长白朝鲜族自治县', '3', 'changbai', '0439', '134400', 'C', '128.20047', '41.41996'); +INSERT INTO `yoshop_region` VALUES ('633', '627', '临江', '临江市', '中国,吉林省,白山市,临江市', '3', 'linjiang', '0439', '134600', 'L', '126.91751', '41.81142'); +INSERT INTO `yoshop_region` VALUES ('634', '585', '松原', '松原市', '中国,吉林省,松原市', '2', 'songyuan', '0438', '138000', 'S', '124.823608', '45.118243'); +INSERT INTO `yoshop_region` VALUES ('635', '634', '宁江', '宁江区', '中国,吉林省,松原市,宁江区', '3', 'ningjiang', '0438', '138000', 'N', '124.81689', '45.17175'); +INSERT INTO `yoshop_region` VALUES ('636', '634', '前郭尔罗斯', '前郭尔罗斯蒙古族自治县', '中国,吉林省,松原市,前郭尔罗斯蒙古族自治县', '3', 'qianguoerluosi', '0438', '138000', 'Q', '124.82351', '45.11726'); +INSERT INTO `yoshop_region` VALUES ('637', '634', '长岭', '长岭县', '中国,吉林省,松原市,长岭县', '3', 'changling', '0438', '131500', 'C', '123.96725', '44.27581'); +INSERT INTO `yoshop_region` VALUES ('638', '634', '乾安', '乾安县', '中国,吉林省,松原市,乾安县', '3', 'qian\'an', '0438', '131400', 'Q', '124.02737', '45.01068'); +INSERT INTO `yoshop_region` VALUES ('639', '634', '扶余', '扶余市', '中国,吉林省,松原市,扶余市', '3', 'fuyu', '0438', '131200', 'F', '126.042758', '44.986199'); +INSERT INTO `yoshop_region` VALUES ('640', '585', '白城', '白城市', '中国,吉林省,白城市', '2', 'baicheng', '0436', '137000', 'B', '122.841114', '45.619026'); +INSERT INTO `yoshop_region` VALUES ('641', '640', '洮北', '洮北区', '中国,吉林省,白城市,洮北区', '3', 'taobei', '0436', '137000', null, '122.85104', '45.62167'); +INSERT INTO `yoshop_region` VALUES ('642', '640', '镇赉', '镇赉县', '中国,吉林省,白城市,镇赉县', '3', 'zhenlai', '0436', '137300', 'Z', '123.19924', '45.84779'); +INSERT INTO `yoshop_region` VALUES ('643', '640', '通榆', '通榆县', '中国,吉林省,白城市,通榆县', '3', 'tongyu', '0436', '137200', 'T', '123.08761', '44.81388'); +INSERT INTO `yoshop_region` VALUES ('644', '640', '洮南', '洮南市', '中国,吉林省,白城市,洮南市', '3', 'taonan', '0436', '137100', null, '122.78772', '45.33502'); +INSERT INTO `yoshop_region` VALUES ('645', '640', '大安', '大安市', '中国,吉林省,白城市,大安市', '3', 'da\'an', '0436', '131300', 'D', '124.29519', '45.50846'); +INSERT INTO `yoshop_region` VALUES ('646', '585', '延边', '延边朝鲜族自治州', '中国,吉林省,延边朝鲜族自治州', '2', 'yanbian', '0433', '133000', 'Y', '129.513228', '42.904823'); +INSERT INTO `yoshop_region` VALUES ('647', '646', '延吉', '延吉市', '中国,吉林省,延边朝鲜族自治州,延吉市', '3', 'yanji', '0433', '133000', 'Y', '129.51357', '42.90682'); +INSERT INTO `yoshop_region` VALUES ('648', '646', '图们', '图们市', '中国,吉林省,延边朝鲜族自治州,图们市', '3', 'tumen', '0433', '133100', 'T', '129.84381', '42.96801'); +INSERT INTO `yoshop_region` VALUES ('649', '646', '敦化', '敦化市', '中国,吉林省,延边朝鲜族自治州,敦化市', '3', 'dunhua', '0433', '133700', 'D', '128.23242', '43.37304'); +INSERT INTO `yoshop_region` VALUES ('650', '646', '珲春', '珲春市', '中国,吉林省,延边朝鲜族自治州,珲春市', '3', 'hunchun', '0433', '133300', null, '130.36572', '42.86242'); +INSERT INTO `yoshop_region` VALUES ('651', '646', '龙井', '龙井市', '中国,吉林省,延边朝鲜族自治州,龙井市', '3', 'longjing', '0433', '133400', 'L', '129.42584', '42.76804'); +INSERT INTO `yoshop_region` VALUES ('652', '646', '和龙', '和龙市', '中国,吉林省,延边朝鲜族自治州,和龙市', '3', 'helong', '0433', '133500', 'H', '129.01077', '42.5464'); +INSERT INTO `yoshop_region` VALUES ('653', '646', '汪清', '汪清县', '中国,吉林省,延边朝鲜族自治州,汪清县', '3', 'wangqing', '0433', '133200', 'W', '129.77121', '43.31278'); +INSERT INTO `yoshop_region` VALUES ('654', '646', '安图', '安图县', '中国,吉林省,延边朝鲜族自治州,安图县', '3', 'antu', '0433', '133600', 'A', '128.90625', '43.11533'); +INSERT INTO `yoshop_region` VALUES ('655', '0', '黑龙江', '黑龙江省', '中国,黑龙江省', '1', 'heilongjiang', '', '', 'H', '126.642464', '45.756967'); +INSERT INTO `yoshop_region` VALUES ('656', '655', '哈尔滨', '哈尔滨市', '中国,黑龙江省,哈尔滨市', '2', 'harbin', '0451', '150010', 'H', '126.642464', '45.756967'); +INSERT INTO `yoshop_region` VALUES ('657', '656', '道里', '道里区', '中国,黑龙江省,哈尔滨市,道里区', '3', 'daoli', '0451', '150010', 'D', '126.61705', '45.75586'); +INSERT INTO `yoshop_region` VALUES ('658', '656', '南岗', '南岗区', '中国,黑龙江省,哈尔滨市,南岗区', '3', 'nangang', '0451', '150006', 'N', '126.66854', '45.75996'); +INSERT INTO `yoshop_region` VALUES ('659', '656', '道外', '道外区', '中国,黑龙江省,哈尔滨市,道外区', '3', 'daowai', '0451', '150020', 'D', '126.64938', '45.79187'); +INSERT INTO `yoshop_region` VALUES ('660', '656', '平房', '平房区', '中国,黑龙江省,哈尔滨市,平房区', '3', 'pingfang', '0451', '150060', 'P', '126.63729', '45.59777'); +INSERT INTO `yoshop_region` VALUES ('661', '656', '松北', '松北区', '中国,黑龙江省,哈尔滨市,松北区', '3', 'songbei', '0451', '150028', 'S', '126.56276', '45.80831'); +INSERT INTO `yoshop_region` VALUES ('662', '656', '香坊', '香坊区', '中国,黑龙江省,哈尔滨市,香坊区', '3', 'xiangfang', '0451', '150036', 'X', '126.67968', '45.72383'); +INSERT INTO `yoshop_region` VALUES ('663', '656', '呼兰', '呼兰区', '中国,黑龙江省,哈尔滨市,呼兰区', '3', 'hulan', '0451', '150500', 'H', '126.58792', '45.88895'); +INSERT INTO `yoshop_region` VALUES ('664', '656', '阿城', '阿城区', '中国,黑龙江省,哈尔滨市,阿城区', '3', 'a\'cheng', '0451', '150300', 'A', '126.97525', '45.54144'); +INSERT INTO `yoshop_region` VALUES ('665', '656', '双城', '双城区', '中国,黑龙江省,哈尔滨市,双城区', '3', 'shuangcheng', '0451', '150100', 'S', '126.308784', '45.377942'); +INSERT INTO `yoshop_region` VALUES ('666', '656', '依兰', '依兰县', '中国,黑龙江省,哈尔滨市,依兰县', '3', 'yilan', '0451', '154800', 'Y', '129.56817', '46.3247'); +INSERT INTO `yoshop_region` VALUES ('667', '656', '方正', '方正县', '中国,黑龙江省,哈尔滨市,方正县', '3', 'fangzheng', '0451', '150800', 'F', '128.82952', '45.85162'); +INSERT INTO `yoshop_region` VALUES ('668', '656', '宾县', '宾县', '中国,黑龙江省,哈尔滨市,宾县', '3', 'binxian', '0451', '150400', 'B', '127.48675', '45.75504'); +INSERT INTO `yoshop_region` VALUES ('669', '656', '巴彦', '巴彦县', '中国,黑龙江省,哈尔滨市,巴彦县', '3', 'bayan', '0451', '151800', 'B', '127.40799', '46.08148'); +INSERT INTO `yoshop_region` VALUES ('670', '656', '木兰', '木兰县', '中国,黑龙江省,哈尔滨市,木兰县', '3', 'mulan', '0451', '151900', 'M', '128.0448', '45.94944'); +INSERT INTO `yoshop_region` VALUES ('671', '656', '通河', '通河县', '中国,黑龙江省,哈尔滨市,通河县', '3', 'tonghe', '0451', '150900', 'T', '128.74603', '45.99007'); +INSERT INTO `yoshop_region` VALUES ('672', '656', '延寿', '延寿县', '中国,黑龙江省,哈尔滨市,延寿县', '3', 'yanshou', '0451', '150700', 'Y', '128.33419', '45.4554'); +INSERT INTO `yoshop_region` VALUES ('673', '656', '尚志', '尚志市', '中国,黑龙江省,哈尔滨市,尚志市', '3', 'shangzhi', '0451', '150600', 'S', '127.96191', '45.21736'); +INSERT INTO `yoshop_region` VALUES ('674', '656', '五常', '五常市', '中国,黑龙江省,哈尔滨市,五常市', '3', 'wuchang', '0451', '150200', 'W', '127.16751', '44.93184'); +INSERT INTO `yoshop_region` VALUES ('675', '655', '齐齐哈尔', '齐齐哈尔市', '中国,黑龙江省,齐齐哈尔市', '2', 'qiqihar', '0452', '161005', 'Q', '123.953486', '47.348079'); +INSERT INTO `yoshop_region` VALUES ('676', '675', '龙沙', '龙沙区', '中国,黑龙江省,齐齐哈尔市,龙沙区', '3', 'longsha', '0452', '161000', 'L', '123.95752', '47.31776'); +INSERT INTO `yoshop_region` VALUES ('677', '675', '建华', '建华区', '中国,黑龙江省,齐齐哈尔市,建华区', '3', 'jianhua', '0452', '161006', 'J', '124.0133', '47.36718'); +INSERT INTO `yoshop_region` VALUES ('678', '675', '铁锋', '铁锋区', '中国,黑龙江省,齐齐哈尔市,铁锋区', '3', 'tiefeng', '0452', '161000', 'T', '123.97821', '47.34075'); +INSERT INTO `yoshop_region` VALUES ('679', '675', '昂昂溪', '昂昂溪区', '中国,黑龙江省,齐齐哈尔市,昂昂溪区', '3', 'angangxi', '0452', '161031', 'A', '123.82229', '47.15513'); +INSERT INTO `yoshop_region` VALUES ('680', '675', '富拉尔基', '富拉尔基区', '中国,黑龙江省,齐齐哈尔市,富拉尔基区', '3', 'fulaerji', '0452', '161041', 'F', '123.62918', '47.20884'); +INSERT INTO `yoshop_region` VALUES ('681', '675', '碾子山', '碾子山区', '中国,黑龙江省,齐齐哈尔市,碾子山区', '3', 'nianzishan', '0452', '161046', 'N', '122.88183', '47.51662'); +INSERT INTO `yoshop_region` VALUES ('682', '675', '梅里斯', '梅里斯达斡尔族区', '中国,黑龙江省,齐齐哈尔市,梅里斯达斡尔族区', '3', 'meilisi', '0452', '161021', 'M', '123.75274', '47.30946'); +INSERT INTO `yoshop_region` VALUES ('683', '675', '龙江', '龙江县', '中国,黑龙江省,齐齐哈尔市,龙江县', '3', 'longjiang', '0452', '161100', 'L', '123.20532', '47.33868'); +INSERT INTO `yoshop_region` VALUES ('684', '675', '依安', '依安县', '中国,黑龙江省,齐齐哈尔市,依安县', '3', 'yi\'an', '0452', '161500', 'Y', '125.30896', '47.8931'); +INSERT INTO `yoshop_region` VALUES ('685', '675', '泰来', '泰来县', '中国,黑龙江省,齐齐哈尔市,泰来县', '3', 'tailai', '0452', '162400', 'T', '123.42285', '46.39386'); +INSERT INTO `yoshop_region` VALUES ('686', '675', '甘南', '甘南县', '中国,黑龙江省,齐齐哈尔市,甘南县', '3', 'gannan', '0452', '162100', 'G', '123.50317', '47.92437'); +INSERT INTO `yoshop_region` VALUES ('687', '675', '富裕', '富裕县', '中国,黑龙江省,齐齐哈尔市,富裕县', '3', 'fuyu', '0452', '161200', 'F', '124.47457', '47.77431'); +INSERT INTO `yoshop_region` VALUES ('688', '675', '克山', '克山县', '中国,黑龙江省,齐齐哈尔市,克山县', '3', 'keshan', '0452', '161600', 'K', '125.87396', '48.03265'); +INSERT INTO `yoshop_region` VALUES ('689', '675', '克东', '克东县', '中国,黑龙江省,齐齐哈尔市,克东县', '3', 'kedong', '0452', '164800', 'K', '126.24917', '48.03828'); +INSERT INTO `yoshop_region` VALUES ('690', '675', '拜泉', '拜泉县', '中国,黑龙江省,齐齐哈尔市,拜泉县', '3', 'baiquan', '0452', '164700', 'B', '126.09167', '47.60817'); +INSERT INTO `yoshop_region` VALUES ('691', '675', '讷河', '讷河市', '中国,黑龙江省,齐齐哈尔市,讷河市', '3', 'nehe', '0452', '161300', null, '124.87713', '48.48388'); +INSERT INTO `yoshop_region` VALUES ('692', '655', '鸡西', '鸡西市', '中国,黑龙江省,鸡西市', '2', 'jixi', '0467', '158100', 'J', '130.975966', '45.300046'); +INSERT INTO `yoshop_region` VALUES ('693', '692', '鸡冠', '鸡冠区', '中国,黑龙江省,鸡西市,鸡冠区', '3', 'jiguan', '0467', '158100', 'J', '130.98139', '45.30396'); +INSERT INTO `yoshop_region` VALUES ('694', '692', '恒山', '恒山区', '中国,黑龙江省,鸡西市,恒山区', '3', 'hengshan', '0467', '158130', 'H', '130.90493', '45.21071'); +INSERT INTO `yoshop_region` VALUES ('695', '692', '滴道', '滴道区', '中国,黑龙江省,鸡西市,滴道区', '3', 'didao', '0467', '158150', 'D', '130.84841', '45.35109'); +INSERT INTO `yoshop_region` VALUES ('696', '692', '梨树', '梨树区', '中国,黑龙江省,鸡西市,梨树区', '3', 'lishu', '0467', '158160', 'L', '130.69848', '45.09037'); +INSERT INTO `yoshop_region` VALUES ('697', '692', '城子河', '城子河区', '中国,黑龙江省,鸡西市,城子河区', '3', 'chengzihe', '0467', '158170', 'C', '131.01132', '45.33689'); +INSERT INTO `yoshop_region` VALUES ('698', '692', '麻山', '麻山区', '中国,黑龙江省,鸡西市,麻山区', '3', 'mashan', '0467', '158180', 'M', '130.47811', '45.21209'); +INSERT INTO `yoshop_region` VALUES ('699', '692', '鸡东', '鸡东县', '中国,黑龙江省,鸡西市,鸡东县', '3', 'jidong', '0467', '158200', 'J', '131.12423', '45.26025'); +INSERT INTO `yoshop_region` VALUES ('700', '692', '虎林', '虎林市', '中国,黑龙江省,鸡西市,虎林市', '3', 'hulin', '0467', '158400', 'H', '132.93679', '45.76291'); +INSERT INTO `yoshop_region` VALUES ('701', '692', '密山', '密山市', '中国,黑龙江省,鸡西市,密山市', '3', 'mishan', '0467', '158300', 'M', '131.84625', '45.5297'); +INSERT INTO `yoshop_region` VALUES ('702', '655', '鹤岗', '鹤岗市', '中国,黑龙江省,鹤岗市', '2', 'hegang', '0468', '154100', 'H', '130.277487', '47.332085'); +INSERT INTO `yoshop_region` VALUES ('703', '702', '向阳', '向阳区', '中国,黑龙江省,鹤岗市,向阳区', '3', 'xiangyang', '0468', '154100', 'X', '130.2943', '47.34247'); +INSERT INTO `yoshop_region` VALUES ('704', '702', '工农', '工农区', '中国,黑龙江省,鹤岗市,工农区', '3', 'gongnong', '0468', '154101', 'G', '130.27468', '47.31869'); +INSERT INTO `yoshop_region` VALUES ('705', '702', '南山', '南山区', '中国,黑龙江省,鹤岗市,南山区', '3', 'nanshan', '0468', '154104', 'N', '130.27676', '47.31404'); +INSERT INTO `yoshop_region` VALUES ('706', '702', '兴安', '兴安区', '中国,黑龙江省,鹤岗市,兴安区', '3', 'xing\'an', '0468', '154102', 'X', '130.23965', '47.2526'); +INSERT INTO `yoshop_region` VALUES ('707', '702', '东山', '东山区', '中国,黑龙江省,鹤岗市,东山区', '3', 'dongshan', '0468', '154106', 'D', '130.31706', '47.33853'); +INSERT INTO `yoshop_region` VALUES ('708', '702', '兴山', '兴山区', '中国,黑龙江省,鹤岗市,兴山区', '3', 'xingshan', '0468', '154105', 'X', '130.29271', '47.35776'); +INSERT INTO `yoshop_region` VALUES ('709', '702', '萝北', '萝北县', '中国,黑龙江省,鹤岗市,萝北县', '3', 'luobei', '0468', '154200', 'L', '130.83346', '47.57959'); +INSERT INTO `yoshop_region` VALUES ('710', '702', '绥滨', '绥滨县', '中国,黑龙江省,鹤岗市,绥滨县', '3', 'suibin', '0468', '156200', 'S', '131.86029', '47.2903'); +INSERT INTO `yoshop_region` VALUES ('711', '655', '双鸭山', '双鸭山市', '中国,黑龙江省,双鸭山市', '2', 'shuangyashan', '0469', '155100', 'S', '131.157304', '46.643442'); +INSERT INTO `yoshop_region` VALUES ('712', '711', '尖山', '尖山区', '中国,黑龙江省,双鸭山市,尖山区', '3', 'jianshan', '0469', '155100', 'J', '131.15841', '46.64635'); +INSERT INTO `yoshop_region` VALUES ('713', '711', '岭东', '岭东区', '中国,黑龙江省,双鸭山市,岭东区', '3', 'lingdong', '0469', '155120', 'L', '131.16473', '46.59043'); +INSERT INTO `yoshop_region` VALUES ('714', '711', '四方台', '四方台区', '中国,黑龙江省,双鸭山市,四方台区', '3', 'sifangtai', '0469', '155130', 'S', '131.33593', '46.59499'); +INSERT INTO `yoshop_region` VALUES ('715', '711', '宝山', '宝山区', '中国,黑龙江省,双鸭山市,宝山区', '3', 'baoshan', '0469', '155131', 'B', '131.4016', '46.57718'); +INSERT INTO `yoshop_region` VALUES ('716', '711', '集贤', '集贤县', '中国,黑龙江省,双鸭山市,集贤县', '3', 'jixian', '0469', '155900', 'J', '131.14053', '46.72678'); +INSERT INTO `yoshop_region` VALUES ('717', '711', '友谊', '友谊县', '中国,黑龙江省,双鸭山市,友谊县', '3', 'youyi', '0469', '155800', 'Y', '131.80789', '46.76739'); +INSERT INTO `yoshop_region` VALUES ('718', '711', '宝清', '宝清县', '中国,黑龙江省,双鸭山市,宝清县', '3', 'baoqing', '0469', '155600', 'B', '132.19695', '46.32716'); +INSERT INTO `yoshop_region` VALUES ('719', '711', '饶河', '饶河县', '中国,黑龙江省,双鸭山市,饶河县', '3', 'raohe', '0469', '155700', 'R', '134.01986', '46.79899'); +INSERT INTO `yoshop_region` VALUES ('720', '655', '大庆', '大庆市', '中国,黑龙江省,大庆市', '2', 'daqing', '0459', '163000', 'D', '125.11272', '46.590734'); +INSERT INTO `yoshop_region` VALUES ('721', '720', '萨尔图', '萨尔图区', '中国,黑龙江省,大庆市,萨尔图区', '3', 'saertu', '0459', '163001', 'S', '125.08792', '46.59359'); +INSERT INTO `yoshop_region` VALUES ('722', '720', '龙凤', '龙凤区', '中国,黑龙江省,大庆市,龙凤区', '3', 'longfeng', '0459', '163711', 'L', '125.11657', '46.53273'); +INSERT INTO `yoshop_region` VALUES ('723', '720', '让胡路', '让胡路区', '中国,黑龙江省,大庆市,让胡路区', '3', 'ranghulu', '0459', '163712', 'R', '124.87075', '46.6522'); +INSERT INTO `yoshop_region` VALUES ('724', '720', '红岗', '红岗区', '中国,黑龙江省,大庆市,红岗区', '3', 'honggang', '0459', '163511', 'H', '124.89248', '46.40128'); +INSERT INTO `yoshop_region` VALUES ('725', '720', '大同', '大同区', '中国,黑龙江省,大庆市,大同区', '3', 'datong', '0459', '163515', 'D', '124.81591', '46.03295'); +INSERT INTO `yoshop_region` VALUES ('726', '720', '肇州', '肇州县', '中国,黑龙江省,大庆市,肇州县', '3', 'zhaozhou', '0459', '166400', 'Z', '125.27059', '45.70414'); +INSERT INTO `yoshop_region` VALUES ('727', '720', '肇源', '肇源县', '中国,黑龙江省,大庆市,肇源县', '3', 'zhaoyuan', '0459', '166500', 'Z', '125.08456', '45.52032'); +INSERT INTO `yoshop_region` VALUES ('728', '720', '林甸', '林甸县', '中国,黑龙江省,大庆市,林甸县', '3', 'lindian', '0459', '166300', 'L', '124.87564', '47.18601'); +INSERT INTO `yoshop_region` VALUES ('729', '720', '杜尔伯特', '杜尔伯特蒙古族自治县', '中国,黑龙江省,大庆市,杜尔伯特蒙古族自治县', '3', 'duerbote', '0459', '166200', 'D', '124.44937', '46.86507'); +INSERT INTO `yoshop_region` VALUES ('730', '655', '伊春', '伊春市', '中国,黑龙江省,伊春市', '2', 'yichun', '0458', '153000', 'Y', '128.899396', '47.724775'); +INSERT INTO `yoshop_region` VALUES ('731', '730', '伊春', '伊春区', '中国,黑龙江省,伊春市,伊春区', '3', 'yichun', '0458', '153000', 'Y', '128.90752', '47.728'); +INSERT INTO `yoshop_region` VALUES ('732', '730', '南岔', '南岔区', '中国,黑龙江省,伊春市,南岔区', '3', 'nancha', '0458', '153100', 'N', '129.28362', '47.13897'); +INSERT INTO `yoshop_region` VALUES ('733', '730', '友好', '友好区', '中国,黑龙江省,伊春市,友好区', '3', 'youhao', '0458', '153031', 'Y', '128.84039', '47.85371'); +INSERT INTO `yoshop_region` VALUES ('734', '730', '西林', '西林区', '中国,黑龙江省,伊春市,西林区', '3', 'xilin', '0458', '153025', 'X', '129.31201', '47.48103'); +INSERT INTO `yoshop_region` VALUES ('735', '730', '翠峦', '翠峦区', '中国,黑龙江省,伊春市,翠峦区', '3', 'cuiluan', '0458', '153013', 'C', '128.66729', '47.72503'); +INSERT INTO `yoshop_region` VALUES ('736', '730', '新青', '新青区', '中国,黑龙江省,伊春市,新青区', '3', 'xinqing', '0458', '153036', 'X', '129.53653', '48.29067'); +INSERT INTO `yoshop_region` VALUES ('737', '730', '美溪', '美溪区', '中国,黑龙江省,伊春市,美溪区', '3', 'meixi', '0458', '153021', 'M', '129.13708', '47.63513'); +INSERT INTO `yoshop_region` VALUES ('738', '730', '金山屯', '金山屯区', '中国,黑龙江省,伊春市,金山屯区', '3', 'jinshantun', '0458', '153026', 'J', '129.43768', '47.41349'); +INSERT INTO `yoshop_region` VALUES ('739', '730', '五营', '五营区', '中国,黑龙江省,伊春市,五营区', '3', 'wuying', '0458', '153033', 'W', '129.24545', '48.10791'); +INSERT INTO `yoshop_region` VALUES ('740', '730', '乌马河', '乌马河区', '中国,黑龙江省,伊春市,乌马河区', '3', 'wumahe', '0458', '153011', 'W', '128.79672', '47.728'); +INSERT INTO `yoshop_region` VALUES ('741', '730', '汤旺河', '汤旺河区', '中国,黑龙江省,伊春市,汤旺河区', '3', 'tangwanghe', '0458', '153037', 'T', '129.57226', '48.45182'); +INSERT INTO `yoshop_region` VALUES ('742', '730', '带岭', '带岭区', '中国,黑龙江省,伊春市,带岭区', '3', 'dailing', '0458', '153106', 'D', '129.02352', '47.02553'); +INSERT INTO `yoshop_region` VALUES ('743', '730', '乌伊岭', '乌伊岭区', '中国,黑龙江省,伊春市,乌伊岭区', '3', 'wuyiling', '0458', '153038', 'W', '129.43981', '48.59602'); +INSERT INTO `yoshop_region` VALUES ('744', '730', '红星', '红星区', '中国,黑龙江省,伊春市,红星区', '3', 'hongxing', '0458', '153035', 'H', '129.3887', '48.23944'); +INSERT INTO `yoshop_region` VALUES ('745', '730', '上甘岭', '上甘岭区', '中国,黑龙江省,伊春市,上甘岭区', '3', 'shangganling', '0458', '153032', 'S', '129.02447', '47.97522'); +INSERT INTO `yoshop_region` VALUES ('746', '730', '嘉荫', '嘉荫县', '中国,黑龙江省,伊春市,嘉荫县', '3', 'jiayin', '0458', '153200', 'J', '130.39825', '48.8917'); +INSERT INTO `yoshop_region` VALUES ('747', '730', '铁力', '铁力市', '中国,黑龙江省,伊春市,铁力市', '3', 'tieli', '0458', '152500', 'T', '128.0317', '46.98571'); +INSERT INTO `yoshop_region` VALUES ('748', '655', '佳木斯', '佳木斯市', '中国,黑龙江省,佳木斯市', '2', 'jiamusi', '0454', '154002', 'J', '130.361634', '46.809606'); +INSERT INTO `yoshop_region` VALUES ('749', '748', '向阳', '向阳区', '中国,黑龙江省,佳木斯市,向阳区', '3', 'xiangyang', '0454', '154002', 'X', '130.36519', '46.80778'); +INSERT INTO `yoshop_region` VALUES ('750', '748', '前进', '前进区', '中国,黑龙江省,佳木斯市,前进区', '3', 'qianjin', '0454', '154002', 'Q', '130.37497', '46.81401'); +INSERT INTO `yoshop_region` VALUES ('751', '748', '东风', '东风区', '中国,黑龙江省,佳木斯市,东风区', '3', 'dongfeng', '0454', '154005', 'D', '130.40366', '46.82257'); +INSERT INTO `yoshop_region` VALUES ('752', '748', '郊区', '郊区', '中国,黑龙江省,佳木斯市,郊区', '3', 'jiaoqu', '0454', '154004', 'J', '130.32731', '46.80958'); +INSERT INTO `yoshop_region` VALUES ('753', '748', '桦南', '桦南县', '中国,黑龙江省,佳木斯市,桦南县', '3', 'huanan', '0454', '154400', null, '130.55361', '46.23921'); +INSERT INTO `yoshop_region` VALUES ('754', '748', '桦川', '桦川县', '中国,黑龙江省,佳木斯市,桦川县', '3', 'huachuan', '0454', '154300', null, '130.71893', '47.02297'); +INSERT INTO `yoshop_region` VALUES ('755', '748', '汤原', '汤原县', '中国,黑龙江省,佳木斯市,汤原县', '3', 'tangyuan', '0454', '154700', 'T', '129.90966', '46.72755'); +INSERT INTO `yoshop_region` VALUES ('756', '748', '抚远', '抚远县', '中国,黑龙江省,佳木斯市,抚远县', '3', 'fuyuan', '0454', '156500', 'F', '134.29595', '48.36794'); +INSERT INTO `yoshop_region` VALUES ('757', '748', '同江', '同江市', '中国,黑龙江省,佳木斯市,同江市', '3', 'tongjiang', '0454', '156400', 'T', '132.51095', '47.64211'); +INSERT INTO `yoshop_region` VALUES ('758', '748', '富锦', '富锦市', '中国,黑龙江省,佳木斯市,富锦市', '3', 'fujin', '0454', '156100', 'F', '132.03707', '47.25132'); +INSERT INTO `yoshop_region` VALUES ('759', '655', '七台河', '七台河市', '中国,黑龙江省,七台河市', '2', 'qitaihe', '0464', '154600', 'Q', '131.015584', '45.771266'); +INSERT INTO `yoshop_region` VALUES ('760', '759', '新兴', '新兴区', '中国,黑龙江省,七台河市,新兴区', '3', 'xinxing', '0464', '154604', 'X', '130.93212', '45.81624'); +INSERT INTO `yoshop_region` VALUES ('761', '759', '桃山', '桃山区', '中国,黑龙江省,七台河市,桃山区', '3', 'taoshan', '0464', '154600', 'T', '131.01786', '45.76782'); +INSERT INTO `yoshop_region` VALUES ('762', '759', '茄子河', '茄子河区', '中国,黑龙江省,七台河市,茄子河区', '3', 'qiezihe', '0464', '154622', 'Q', '131.06807', '45.78519'); +INSERT INTO `yoshop_region` VALUES ('763', '759', '勃利', '勃利县', '中国,黑龙江省,七台河市,勃利县', '3', 'boli', '0464', '154500', 'B', '130.59179', '45.755'); +INSERT INTO `yoshop_region` VALUES ('764', '655', '牡丹江', '牡丹江市', '中国,黑龙江省,牡丹江市', '2', 'mudanjiang', '0453', '157000', 'M', '129.618602', '44.582962'); +INSERT INTO `yoshop_region` VALUES ('765', '764', '东安', '东安区', '中国,黑龙江省,牡丹江市,东安区', '3', 'dong\'an', '0453', '157000', 'D', '129.62665', '44.58133'); +INSERT INTO `yoshop_region` VALUES ('766', '764', '阳明', '阳明区', '中国,黑龙江省,牡丹江市,阳明区', '3', 'yangming', '0453', '157013', 'Y', '129.63547', '44.59603'); +INSERT INTO `yoshop_region` VALUES ('767', '764', '爱民', '爱民区', '中国,黑龙江省,牡丹江市,爱民区', '3', 'aimin', '0453', '157009', 'A', '129.59077', '44.59648'); +INSERT INTO `yoshop_region` VALUES ('768', '764', '西安', '西安区', '中国,黑龙江省,牡丹江市,西安区', '3', 'xi\'an', '0453', '157000', 'X', '129.61616', '44.57766'); +INSERT INTO `yoshop_region` VALUES ('769', '764', '东宁', '东宁县', '中国,黑龙江省,牡丹江市,东宁县', '3', 'dongning', '0453', '157200', 'D', '131.12793', '44.0661'); +INSERT INTO `yoshop_region` VALUES ('770', '764', '林口', '林口县', '中国,黑龙江省,牡丹江市,林口县', '3', 'linkou', '0453', '157600', 'L', '130.28393', '45.27809'); +INSERT INTO `yoshop_region` VALUES ('771', '764', '绥芬河', '绥芬河市', '中国,黑龙江省,牡丹江市,绥芬河市', '3', 'suifenhe', '0453', '157300', 'S', '131.15139', '44.41249'); +INSERT INTO `yoshop_region` VALUES ('772', '764', '海林', '海林市', '中国,黑龙江省,牡丹江市,海林市', '3', 'hailin', '0453', '157100', 'H', '129.38156', '44.59'); +INSERT INTO `yoshop_region` VALUES ('773', '764', '宁安', '宁安市', '中国,黑龙江省,牡丹江市,宁安市', '3', 'ning\'an', '0453', '157400', 'N', '129.48303', '44.34016'); +INSERT INTO `yoshop_region` VALUES ('774', '764', '穆棱', '穆棱市', '中国,黑龙江省,牡丹江市,穆棱市', '3', 'muling', '0453', '157500', 'M', '130.52465', '44.919'); +INSERT INTO `yoshop_region` VALUES ('775', '655', '黑河', '黑河市', '中国,黑龙江省,黑河市', '2', 'heihe', '0456', '164300', 'H', '127.499023', '50.249585'); +INSERT INTO `yoshop_region` VALUES ('776', '775', '爱辉', '爱辉区', '中国,黑龙江省,黑河市,爱辉区', '3', 'aihui', '0456', '164300', 'A', '127.50074', '50.25202'); +INSERT INTO `yoshop_region` VALUES ('777', '775', '嫩江', '嫩江县', '中国,黑龙江省,黑河市,嫩江县', '3', 'nenjiang', '0456', '161400', 'N', '125.22607', '49.17844'); +INSERT INTO `yoshop_region` VALUES ('778', '775', '逊克', '逊克县', '中国,黑龙江省,黑河市,逊克县', '3', 'xunke', '0456', '164400', 'X', '128.47882', '49.57983'); +INSERT INTO `yoshop_region` VALUES ('779', '775', '孙吴', '孙吴县', '中国,黑龙江省,黑河市,孙吴县', '3', 'sunwu', '0456', '164200', 'S', '127.33599', '49.42539'); +INSERT INTO `yoshop_region` VALUES ('780', '775', '北安', '北安市', '中国,黑龙江省,黑河市,北安市', '3', 'bei\'an', '0456', '164000', 'B', '126.48193', '48.23872'); +INSERT INTO `yoshop_region` VALUES ('781', '775', '五大连池', '五大连池市', '中国,黑龙江省,黑河市,五大连池市', '3', 'wudalianchi', '0456', '164100', 'W', '126.20294', '48.51507'); +INSERT INTO `yoshop_region` VALUES ('782', '655', '绥化', '绥化市', '中国,黑龙江省,绥化市', '2', 'suihua', '0455', '152000', 'S', '126.99293', '46.637393'); +INSERT INTO `yoshop_region` VALUES ('783', '782', '北林', '北林区', '中国,黑龙江省,绥化市,北林区', '3', 'beilin', '0455', '152000', 'B', '126.98564', '46.63735'); +INSERT INTO `yoshop_region` VALUES ('784', '782', '望奎', '望奎县', '中国,黑龙江省,绥化市,望奎县', '3', 'wangkui', '0455', '152100', 'W', '126.48187', '46.83079'); +INSERT INTO `yoshop_region` VALUES ('785', '782', '兰西', '兰西县', '中国,黑龙江省,绥化市,兰西县', '3', 'lanxi', '0455', '151500', 'L', '126.28994', '46.2525'); +INSERT INTO `yoshop_region` VALUES ('786', '782', '青冈', '青冈县', '中国,黑龙江省,绥化市,青冈县', '3', 'qinggang', '0455', '151600', 'Q', '126.11325', '46.68534'); +INSERT INTO `yoshop_region` VALUES ('787', '782', '庆安', '庆安县', '中国,黑龙江省,绥化市,庆安县', '3', 'qing\'an', '0455', '152400', 'Q', '127.50753', '46.88016'); +INSERT INTO `yoshop_region` VALUES ('788', '782', '明水', '明水县', '中国,黑龙江省,绥化市,明水县', '3', 'mingshui', '0455', '151700', 'M', '125.90594', '47.17327'); +INSERT INTO `yoshop_region` VALUES ('789', '782', '绥棱', '绥棱县', '中国,黑龙江省,绥化市,绥棱县', '3', 'suileng', '0455', '152200', 'S', '127.11584', '47.24267'); +INSERT INTO `yoshop_region` VALUES ('790', '782', '安达', '安达市', '中国,黑龙江省,绥化市,安达市', '3', 'anda', '0455', '151400', 'A', '125.34375', '46.4177'); +INSERT INTO `yoshop_region` VALUES ('791', '782', '肇东', '肇东市', '中国,黑龙江省,绥化市,肇东市', '3', 'zhaodong', '0455', '151100', 'Z', '125.96243', '46.05131'); +INSERT INTO `yoshop_region` VALUES ('792', '782', '海伦', '海伦市', '中国,黑龙江省,绥化市,海伦市', '3', 'hailun', '0455', '152300', 'H', '126.9682', '47.46093'); +INSERT INTO `yoshop_region` VALUES ('793', '655', '大兴安岭', '大兴安岭地区', '中国,黑龙江省,大兴安岭地区', '2', 'daxinganling', '0457', '165000', 'D', '124.711526', '52.335262'); +INSERT INTO `yoshop_region` VALUES ('794', '793', '加格达奇', '加格达奇区', '中国,黑龙江省,大兴安岭地区,加格达奇区', '3', 'jiagedaqi', '0457', '165000', 'J', '124.30954', '51.98144'); +INSERT INTO `yoshop_region` VALUES ('795', '793', '新林', '新林区', '中国,黑龙江省,大兴安岭地区,新林区', '3', 'xinlin', '0457', '165000', 'X', '124.397983', '51.67341'); +INSERT INTO `yoshop_region` VALUES ('796', '793', '松岭', '松岭区', '中国,黑龙江省,大兴安岭地区,松岭区', '3', 'songling', '0457', '165000', 'S', '124.189713', '51.985453'); +INSERT INTO `yoshop_region` VALUES ('797', '793', '呼中', '呼中区', '中国,黑龙江省,大兴安岭地区,呼中区', '3', 'huzhong', '0457', '165000', 'H', '123.60009', '52.03346'); +INSERT INTO `yoshop_region` VALUES ('798', '793', '呼玛', '呼玛县', '中国,黑龙江省,大兴安岭地区,呼玛县', '3', 'huma', '0457', '165100', 'H', '126.66174', '51.73112'); +INSERT INTO `yoshop_region` VALUES ('799', '793', '塔河', '塔河县', '中国,黑龙江省,大兴安岭地区,塔河县', '3', 'tahe', '0457', '165200', 'T', '124.70999', '52.33431'); +INSERT INTO `yoshop_region` VALUES ('800', '793', '漠河', '漠河县', '中国,黑龙江省,大兴安岭地区,漠河县', '3', 'mohe', '0457', '165300', 'M', '122.53759', '52.97003'); +INSERT INTO `yoshop_region` VALUES ('801', '0', '上海', '上海市', '中国,上海', '1', 'shanghai', '', '', 'S', '121.472644', '31.231706'); +INSERT INTO `yoshop_region` VALUES ('802', '801', '上海', '上海市', '中国,上海,上海市', '2', 'shanghai', '021', '200000', 'S', '121.472644', '31.231706'); +INSERT INTO `yoshop_region` VALUES ('803', '802', '黄浦', '黄浦区', '中国,上海,上海市,黄浦区', '3', 'huangpu', '021', '200001', 'H', '121.49295', '31.22337'); +INSERT INTO `yoshop_region` VALUES ('804', '802', '徐汇', '徐汇区', '中国,上海,上海市,徐汇区', '3', 'xuhui', '021', '200030', 'X', '121.43676', '31.18831'); +INSERT INTO `yoshop_region` VALUES ('805', '802', '长宁', '长宁区', '中国,上海,上海市,长宁区', '3', 'changning', '021', '200050', 'C', '121.42462', '31.22036'); +INSERT INTO `yoshop_region` VALUES ('806', '802', '静安', '静安区', '中国,上海,上海市,静安区', '3', 'jing\'an', '021', '200040', 'J', '121.4444', '31.22884'); +INSERT INTO `yoshop_region` VALUES ('807', '802', '普陀', '普陀区', '中国,上海,上海市,普陀区', '3', 'putuo', '021', '200333', 'P', '121.39703', '31.24951'); +INSERT INTO `yoshop_region` VALUES ('808', '802', '闸北', '闸北区', '中国,上海,上海市,闸北区', '3', 'zhabei', '021', '200070', 'Z', '121.44636', '31.28075'); +INSERT INTO `yoshop_region` VALUES ('809', '802', '虹口', '虹口区', '中国,上海,上海市,虹口区', '3', 'hongkou', '021', '200086', 'H', '121.48162', '31.27788'); +INSERT INTO `yoshop_region` VALUES ('810', '802', '杨浦', '杨浦区', '中国,上海,上海市,杨浦区', '3', 'yangpu', '021', '200082', 'Y', '121.526', '31.2595'); +INSERT INTO `yoshop_region` VALUES ('811', '802', '闵行', '闵行区', '中国,上海,上海市,闵行区', '3', 'minhang', '021', '201100', null, '121.38162', '31.11246'); +INSERT INTO `yoshop_region` VALUES ('812', '802', '宝山', '宝山区', '中国,上海,上海市,宝山区', '3', 'baoshan', '021', '201900', 'B', '121.4891', '31.4045'); +INSERT INTO `yoshop_region` VALUES ('813', '802', '嘉定', '嘉定区', '中国,上海,上海市,嘉定区', '3', 'jiading', '021', '201800', 'J', '121.2655', '31.37473'); +INSERT INTO `yoshop_region` VALUES ('814', '802', '浦东', '浦东新区', '中国,上海,上海市,浦东新区', '3', 'pudong', '021', '200135', 'P', '121.5447', '31.22249'); +INSERT INTO `yoshop_region` VALUES ('815', '802', '金山', '金山区', '中国,上海,上海市,金山区', '3', 'jinshan', '021', '200540', 'J', '121.34164', '30.74163'); +INSERT INTO `yoshop_region` VALUES ('816', '802', '松江', '松江区', '中国,上海,上海市,松江区', '3', 'songjiang', '021', '201600', 'S', '121.22879', '31.03222'); +INSERT INTO `yoshop_region` VALUES ('817', '802', '青浦', '青浦区', '中国,上海,上海市,青浦区', '3', 'qingpu', '021', '201700', 'Q', '121.12417', '31.14974'); +INSERT INTO `yoshop_region` VALUES ('818', '802', '奉贤', '奉贤区', '中国,上海,上海市,奉贤区', '3', 'fengxian', '021', '201400', 'F', '121.47412', '30.9179'); +INSERT INTO `yoshop_region` VALUES ('819', '802', '崇明', '崇明县', '中国,上海,上海市,崇明县', '3', 'chongming', '021', '202150', 'C', '121.39758', '31.62278'); +INSERT INTO `yoshop_region` VALUES ('820', '0', '江苏', '江苏省', '中国,江苏省', '1', 'jiangsu', '', '', 'J', '118.767413', '32.041544'); +INSERT INTO `yoshop_region` VALUES ('821', '820', '南京', '南京市', '中国,江苏省,南京市', '2', 'nanjing', '025', '210008', 'N', '118.767413', '32.041544'); +INSERT INTO `yoshop_region` VALUES ('822', '821', '玄武', '玄武区', '中国,江苏省,南京市,玄武区', '3', 'xuanwu', '025', '210018', 'X', '118.79772', '32.04856'); +INSERT INTO `yoshop_region` VALUES ('823', '821', '秦淮', '秦淮区', '中国,江苏省,南京市,秦淮区', '3', 'qinhuai', '025', '210001', 'Q', '118.79815', '32.01112'); +INSERT INTO `yoshop_region` VALUES ('824', '821', '建邺', '建邺区', '中国,江苏省,南京市,建邺区', '3', 'jianye', '025', '210004', 'J', '118.76641', '32.03096'); +INSERT INTO `yoshop_region` VALUES ('825', '821', '鼓楼', '鼓楼区', '中国,江苏省,南京市,鼓楼区', '3', 'gulou', '025', '210009', 'G', '118.76974', '32.06632'); +INSERT INTO `yoshop_region` VALUES ('826', '821', '浦口', '浦口区', '中国,江苏省,南京市,浦口区', '3', 'pukou', '025', '211800', 'P', '118.62802', '32.05881'); +INSERT INTO `yoshop_region` VALUES ('827', '821', '栖霞', '栖霞区', '中国,江苏省,南京市,栖霞区', '3', 'qixia', '025', '210046', 'Q', '118.88064', '32.11352'); +INSERT INTO `yoshop_region` VALUES ('828', '821', '雨花台', '雨花台区', '中国,江苏省,南京市,雨花台区', '3', 'yuhuatai', '025', '210012', 'Y', '118.7799', '31.99202'); +INSERT INTO `yoshop_region` VALUES ('829', '821', '江宁', '江宁区', '中国,江苏省,南京市,江宁区', '3', 'jiangning', '025', '211100', 'J', '118.8399', '31.95263'); +INSERT INTO `yoshop_region` VALUES ('830', '821', '六合', '六合区', '中国,江苏省,南京市,六合区', '3', 'luhe', '025', '211500', 'L', '118.8413', '32.34222'); +INSERT INTO `yoshop_region` VALUES ('831', '821', '溧水', '溧水区', '中国,江苏省,南京市,溧水区', '3', 'lishui', '025', '211200', null, '119.028732', '31.653061'); +INSERT INTO `yoshop_region` VALUES ('832', '821', '高淳', '高淳区', '中国,江苏省,南京市,高淳区', '3', 'gaochun', '025', '211300', 'G', '118.87589', '31.327132'); +INSERT INTO `yoshop_region` VALUES ('833', '820', '无锡', '无锡市', '中国,江苏省,无锡市', '2', 'wuxi', '0510', '214000', 'W', '120.301663', '31.574729'); +INSERT INTO `yoshop_region` VALUES ('834', '833', '崇安', '崇安区', '中国,江苏省,无锡市,崇安区', '3', 'chong\'an', '0510', '214001', 'C', '120.29975', '31.58002'); +INSERT INTO `yoshop_region` VALUES ('835', '833', '南长', '南长区', '中国,江苏省,无锡市,南长区', '3', 'nanchang', '0510', '214021', 'N', '120.30873', '31.56359'); +INSERT INTO `yoshop_region` VALUES ('836', '833', '北塘', '北塘区', '中国,江苏省,无锡市,北塘区', '3', 'beitang', '0510', '214044', 'B', '120.29405', '31.60592'); +INSERT INTO `yoshop_region` VALUES ('837', '833', '锡山', '锡山区', '中国,江苏省,无锡市,锡山区', '3', 'xishan', '0510', '214101', 'X', '120.35699', '31.5886'); +INSERT INTO `yoshop_region` VALUES ('838', '833', '惠山', '惠山区', '中国,江苏省,无锡市,惠山区', '3', 'huishan', '0510', '214174', 'H', '120.29849', '31.68088'); +INSERT INTO `yoshop_region` VALUES ('839', '833', '滨湖', '滨湖区', '中国,江苏省,无锡市,滨湖区', '3', 'binhu', '0510', '214123', 'B', '120.29461', '31.52162'); +INSERT INTO `yoshop_region` VALUES ('840', '833', '江阴', '江阴市', '中国,江苏省,无锡市,江阴市', '3', 'jiangyin', '0510', '214431', 'J', '120.2853', '31.91996'); +INSERT INTO `yoshop_region` VALUES ('841', '833', '宜兴', '宜兴市', '中国,江苏省,无锡市,宜兴市', '3', 'yixing', '0510', '214200', 'Y', '119.82357', '31.33978'); +INSERT INTO `yoshop_region` VALUES ('842', '820', '徐州', '徐州市', '中国,江苏省,徐州市', '2', 'xuzhou', '0516', '221003', 'X', '117.184811', '34.261792'); +INSERT INTO `yoshop_region` VALUES ('843', '842', '鼓楼', '鼓楼区', '中国,江苏省,徐州市,鼓楼区', '3', 'gulou', '0516', '221005', 'G', '117.18559', '34.28851'); +INSERT INTO `yoshop_region` VALUES ('844', '842', '云龙', '云龙区', '中国,江苏省,徐州市,云龙区', '3', 'yunlong', '0516', '221007', 'Y', '117.23053', '34.24895'); +INSERT INTO `yoshop_region` VALUES ('845', '842', '贾汪', '贾汪区', '中国,江苏省,徐州市,贾汪区', '3', 'jiawang', '0516', '221003', 'J', '117.45346', '34.44264'); +INSERT INTO `yoshop_region` VALUES ('846', '842', '泉山', '泉山区', '中国,江苏省,徐州市,泉山区', '3', 'quanshan', '0516', '221006', 'Q', '117.19378', '34.24418'); +INSERT INTO `yoshop_region` VALUES ('847', '842', '铜山', '铜山区', '中国,江苏省,徐州市,铜山区', '3', 'tongshan', '0516', '221106', 'T', '117.183894', '34.19288'); +INSERT INTO `yoshop_region` VALUES ('848', '842', '丰县', '丰县', '中国,江苏省,徐州市,丰县', '3', 'fengxian', '0516', '221700', 'F', '116.59957', '34.69972'); +INSERT INTO `yoshop_region` VALUES ('849', '842', '沛县', '沛县', '中国,江苏省,徐州市,沛县', '3', 'peixian', '0516', '221600', 'P', '116.93743', '34.72163'); +INSERT INTO `yoshop_region` VALUES ('850', '842', '睢宁', '睢宁县', '中国,江苏省,徐州市,睢宁县', '3', 'suining', '0516', '221200', null, '117.94104', '33.91269'); +INSERT INTO `yoshop_region` VALUES ('851', '842', '新沂', '新沂市', '中国,江苏省,徐州市,新沂市', '3', 'xinyi', '0516', '221400', 'X', '118.35452', '34.36942'); +INSERT INTO `yoshop_region` VALUES ('852', '842', '邳州', '邳州市', '中国,江苏省,徐州市,邳州市', '3', 'pizhou', '0516', '221300', null, '117.95858', '34.33329'); +INSERT INTO `yoshop_region` VALUES ('853', '820', '常州', '常州市', '中国,江苏省,常州市', '2', 'changzhou', '0519', '213000', 'C', '119.946973', '31.772752'); +INSERT INTO `yoshop_region` VALUES ('854', '853', '天宁', '天宁区', '中国,江苏省,常州市,天宁区', '3', 'tianning', '0519', '213000', 'T', '119.95132', '31.75211'); +INSERT INTO `yoshop_region` VALUES ('855', '853', '钟楼', '钟楼区', '中国,江苏省,常州市,钟楼区', '3', 'zhonglou', '0519', '213023', 'Z', '119.90178', '31.80221'); +INSERT INTO `yoshop_region` VALUES ('856', '853', '戚墅堰', '戚墅堰区', '中国,江苏省,常州市,戚墅堰区', '3', 'qishuyan', '0519', '213025', 'Q', '120.06106', '31.71956'); +INSERT INTO `yoshop_region` VALUES ('857', '853', '新北', '新北区', '中国,江苏省,常州市,新北区', '3', 'xinbei', '0519', '213022', 'X', '119.97131', '31.83046'); +INSERT INTO `yoshop_region` VALUES ('858', '853', '武进', '武进区', '中国,江苏省,常州市,武进区', '3', 'wujin', '0519', '213100', 'W', '119.94244', '31.70086'); +INSERT INTO `yoshop_region` VALUES ('859', '853', '溧阳', '溧阳市', '中国,江苏省,常州市,溧阳市', '3', 'liyang', '0519', '213300', null, '119.4837', '31.41538'); +INSERT INTO `yoshop_region` VALUES ('860', '853', '金坛', '金坛市', '中国,江苏省,常州市,金坛市', '3', 'jintan', '0519', '213200', 'J', '119.57757', '31.74043'); +INSERT INTO `yoshop_region` VALUES ('861', '820', '苏州', '苏州市', '中国,江苏省,苏州市', '2', 'suzhou', '0512', '215002', 'S', '120.619585', '31.299379'); +INSERT INTO `yoshop_region` VALUES ('862', '861', '虎丘', '虎丘区', '中国,江苏省,苏州市,虎丘区', '3', 'huqiu', '0512', '215004', 'H', '120.57345', '31.2953'); +INSERT INTO `yoshop_region` VALUES ('863', '861', '吴中', '吴中区', '中国,江苏省,苏州市,吴中区', '3', 'wuzhong', '0512', '215128', 'W', '120.63211', '31.26226'); +INSERT INTO `yoshop_region` VALUES ('864', '861', '相城', '相城区', '中国,江苏省,苏州市,相城区', '3', 'xiangcheng', '0512', '215131', 'X', '120.64239', '31.36889'); +INSERT INTO `yoshop_region` VALUES ('865', '861', '姑苏', '姑苏区', '中国,江苏省,苏州市,姑苏区', '3', 'gusu', '0512', '215031', 'G', '120.619585', '31.299379'); +INSERT INTO `yoshop_region` VALUES ('866', '861', '吴江', '吴江区', '中国,江苏省,苏州市,吴江区', '3', 'wujiang', '0512', '215200', 'W', '120.638317', '31.159815'); +INSERT INTO `yoshop_region` VALUES ('867', '861', '常熟', '常熟市', '中国,江苏省,苏州市,常熟市', '3', 'changshu', '0512', '215500', 'C', '120.75225', '31.65374'); +INSERT INTO `yoshop_region` VALUES ('868', '861', '张家港', '张家港市', '中国,江苏省,苏州市,张家港市', '3', 'zhangjiagang', '0512', '215600', 'Z', '120.55538', '31.87532'); +INSERT INTO `yoshop_region` VALUES ('869', '861', '昆山', '昆山市', '中国,江苏省,苏州市,昆山市', '3', 'kunshan', '0512', '215300', 'K', '120.98074', '31.38464'); +INSERT INTO `yoshop_region` VALUES ('870', '861', '太仓', '太仓市', '中国,江苏省,苏州市,太仓市', '3', 'taicang', '0512', '215400', 'T', '121.10891', '31.4497'); +INSERT INTO `yoshop_region` VALUES ('871', '820', '南通', '南通市', '中国,江苏省,南通市', '2', 'nantong', '0513', '226001', 'N', '120.864608', '32.016212'); +INSERT INTO `yoshop_region` VALUES ('872', '871', '崇川', '崇川区', '中国,江苏省,南通市,崇川区', '3', 'chongchuan', '0513', '226001', 'C', '120.8573', '32.0098'); +INSERT INTO `yoshop_region` VALUES ('873', '871', '港闸', '港闸区', '中国,江苏省,南通市,港闸区', '3', 'gangzha', '0513', '226001', 'G', '120.81778', '32.03163'); +INSERT INTO `yoshop_region` VALUES ('874', '871', '通州', '通州区', '中国,江苏省,南通市,通州区', '3', 'tongzhou', '0513', '226300', 'T', '121.07293', '32.0676'); +INSERT INTO `yoshop_region` VALUES ('875', '871', '海安', '海安县', '中国,江苏省,南通市,海安县', '3', 'hai\'an', '0513', '226600', 'H', '120.45852', '32.54514'); +INSERT INTO `yoshop_region` VALUES ('876', '871', '如东', '如东县', '中国,江苏省,南通市,如东县', '3', 'rudong', '0513', '226400', 'R', '121.18942', '32.31439'); +INSERT INTO `yoshop_region` VALUES ('877', '871', '启东', '启东市', '中国,江苏省,南通市,启东市', '3', 'qidong', '0513', '226200', 'Q', '121.65985', '31.81083'); +INSERT INTO `yoshop_region` VALUES ('878', '871', '如皋', '如皋市', '中国,江苏省,南通市,如皋市', '3', 'rugao', '0513', '226500', 'R', '120.55969', '32.37597'); +INSERT INTO `yoshop_region` VALUES ('879', '871', '海门', '海门市', '中国,江苏省,南通市,海门市', '3', 'haimen', '0513', '226100', 'H', '121.16995', '31.89422'); +INSERT INTO `yoshop_region` VALUES ('880', '820', '连云港', '连云港市', '中国,江苏省,连云港市', '2', 'lianyungang', '0518', '222002', 'L', '119.178821', '34.600018'); +INSERT INTO `yoshop_region` VALUES ('881', '880', '连云', '连云区', '中国,江苏省,连云港市,连云区', '3', 'lianyun', '0518', '222042', 'L', '119.37304', '34.75293'); +INSERT INTO `yoshop_region` VALUES ('882', '880', '海州', '海州区', '中国,江苏省,连云港市,海州区', '3', 'haizhou', '0518', '222003', 'H', '119.13128', '34.56986'); +INSERT INTO `yoshop_region` VALUES ('883', '880', '赣榆', '赣榆区', '中国,江苏省,连云港市,赣榆区', '3', 'ganyu', '0518', '222100', 'G', '119.128774', '34.839154'); +INSERT INTO `yoshop_region` VALUES ('884', '880', '东海', '东海县', '中国,江苏省,连云港市,东海县', '3', 'donghai', '0518', '222300', 'D', '118.77145', '34.54215'); +INSERT INTO `yoshop_region` VALUES ('885', '880', '灌云', '灌云县', '中国,江苏省,连云港市,灌云县', '3', 'guanyun', '0518', '222200', 'G', '119.23925', '34.28391'); +INSERT INTO `yoshop_region` VALUES ('886', '880', '灌南', '灌南县', '中国,江苏省,连云港市,灌南县', '3', 'guannan', '0518', '222500', 'G', '119.35632', '34.09'); +INSERT INTO `yoshop_region` VALUES ('887', '820', '淮安', '淮安市', '中国,江苏省,淮安市', '2', 'huai\'an', '0517', '223001', 'H', '119.021265', '33.597506'); +INSERT INTO `yoshop_region` VALUES ('888', '887', '清河', '清河区', '中国,江苏省,淮安市,清河区', '3', 'qinghe', '0517', '223001', 'Q', '119.00778', '33.59949'); +INSERT INTO `yoshop_region` VALUES ('889', '887', '淮安', '淮安区', '中国,江苏省,淮安市,淮安区', '3', 'huai\'an', '0517', '223200', 'H', '119.021265', '33.597506'); +INSERT INTO `yoshop_region` VALUES ('890', '887', '淮阴', '淮阴区', '中国,江苏省,淮安市,淮阴区', '3', 'huaiyin', '0517', '223300', 'H', '119.03485', '33.63171'); +INSERT INTO `yoshop_region` VALUES ('891', '887', '清浦', '清浦区', '中国,江苏省,淮安市,清浦区', '3', 'qingpu', '0517', '223002', 'Q', '119.02648', '33.55232'); +INSERT INTO `yoshop_region` VALUES ('892', '887', '涟水', '涟水县', '中国,江苏省,淮安市,涟水县', '3', 'lianshui', '0517', '223400', 'L', '119.26083', '33.78094'); +INSERT INTO `yoshop_region` VALUES ('893', '887', '洪泽', '洪泽县', '中国,江苏省,淮安市,洪泽县', '3', 'hongze', '0517', '223100', 'H', '118.87344', '33.29429'); +INSERT INTO `yoshop_region` VALUES ('894', '887', '盱眙', '盱眙县', '中国,江苏省,淮安市,盱眙县', '3', 'xuyi', '0517', '211700', null, '118.54495', '33.01086'); +INSERT INTO `yoshop_region` VALUES ('895', '887', '金湖', '金湖县', '中国,江苏省,淮安市,金湖县', '3', 'jinhu', '0517', '211600', 'J', '119.02307', '33.02219'); +INSERT INTO `yoshop_region` VALUES ('896', '820', '盐城', '盐城市', '中国,江苏省,盐城市', '2', 'yancheng', '0515', '224005', 'Y', '120.139998', '33.377631'); +INSERT INTO `yoshop_region` VALUES ('897', '896', '亭湖', '亭湖区', '中国,江苏省,盐城市,亭湖区', '3', 'tinghu', '0515', '224005', 'T', '120.16583', '33.37825'); +INSERT INTO `yoshop_region` VALUES ('898', '896', '盐都', '盐都区', '中国,江苏省,盐城市,盐都区', '3', 'yandu', '0515', '224055', 'Y', '120.15441', '33.3373'); +INSERT INTO `yoshop_region` VALUES ('899', '896', '响水', '响水县', '中国,江苏省,盐城市,响水县', '3', 'xiangshui', '0515', '224600', 'X', '119.56985', '34.20513'); +INSERT INTO `yoshop_region` VALUES ('900', '896', '滨海', '滨海县', '中国,江苏省,盐城市,滨海县', '3', 'binhai', '0515', '224500', 'B', '119.82058', '33.98972'); +INSERT INTO `yoshop_region` VALUES ('901', '896', '阜宁', '阜宁县', '中国,江苏省,盐城市,阜宁县', '3', 'funing', '0515', '224400', 'F', '119.80175', '33.78228'); +INSERT INTO `yoshop_region` VALUES ('902', '896', '射阳', '射阳县', '中国,江苏省,盐城市,射阳县', '3', 'sheyang', '0515', '224300', 'S', '120.26043', '33.77636'); +INSERT INTO `yoshop_region` VALUES ('903', '896', '建湖', '建湖县', '中国,江苏省,盐城市,建湖县', '3', 'jianhu', '0515', '224700', 'J', '119.79852', '33.47241'); +INSERT INTO `yoshop_region` VALUES ('904', '896', '东台', '东台市', '中国,江苏省,盐城市,东台市', '3', 'dongtai', '0515', '224200', 'D', '120.32376', '32.85078'); +INSERT INTO `yoshop_region` VALUES ('905', '896', '大丰', '大丰市', '中国,江苏省,盐城市,大丰市', '3', 'dafeng', '0515', '224100', 'D', '120.46594', '33.19893'); +INSERT INTO `yoshop_region` VALUES ('906', '820', '扬州', '扬州市', '中国,江苏省,扬州市', '2', 'yangzhou', '0514', '225002', 'Y', '119.421003', '32.393159'); +INSERT INTO `yoshop_region` VALUES ('907', '906', '广陵', '广陵区', '中国,江苏省,扬州市,广陵区', '3', 'guangling', '0514', '225002', 'G', '119.43186', '32.39472'); +INSERT INTO `yoshop_region` VALUES ('908', '906', '邗江', '邗江区', '中国,江苏省,扬州市,邗江区', '3', 'hanjiang', '0514', '225002', null, '119.39816', '32.3765'); +INSERT INTO `yoshop_region` VALUES ('909', '906', '江都', '江都区', '中国,江苏省,扬州市,江都区', '3', 'jiangdu', '0514', '225200', 'J', '119.567481', '32.426564'); +INSERT INTO `yoshop_region` VALUES ('910', '906', '宝应', '宝应县', '中国,江苏省,扬州市,宝应县', '3', 'baoying', '0514', '225800', 'B', '119.31213', '33.23549'); +INSERT INTO `yoshop_region` VALUES ('911', '906', '仪征', '仪征市', '中国,江苏省,扬州市,仪征市', '3', 'yizheng', '0514', '211400', 'Y', '119.18432', '32.27197'); +INSERT INTO `yoshop_region` VALUES ('912', '906', '高邮', '高邮市', '中国,江苏省,扬州市,高邮市', '3', 'gaoyou', '0514', '225600', 'G', '119.45965', '32.78135'); +INSERT INTO `yoshop_region` VALUES ('913', '820', '镇江', '镇江市', '中国,江苏省,镇江市', '2', 'zhenjiang', '0511', '212004', 'Z', '119.452753', '32.204402'); +INSERT INTO `yoshop_region` VALUES ('914', '913', '京口', '京口区', '中国,江苏省,镇江市,京口区', '3', 'jingkou', '0511', '212003', 'J', '119.46947', '32.19809'); +INSERT INTO `yoshop_region` VALUES ('915', '913', '润州', '润州区', '中国,江苏省,镇江市,润州区', '3', 'runzhou', '0511', '212005', 'R', '119.41134', '32.19523'); +INSERT INTO `yoshop_region` VALUES ('916', '913', '丹徒', '丹徒区', '中国,江苏省,镇江市,丹徒区', '3', 'dantu', '0511', '212028', 'D', '119.43383', '32.13183'); +INSERT INTO `yoshop_region` VALUES ('917', '913', '丹阳', '丹阳市', '中国,江苏省,镇江市,丹阳市', '3', 'danyang', '0511', '212300', 'D', '119.57525', '31.99121'); +INSERT INTO `yoshop_region` VALUES ('918', '913', '扬中', '扬中市', '中国,江苏省,镇江市,扬中市', '3', 'yangzhong', '0511', '212200', 'Y', '119.79718', '32.2363'); +INSERT INTO `yoshop_region` VALUES ('919', '913', '句容', '句容市', '中国,江苏省,镇江市,句容市', '3', 'jurong', '0511', '212400', 'J', '119.16482', '31.95591'); +INSERT INTO `yoshop_region` VALUES ('920', '820', '泰州', '泰州市', '中国,江苏省,泰州市', '2', 'taizhou', '0523', '225300', 'T', '119.915176', '32.484882'); +INSERT INTO `yoshop_region` VALUES ('921', '920', '海陵', '海陵区', '中国,江苏省,泰州市,海陵区', '3', 'hailing', '0523', '225300', 'H', '119.91942', '32.49101'); +INSERT INTO `yoshop_region` VALUES ('922', '920', '高港', '高港区', '中国,江苏省,泰州市,高港区', '3', 'gaogang', '0523', '225321', 'G', '119.88089', '32.31833'); +INSERT INTO `yoshop_region` VALUES ('923', '920', '姜堰', '姜堰区', '中国,江苏省,泰州市,姜堰区', '3', 'jiangyan', '0523', '225500', 'J', '120.148208', '32.508483'); +INSERT INTO `yoshop_region` VALUES ('924', '920', '兴化', '兴化市', '中国,江苏省,泰州市,兴化市', '3', 'xinghua', '0523', '225700', 'X', '119.85238', '32.90944'); +INSERT INTO `yoshop_region` VALUES ('925', '920', '靖江', '靖江市', '中国,江苏省,泰州市,靖江市', '3', 'jingjiang', '0523', '214500', 'J', '120.27291', '32.01595'); +INSERT INTO `yoshop_region` VALUES ('926', '920', '泰兴', '泰兴市', '中国,江苏省,泰州市,泰兴市', '3', 'taixing', '0523', '225400', 'T', '120.05194', '32.17187'); +INSERT INTO `yoshop_region` VALUES ('927', '820', '宿迁', '宿迁市', '中国,江苏省,宿迁市', '2', 'suqian', '0527', '223800', 'S', '118.293328', '33.945154'); +INSERT INTO `yoshop_region` VALUES ('928', '927', '宿城', '宿城区', '中国,江苏省,宿迁市,宿城区', '3', 'sucheng', '0527', '223800', 'S', '118.29141', '33.94219'); +INSERT INTO `yoshop_region` VALUES ('929', '927', '宿豫', '宿豫区', '中国,江苏省,宿迁市,宿豫区', '3', 'suyu', '0527', '223800', 'S', '118.32922', '33.94673'); +INSERT INTO `yoshop_region` VALUES ('930', '927', '沭阳', '沭阳县', '中国,江苏省,宿迁市,沭阳县', '3', 'shuyang', '0527', '223600', null, '118.76873', '34.11446'); +INSERT INTO `yoshop_region` VALUES ('931', '927', '泗阳', '泗阳县', '中国,江苏省,宿迁市,泗阳县', '3', 'siyang', '0527', '223700', null, '118.7033', '33.72096'); +INSERT INTO `yoshop_region` VALUES ('932', '927', '泗洪', '泗洪县', '中国,江苏省,宿迁市,泗洪县', '3', 'sihong', '0527', '223900', null, '118.21716', '33.45996'); +INSERT INTO `yoshop_region` VALUES ('933', '0', '浙江', '浙江省', '中国,浙江省', '1', 'zhejiang', '', '', 'Z', '120.153576', '30.287459'); +INSERT INTO `yoshop_region` VALUES ('934', '933', '杭州', '杭州市', '中国,浙江省,杭州市', '2', 'hangzhou', '0571', '310026', 'H', '120.153576', '30.287459'); +INSERT INTO `yoshop_region` VALUES ('935', '934', '上城', '上城区', '中国,浙江省,杭州市,上城区', '3', 'shangcheng', '0571', '310002', 'S', '120.16922', '30.24255'); +INSERT INTO `yoshop_region` VALUES ('936', '934', '下城', '下城区', '中国,浙江省,杭州市,下城区', '3', 'xiacheng', '0571', '310006', 'X', '120.18096', '30.28153'); +INSERT INTO `yoshop_region` VALUES ('937', '934', '江干', '江干区', '中国,浙江省,杭州市,江干区', '3', 'jianggan', '0571', '310016', 'J', '120.20517', '30.2572'); +INSERT INTO `yoshop_region` VALUES ('938', '934', '拱墅', '拱墅区', '中国,浙江省,杭州市,拱墅区', '3', 'gongshu', '0571', '310011', 'G', '120.14209', '30.31968'); +INSERT INTO `yoshop_region` VALUES ('939', '934', '西湖', '西湖区', '中国,浙江省,杭州市,西湖区', '3', 'xihu', '0571', '310013', 'X', '120.12979', '30.25949'); +INSERT INTO `yoshop_region` VALUES ('940', '934', '滨江', '滨江区', '中国,浙江省,杭州市,滨江区', '3', 'binjiang', '0571', '310051', 'B', '120.21194', '30.20835'); +INSERT INTO `yoshop_region` VALUES ('941', '934', '萧山', '萧山区', '中国,浙江省,杭州市,萧山区', '3', 'xiaoshan', '0571', '311200', 'X', '120.26452', '30.18505'); +INSERT INTO `yoshop_region` VALUES ('942', '934', '余杭', '余杭区', '中国,浙江省,杭州市,余杭区', '3', 'yuhang', '0571', '311100', 'Y', '120.29986', '30.41829'); +INSERT INTO `yoshop_region` VALUES ('943', '934', '桐庐', '桐庐县', '中国,浙江省,杭州市,桐庐县', '3', 'tonglu', '0571', '311500', 'T', '119.68853', '29.79779'); +INSERT INTO `yoshop_region` VALUES ('944', '934', '淳安', '淳安县', '中国,浙江省,杭州市,淳安县', '3', 'chun\'an', '0571', '311700', 'C', '119.04257', '29.60988'); +INSERT INTO `yoshop_region` VALUES ('945', '934', '建德', '建德市', '中国,浙江省,杭州市,建德市', '3', 'jiande', '0571', '311600', 'J', '119.28158', '29.47603'); +INSERT INTO `yoshop_region` VALUES ('946', '934', '富阳', '富阳区', '中国,浙江省,杭州市,富阳区', '3', 'fuyang', '0571', '311400', 'F', '119.96041', '30.04878'); +INSERT INTO `yoshop_region` VALUES ('947', '934', '临安', '临安市', '中国,浙江省,杭州市,临安市', '3', 'lin\'an', '0571', '311300', 'L', '119.72473', '30.23447'); +INSERT INTO `yoshop_region` VALUES ('948', '933', '宁波', '宁波市', '中国,浙江省,宁波市', '2', 'ningbo', '0574', '315000', 'N', '121.549792', '29.868388'); +INSERT INTO `yoshop_region` VALUES ('949', '948', '海曙', '海曙区', '中国,浙江省,宁波市,海曙区', '3', 'haishu', '0574', '315000', 'H', '121.55106', '29.85977'); +INSERT INTO `yoshop_region` VALUES ('950', '948', '江东', '江东区', '中国,浙江省,宁波市,江东区', '3', 'jiangdong', '0574', '315040', 'J', '121.57028', '29.86701'); +INSERT INTO `yoshop_region` VALUES ('951', '948', '江北', '江北区', '中国,浙江省,宁波市,江北区', '3', 'jiangbei', '0574', '315020', 'J', '121.55681', '29.88776'); +INSERT INTO `yoshop_region` VALUES ('952', '948', '北仑', '北仑区', '中国,浙江省,宁波市,北仑区', '3', 'beilun', '0574', '315800', 'B', '121.84408', '29.90069'); +INSERT INTO `yoshop_region` VALUES ('953', '948', '镇海', '镇海区', '中国,浙江省,宁波市,镇海区', '3', 'zhenhai', '0574', '315200', 'Z', '121.71615', '29.94893'); +INSERT INTO `yoshop_region` VALUES ('954', '948', '鄞州', '鄞州区', '中国,浙江省,宁波市,鄞州区', '3', 'yinzhou', '0574', '315100', null, '121.54754', '29.81614'); +INSERT INTO `yoshop_region` VALUES ('955', '948', '象山', '象山县', '中国,浙江省,宁波市,象山县', '3', 'xiangshan', '0574', '315700', 'X', '121.86917', '29.47758'); +INSERT INTO `yoshop_region` VALUES ('956', '948', '宁海', '宁海县', '中国,浙江省,宁波市,宁海县', '3', 'ninghai', '0574', '315600', 'N', '121.43072', '29.2889'); +INSERT INTO `yoshop_region` VALUES ('957', '948', '余姚', '余姚市', '中国,浙江省,宁波市,余姚市', '3', 'yuyao', '0574', '315400', 'Y', '121.15341', '30.03867'); +INSERT INTO `yoshop_region` VALUES ('958', '948', '慈溪', '慈溪市', '中国,浙江省,宁波市,慈溪市', '3', 'cixi', '0574', '315300', 'C', '121.26641', '30.16959'); +INSERT INTO `yoshop_region` VALUES ('959', '948', '奉化', '奉化市', '中国,浙江省,宁波市,奉化市', '3', 'fenghua', '0574', '315500', 'F', '121.41003', '29.65537'); +INSERT INTO `yoshop_region` VALUES ('960', '933', '温州', '温州市', '中国,浙江省,温州市', '2', 'wenzhou', '0577', '325000', 'W', '120.672111', '28.000575'); +INSERT INTO `yoshop_region` VALUES ('961', '960', '鹿城', '鹿城区', '中国,浙江省,温州市,鹿城区', '3', 'lucheng', '0577', '325000', 'L', '120.65505', '28.01489'); +INSERT INTO `yoshop_region` VALUES ('962', '960', '龙湾', '龙湾区', '中国,浙江省,温州市,龙湾区', '3', 'longwan', '0577', '325013', 'L', '120.83053', '27.91284'); +INSERT INTO `yoshop_region` VALUES ('963', '960', '瓯海', '瓯海区', '中国,浙江省,温州市,瓯海区', '3', 'ouhai', '0577', '325005', null, '120.63751', '28.00714'); +INSERT INTO `yoshop_region` VALUES ('964', '960', '洞头', '洞头县', '中国,浙江省,温州市,洞头县', '3', 'dongtou', '0577', '325700', 'D', '121.15606', '27.83634'); +INSERT INTO `yoshop_region` VALUES ('965', '960', '永嘉', '永嘉县', '中国,浙江省,温州市,永嘉县', '3', 'yongjia', '0577', '325100', 'Y', '120.69317', '28.15456'); +INSERT INTO `yoshop_region` VALUES ('966', '960', '平阳', '平阳县', '中国,浙江省,温州市,平阳县', '3', 'pingyang', '0577', '325400', 'P', '120.56506', '27.66245'); +INSERT INTO `yoshop_region` VALUES ('967', '960', '苍南', '苍南县', '中国,浙江省,温州市,苍南县', '3', 'cangnan', '0577', '325800', 'C', '120.42608', '27.51739'); +INSERT INTO `yoshop_region` VALUES ('968', '960', '文成', '文成县', '中国,浙江省,温州市,文成县', '3', 'wencheng', '0577', '325300', 'W', '120.09063', '27.78678'); +INSERT INTO `yoshop_region` VALUES ('969', '960', '泰顺', '泰顺县', '中国,浙江省,温州市,泰顺县', '3', 'taishun', '0577', '325500', 'T', '119.7182', '27.55694'); +INSERT INTO `yoshop_region` VALUES ('970', '960', '瑞安', '瑞安市', '中国,浙江省,温州市,瑞安市', '3', 'rui\'an', '0577', '325200', 'R', '120.65466', '27.78041'); +INSERT INTO `yoshop_region` VALUES ('971', '960', '乐清', '乐清市', '中国,浙江省,温州市,乐清市', '3', 'yueqing', '0577', '325600', 'L', '120.9617', '28.12404'); +INSERT INTO `yoshop_region` VALUES ('972', '933', '嘉兴', '嘉兴市', '中国,浙江省,嘉兴市', '2', 'jiaxing', '0573', '314000', 'J', '120.750865', '30.762653'); +INSERT INTO `yoshop_region` VALUES ('973', '972', '南湖', '南湖区', '中国,浙江省,嘉兴市,南湖区', '3', 'nanhu', '0573', '314051', 'N', '120.78524', '30.74865'); +INSERT INTO `yoshop_region` VALUES ('974', '972', '秀洲', '秀洲区', '中国,浙江省,嘉兴市,秀洲区', '3', 'xiuzhou', '0573', '314031', 'X', '120.70867', '30.76454'); +INSERT INTO `yoshop_region` VALUES ('975', '972', '嘉善', '嘉善县', '中国,浙江省,嘉兴市,嘉善县', '3', 'jiashan', '0573', '314100', 'J', '120.92559', '30.82993'); +INSERT INTO `yoshop_region` VALUES ('976', '972', '海盐', '海盐县', '中国,浙江省,嘉兴市,海盐县', '3', 'haiyan', '0573', '314300', 'H', '120.9457', '30.52547'); +INSERT INTO `yoshop_region` VALUES ('977', '972', '海宁', '海宁市', '中国,浙江省,嘉兴市,海宁市', '3', 'haining', '0573', '314400', 'H', '120.6813', '30.5097'); +INSERT INTO `yoshop_region` VALUES ('978', '972', '平湖', '平湖市', '中国,浙江省,嘉兴市,平湖市', '3', 'pinghu', '0573', '314200', 'P', '121.02166', '30.69618'); +INSERT INTO `yoshop_region` VALUES ('979', '972', '桐乡', '桐乡市', '中国,浙江省,嘉兴市,桐乡市', '3', 'tongxiang', '0573', '314500', 'T', '120.56485', '30.6302'); +INSERT INTO `yoshop_region` VALUES ('980', '933', '湖州', '湖州市', '中国,浙江省,湖州市', '2', 'huzhou', '0572', '313000', 'H', '120.102398', '30.867198'); +INSERT INTO `yoshop_region` VALUES ('981', '980', '吴兴', '吴兴区', '中国,浙江省,湖州市,吴兴区', '3', 'wuxing', '0572', '313000', 'W', '120.12548', '30.85752'); +INSERT INTO `yoshop_region` VALUES ('982', '980', '南浔', '南浔区', '中国,浙江省,湖州市,南浔区', '3', 'nanxun', '0572', '313009', 'N', '120.42038', '30.86686'); +INSERT INTO `yoshop_region` VALUES ('983', '980', '德清', '德清县', '中国,浙江省,湖州市,德清县', '3', 'deqing', '0572', '313200', 'D', '119.97836', '30.53369'); +INSERT INTO `yoshop_region` VALUES ('984', '980', '长兴', '长兴县', '中国,浙江省,湖州市,长兴县', '3', 'changxing', '0572', '313100', 'C', '119.90783', '31.00606'); +INSERT INTO `yoshop_region` VALUES ('985', '980', '安吉', '安吉县', '中国,浙江省,湖州市,安吉县', '3', 'anji', '0572', '313300', 'A', '119.68158', '30.63798'); +INSERT INTO `yoshop_region` VALUES ('986', '933', '绍兴', '绍兴市', '中国,浙江省,绍兴市', '2', 'shaoxing', '0575', '312000', 'S', '120.582112', '29.997117'); +INSERT INTO `yoshop_region` VALUES ('987', '986', '越城', '越城区', '中国,浙江省,绍兴市,越城区', '3', 'yuecheng', '0575', '312000', 'Y', '120.5819', '29.98895'); +INSERT INTO `yoshop_region` VALUES ('988', '986', '柯桥', '柯桥区', '中国,浙江省,绍兴市,柯桥区', '3', 'keqiao', '0575', '312030', 'K', '120.492736', '30.08763'); +INSERT INTO `yoshop_region` VALUES ('989', '986', '上虞', '上虞区', '中国,浙江省,绍兴市,上虞区', '3', 'shangyu', '0575', '312300', 'S', '120.476075', '30.078038'); +INSERT INTO `yoshop_region` VALUES ('990', '986', '新昌', '新昌县', '中国,浙江省,绍兴市,新昌县', '3', 'xinchang', '0575', '312500', 'X', '120.90435', '29.49991'); +INSERT INTO `yoshop_region` VALUES ('991', '986', '诸暨', '诸暨市', '中国,浙江省,绍兴市,诸暨市', '3', 'zhuji', '0575', '311800', 'Z', '120.23629', '29.71358'); +INSERT INTO `yoshop_region` VALUES ('992', '986', '嵊州', '嵊州市', '中国,浙江省,绍兴市,嵊州市', '3', 'shengzhou', '0575', '312400', null, '120.82174', '29.58854'); +INSERT INTO `yoshop_region` VALUES ('993', '933', '金华', '金华市', '中国,浙江省,金华市', '2', 'jinhua', '0579', '321000', 'J', '119.649506', '29.089524'); +INSERT INTO `yoshop_region` VALUES ('994', '993', '婺城', '婺城区', '中国,浙江省,金华市,婺城区', '3', 'wucheng', '0579', '321000', null, '119.57135', '29.09521'); +INSERT INTO `yoshop_region` VALUES ('995', '993', '金东', '金东区', '中国,浙江省,金华市,金东区', '3', 'jindong', '0579', '321000', 'J', '119.69302', '29.0991'); +INSERT INTO `yoshop_region` VALUES ('996', '993', '武义', '武义县', '中国,浙江省,金华市,武义县', '3', 'wuyi', '0579', '321200', 'W', '119.8164', '28.89331'); +INSERT INTO `yoshop_region` VALUES ('997', '993', '浦江', '浦江县', '中国,浙江省,金华市,浦江县', '3', 'pujiang', '0579', '322200', 'P', '119.89181', '29.45353'); +INSERT INTO `yoshop_region` VALUES ('998', '993', '磐安', '磐安县', '中国,浙江省,金华市,磐安县', '3', 'pan\'an', '0579', '322300', 'P', '120.45022', '29.05733'); +INSERT INTO `yoshop_region` VALUES ('999', '993', '兰溪', '兰溪市', '中国,浙江省,金华市,兰溪市', '3', 'lanxi', '0579', '321100', 'L', '119.45965', '29.20841'); +INSERT INTO `yoshop_region` VALUES ('1000', '993', '义乌', '义乌市', '中国,浙江省,金华市,义乌市', '3', 'yiwu', '0579', '322000', 'Y', '120.0744', '29.30558'); +INSERT INTO `yoshop_region` VALUES ('1001', '993', '东阳', '东阳市', '中国,浙江省,金华市,东阳市', '3', 'dongyang', '0579', '322100', 'D', '120.24185', '29.28942'); +INSERT INTO `yoshop_region` VALUES ('1002', '993', '永康', '永康市', '中国,浙江省,金华市,永康市', '3', 'yongkang', '0579', '321300', 'Y', '120.04727', '28.88844'); +INSERT INTO `yoshop_region` VALUES ('1003', '933', '衢州', '衢州市', '中国,浙江省,衢州市', '2', 'quzhou', '0570', '324002', null, '118.87263', '28.941708'); +INSERT INTO `yoshop_region` VALUES ('1004', '1003', '柯城', '柯城区', '中国,浙江省,衢州市,柯城区', '3', 'kecheng', '0570', '324100', 'K', '118.87109', '28.96858'); +INSERT INTO `yoshop_region` VALUES ('1005', '1003', '衢江', '衢江区', '中国,浙江省,衢州市,衢江区', '3', 'qujiang', '0570', '324022', null, '118.9598', '28.97977'); +INSERT INTO `yoshop_region` VALUES ('1006', '1003', '常山', '常山县', '中国,浙江省,衢州市,常山县', '3', 'changshan', '0570', '324200', 'C', '118.51025', '28.90191'); +INSERT INTO `yoshop_region` VALUES ('1007', '1003', '开化', '开化县', '中国,浙江省,衢州市,开化县', '3', 'kaihua', '0570', '324300', 'K', '118.41616', '29.13785'); +INSERT INTO `yoshop_region` VALUES ('1008', '1003', '龙游', '龙游县', '中国,浙江省,衢州市,龙游县', '3', 'longyou', '0570', '324400', 'L', '119.17221', '29.02823'); +INSERT INTO `yoshop_region` VALUES ('1009', '1003', '江山', '江山市', '中国,浙江省,衢州市,江山市', '3', 'jiangshan', '0570', '324100', 'J', '118.62674', '28.7386'); +INSERT INTO `yoshop_region` VALUES ('1010', '933', '舟山', '舟山市', '中国,浙江省,舟山市', '2', 'zhoushan', '0580', '316000', 'Z', '122.106863', '30.016028'); +INSERT INTO `yoshop_region` VALUES ('1011', '1010', '定海', '定海区', '中国,浙江省,舟山市,定海区', '3', 'dinghai', '0580', '316000', 'D', '122.10677', '30.01985'); +INSERT INTO `yoshop_region` VALUES ('1012', '1010', '普陀', '普陀区', '中国,浙江省,舟山市,普陀区', '3', 'putuo', '0580', '316100', 'P', '122.30278', '29.94908'); +INSERT INTO `yoshop_region` VALUES ('1013', '1010', '岱山', '岱山县', '中国,浙江省,舟山市,岱山县', '3', 'daishan', '0580', '316200', null, '122.20486', '30.24385'); +INSERT INTO `yoshop_region` VALUES ('1014', '1010', '嵊泗', '嵊泗县', '中国,浙江省,舟山市,嵊泗县', '3', 'shengsi', '0580', '202450', null, '122.45129', '30.72678'); +INSERT INTO `yoshop_region` VALUES ('1015', '933', '台州', '台州市', '中国,浙江省,台州市', '2', 'taizhou', '0576', '318000', 'T', '121.428599', '28.661378'); +INSERT INTO `yoshop_region` VALUES ('1016', '1015', '椒江', '椒江区', '中国,浙江省,台州市,椒江区', '3', 'jiaojiang', '0576', '318000', 'J', '121.44287', '28.67301'); +INSERT INTO `yoshop_region` VALUES ('1017', '1015', '黄岩', '黄岩区', '中国,浙江省,台州市,黄岩区', '3', 'huangyan', '0576', '318020', 'H', '121.25891', '28.65077'); +INSERT INTO `yoshop_region` VALUES ('1018', '1015', '路桥', '路桥区', '中国,浙江省,台州市,路桥区', '3', 'luqiao', '0576', '318050', 'L', '121.37381', '28.58016'); +INSERT INTO `yoshop_region` VALUES ('1019', '1015', '玉环', '玉环县', '中国,浙江省,台州市,玉环县', '3', 'yuhuan', '0576', '317600', 'Y', '121.23242', '28.13637'); +INSERT INTO `yoshop_region` VALUES ('1020', '1015', '三门', '三门县', '中国,浙江省,台州市,三门县', '3', 'sanmen', '0576', '317100', 'S', '121.3937', '29.1051'); +INSERT INTO `yoshop_region` VALUES ('1021', '1015', '天台', '天台县', '中国,浙江省,台州市,天台县', '3', 'tiantai', '0576', '317200', 'T', '121.00848', '29.1429'); +INSERT INTO `yoshop_region` VALUES ('1022', '1015', '仙居', '仙居县', '中国,浙江省,台州市,仙居县', '3', 'xianju', '0576', '317300', 'X', '120.72872', '28.84672'); +INSERT INTO `yoshop_region` VALUES ('1023', '1015', '温岭', '温岭市', '中国,浙江省,台州市,温岭市', '3', 'wenling', '0576', '317500', 'W', '121.38595', '28.37176'); +INSERT INTO `yoshop_region` VALUES ('1024', '1015', '临海', '临海市', '中国,浙江省,台州市,临海市', '3', 'linhai', '0576', '317000', 'L', '121.13885', '28.85603'); +INSERT INTO `yoshop_region` VALUES ('1025', '933', '丽水', '丽水市', '中国,浙江省,丽水市', '2', 'lishui', '0578', '323000', 'L', '119.921786', '28.451993'); +INSERT INTO `yoshop_region` VALUES ('1026', '1025', '莲都', '莲都区', '中国,浙江省,丽水市,莲都区', '3', 'liandu', '0578', '323000', 'L', '119.9127', '28.44583'); +INSERT INTO `yoshop_region` VALUES ('1027', '1025', '青田', '青田县', '中国,浙江省,丽水市,青田县', '3', 'qingtian', '0578', '323900', 'Q', '120.29028', '28.13897'); +INSERT INTO `yoshop_region` VALUES ('1028', '1025', '缙云', '缙云县', '中国,浙江省,丽水市,缙云县', '3', 'jinyun', '0578', '321400', null, '120.09036', '28.65944'); +INSERT INTO `yoshop_region` VALUES ('1029', '1025', '遂昌', '遂昌县', '中国,浙江省,丽水市,遂昌县', '3', 'suichang', '0578', '323300', 'S', '119.27606', '28.59291'); +INSERT INTO `yoshop_region` VALUES ('1030', '1025', '松阳', '松阳县', '中国,浙江省,丽水市,松阳县', '3', 'songyang', '0578', '323400', 'S', '119.48199', '28.4494'); +INSERT INTO `yoshop_region` VALUES ('1031', '1025', '云和', '云和县', '中国,浙江省,丽水市,云和县', '3', 'yunhe', '0578', '323600', 'Y', '119.57287', '28.11643'); +INSERT INTO `yoshop_region` VALUES ('1032', '1025', '庆元', '庆元县', '中国,浙江省,丽水市,庆元县', '3', 'qingyuan', '0578', '323800', 'Q', '119.06256', '27.61842'); +INSERT INTO `yoshop_region` VALUES ('1033', '1025', '景宁', '景宁畲族自治县', '中国,浙江省,丽水市,景宁畲族自治县', '3', 'jingning', '0578', '323500', 'J', '119.63839', '27.97393'); +INSERT INTO `yoshop_region` VALUES ('1034', '1025', '龙泉', '龙泉市', '中国,浙江省,丽水市,龙泉市', '3', 'longquan', '0578', '323700', 'L', '119.14163', '28.0743'); +INSERT INTO `yoshop_region` VALUES ('1035', '933', '舟山新区', '舟山群岛新区', '中国,浙江省,舟山群岛新区', '2', 'zhoushan', '0580', '316000', 'Z', '122.317657', '29.813242'); +INSERT INTO `yoshop_region` VALUES ('1036', '1035', '金塘', '金塘岛', '中国,浙江省,舟山群岛新区,金塘岛', '3', 'jintang', '0580', '316000', 'J', '121.893373', '30.040641'); +INSERT INTO `yoshop_region` VALUES ('1037', '1035', '六横', '六横岛', '中国,浙江省,舟山群岛新区,六横岛', '3', 'liuheng', '0580', '316000', 'L', '122.14265', '29.662938'); +INSERT INTO `yoshop_region` VALUES ('1038', '1035', '衢山', '衢山岛', '中国,浙江省,舟山群岛新区,衢山岛', '3', 'qushan', '0580', '316000', null, '122.358425', '30.442642'); +INSERT INTO `yoshop_region` VALUES ('1039', '1035', '舟山', '舟山本岛西北部', '中国,浙江省,舟山群岛新区,舟山本岛西北部', '3', 'zhoushan', '0580', '316000', 'Z', '122.03064', '30.140377'); +INSERT INTO `yoshop_region` VALUES ('1040', '1035', '岱山', '岱山岛西南部', '中国,浙江省,舟山群岛新区,岱山岛西南部', '3', 'daishan', '0580', '316000', null, '122.180123', '30.277269'); +INSERT INTO `yoshop_region` VALUES ('1041', '1035', '泗礁', '泗礁岛', '中国,浙江省,舟山群岛新区,泗礁岛', '3', 'sijiao', '0580', '316000', null, '122.45803', '30.725112'); +INSERT INTO `yoshop_region` VALUES ('1042', '1035', '朱家尖', '朱家尖岛', '中国,浙江省,舟山群岛新区,朱家尖岛', '3', 'zhujiajian', '0580', '316000', 'Z', '122.390636', '29.916303'); +INSERT INTO `yoshop_region` VALUES ('1043', '1035', '洋山', '洋山岛', '中国,浙江省,舟山群岛新区,洋山岛', '3', 'yangshan', '0580', '316000', 'Y', '121.995891', '30.094637'); +INSERT INTO `yoshop_region` VALUES ('1044', '1035', '长涂', '长涂岛', '中国,浙江省,舟山群岛新区,长涂岛', '3', 'changtu', '0580', '316000', 'C', '122.284681', '30.24888'); +INSERT INTO `yoshop_region` VALUES ('1045', '1035', '虾峙', '虾峙岛', '中国,浙江省,舟山群岛新区,虾峙岛', '3', 'xiazhi', '0580', '316000', 'X', '122.244686', '29.752941'); +INSERT INTO `yoshop_region` VALUES ('1046', '0', '安徽', '安徽省', '中国,安徽省', '1', 'anhui', '', '', 'A', '117.283042', '31.86119'); +INSERT INTO `yoshop_region` VALUES ('1047', '1046', '合肥', '合肥市', '中国,安徽省,合肥市', '2', 'hefei', '0551', '230001', 'H', '117.283042', '31.86119'); +INSERT INTO `yoshop_region` VALUES ('1048', '1047', '瑶海', '瑶海区', '中国,安徽省,合肥市,瑶海区', '3', 'yaohai', '0551', '230011', 'Y', '117.30947', '31.85809'); +INSERT INTO `yoshop_region` VALUES ('1049', '1047', '庐阳', '庐阳区', '中国,安徽省,合肥市,庐阳区', '3', 'luyang', '0551', '230001', 'L', '117.26452', '31.87874'); +INSERT INTO `yoshop_region` VALUES ('1050', '1047', '蜀山', '蜀山区', '中国,安徽省,合肥市,蜀山区', '3', 'shushan', '0551', '230031', 'S', '117.26104', '31.85117'); +INSERT INTO `yoshop_region` VALUES ('1051', '1047', '包河', '包河区', '中国,安徽省,合肥市,包河区', '3', 'baohe', '0551', '230041', 'B', '117.30984', '31.79502'); +INSERT INTO `yoshop_region` VALUES ('1052', '1047', '长丰', '长丰县', '中国,安徽省,合肥市,长丰县', '3', 'changfeng', '0551', '231100', 'C', '117.16549', '32.47959'); +INSERT INTO `yoshop_region` VALUES ('1053', '1047', '肥东', '肥东县', '中国,安徽省,合肥市,肥东县', '3', 'feidong', '0551', '231600', 'F', '117.47128', '31.88525'); +INSERT INTO `yoshop_region` VALUES ('1054', '1047', '肥西', '肥西县', '中国,安徽省,合肥市,肥西县', '3', 'feixi', '0551', '231200', 'F', '117.16845', '31.72143'); +INSERT INTO `yoshop_region` VALUES ('1055', '1047', '庐江', '庐江县', '中国,安徽省,合肥市,庐江县', '3', 'lujiang', '0565', '231500', 'L', '117.289844', '31.251488'); +INSERT INTO `yoshop_region` VALUES ('1056', '1047', '巢湖', '巢湖市', '中国,安徽省,合肥市,巢湖市', '3', 'chaohu', '0565', '238000', 'C', '117.874155', '31.600518'); +INSERT INTO `yoshop_region` VALUES ('1057', '1046', '芜湖', '芜湖市', '中国,安徽省,芜湖市', '2', 'wuhu', '0551', '241000', 'W', '118.376451', '31.326319'); +INSERT INTO `yoshop_region` VALUES ('1058', '1057', '镜湖', '镜湖区', '中国,安徽省,芜湖市,镜湖区', '3', 'jinghu', '0553', '241000', 'J', '118.38525', '31.34038'); +INSERT INTO `yoshop_region` VALUES ('1059', '1057', '弋江', '弋江区', '中国,安徽省,芜湖市,弋江区', '3', 'yijiang', '0553', '241000', null, '118.37265', '31.31178'); +INSERT INTO `yoshop_region` VALUES ('1060', '1057', '鸠江', '鸠江区', '中国,安徽省,芜湖市,鸠江区', '3', 'jiujiang', '0553', '241000', null, '118.39215', '31.36928'); +INSERT INTO `yoshop_region` VALUES ('1061', '1057', '三山', '三山区', '中国,安徽省,芜湖市,三山区', '3', 'sanshan', '0553', '241000', 'S', '118.22509', '31.20703'); +INSERT INTO `yoshop_region` VALUES ('1062', '1057', '芜湖', '芜湖县', '中国,安徽省,芜湖市,芜湖县', '3', 'wuhu', '0553', '241100', 'W', '118.57525', '31.13476'); +INSERT INTO `yoshop_region` VALUES ('1063', '1057', '繁昌', '繁昌县', '中国,安徽省,芜湖市,繁昌县', '3', 'fanchang', '0553', '241200', 'F', '118.19982', '31.08319'); +INSERT INTO `yoshop_region` VALUES ('1064', '1057', '南陵', '南陵县', '中国,安徽省,芜湖市,南陵县', '3', 'nanling', '0553', '242400', 'N', '118.33688', '30.91969'); +INSERT INTO `yoshop_region` VALUES ('1065', '1057', '无为', '无为县', '中国,安徽省,芜湖市,无为县', '3', 'wuwei', '0565', '238300', 'W', '117.911432', '31.303075'); +INSERT INTO `yoshop_region` VALUES ('1066', '1046', '蚌埠', '蚌埠市', '中国,安徽省,蚌埠市', '2', 'bengbu', '0552', '233000', 'B', '117.36237', '32.934037'); +INSERT INTO `yoshop_region` VALUES ('1067', '1066', '龙子湖', '龙子湖区', '中国,安徽省,蚌埠市,龙子湖区', '3', 'longzihu', '0552', '233000', 'L', '117.39379', '32.94301'); +INSERT INTO `yoshop_region` VALUES ('1068', '1066', '蚌山', '蚌山区', '中国,安徽省,蚌埠市,蚌山区', '3', 'bengshan', '0552', '233000', 'B', '117.36767', '32.94411'); +INSERT INTO `yoshop_region` VALUES ('1069', '1066', '禹会', '禹会区', '中国,安徽省,蚌埠市,禹会区', '3', 'yuhui', '0552', '233010', 'Y', '117.35315', '32.93336'); +INSERT INTO `yoshop_region` VALUES ('1070', '1066', '淮上', '淮上区', '中国,安徽省,蚌埠市,淮上区', '3', 'huaishang', '0552', '233002', 'H', '117.35983', '32.96423'); +INSERT INTO `yoshop_region` VALUES ('1071', '1066', '怀远', '怀远县', '中国,安徽省,蚌埠市,怀远县', '3', 'huaiyuan', '0552', '233400', 'H', '117.20507', '32.97007'); +INSERT INTO `yoshop_region` VALUES ('1072', '1066', '五河', '五河县', '中国,安徽省,蚌埠市,五河县', '3', 'wuhe', '0552', '233300', 'W', '117.89144', '33.14457'); +INSERT INTO `yoshop_region` VALUES ('1073', '1066', '固镇', '固镇县', '中国,安徽省,蚌埠市,固镇县', '3', 'guzhen', '0552', '233700', 'G', '117.31558', '33.31803'); +INSERT INTO `yoshop_region` VALUES ('1074', '1046', '淮南', '淮南市', '中国,安徽省,淮南市', '2', 'huainan', '0554', '232001', 'H', '117.025449', '32.645947'); +INSERT INTO `yoshop_region` VALUES ('1075', '1074', '大通', '大通区', '中国,安徽省,淮南市,大通区', '3', 'datong', '0554', '232033', 'D', '117.05255', '32.63265'); +INSERT INTO `yoshop_region` VALUES ('1076', '1074', '田家庵', '田家庵区', '中国,安徽省,淮南市,田家庵区', '3', 'tianjiaan', '0554', '232000', 'T', '117.01739', '32.64697'); +INSERT INTO `yoshop_region` VALUES ('1077', '1074', '谢家集', '谢家集区', '中国,安徽省,淮南市,谢家集区', '3', 'xiejiaji', '0554', '232052', 'X', '116.86377', '32.59818'); +INSERT INTO `yoshop_region` VALUES ('1078', '1074', '八公山', '八公山区', '中国,安徽省,淮南市,八公山区', '3', 'bagongshan', '0554', '232072', 'B', '116.83694', '32.62941'); +INSERT INTO `yoshop_region` VALUES ('1079', '1074', '潘集', '潘集区', '中国,安徽省,淮南市,潘集区', '3', 'panji', '0554', '232082', 'P', '116.81622', '32.78287'); +INSERT INTO `yoshop_region` VALUES ('1080', '1074', '凤台', '凤台县', '中国,安徽省,淮南市,凤台县', '3', 'fengtai', '0554', '232100', 'F', '116.71569', '32.70752'); +INSERT INTO `yoshop_region` VALUES ('1081', '1046', '马鞍山', '马鞍山市', '中国,安徽省,马鞍山市', '2', 'ma\'anshan', '0555', '243001', 'M', '118.507906', '31.689362'); +INSERT INTO `yoshop_region` VALUES ('1082', '1081', '花山', '花山区', '中国,安徽省,马鞍山市,花山区', '3', 'huashan', '0555', '243000', 'H', '118.51231', '31.7001'); +INSERT INTO `yoshop_region` VALUES ('1083', '1081', '雨山', '雨山区', '中国,安徽省,马鞍山市,雨山区', '3', 'yushan', '0555', '243071', 'Y', '118.49869', '31.68219'); +INSERT INTO `yoshop_region` VALUES ('1084', '1081', '博望', '博望区', '中国,安徽省,马鞍山市,博望区', '3', 'bowang', '0555', '243131', 'B', '118.844387', '31.561871'); +INSERT INTO `yoshop_region` VALUES ('1085', '1081', '当涂', '当涂县', '中国,安徽省,马鞍山市,当涂县', '3', 'dangtu', '0555', '243100', 'D', '118.49786', '31.57098'); +INSERT INTO `yoshop_region` VALUES ('1086', '1081', '含山', '含山县', '中国,安徽省,马鞍山市,含山县', '3', 'hanshan', '0555', '238100', 'H', '118.105545', '31.727758'); +INSERT INTO `yoshop_region` VALUES ('1087', '1081', '和县', '和县', '中国,安徽省,马鞍山市,和县', '3', 'hexian', '0555', '238200', 'H', '118.351405', '31.741794'); +INSERT INTO `yoshop_region` VALUES ('1088', '1046', '淮北', '淮北市', '中国,安徽省,淮北市', '2', 'huaibei', '0561', '235000', 'H', '116.794664', '33.971707'); +INSERT INTO `yoshop_region` VALUES ('1089', '1088', '杜集', '杜集区', '中国,安徽省,淮北市,杜集区', '3', 'duji', '0561', '235000', 'D', '116.82998', '33.99363'); +INSERT INTO `yoshop_region` VALUES ('1090', '1088', '相山', '相山区', '中国,安徽省,淮北市,相山区', '3', 'xiangshan', '0561', '235000', 'X', '116.79464', '33.95979'); +INSERT INTO `yoshop_region` VALUES ('1091', '1088', '烈山', '烈山区', '中国,安徽省,淮北市,烈山区', '3', 'lieshan', '0561', '235000', 'L', '116.81448', '33.89355'); +INSERT INTO `yoshop_region` VALUES ('1092', '1088', '濉溪', '濉溪县', '中国,安徽省,淮北市,濉溪县', '3', 'suixi', '0561', '235100', null, '116.76785', '33.91455'); +INSERT INTO `yoshop_region` VALUES ('1093', '1046', '铜陵', '铜陵市', '中国,安徽省,铜陵市', '2', 'tongling', '0562', '244000', 'T', '117.816576', '30.929935'); +INSERT INTO `yoshop_region` VALUES ('1094', '1093', '铜官山', '铜官山区', '中国,安徽省,铜陵市,铜官山区', '3', 'tongguanshan', '0562', '244000', 'T', '117.81525', '30.93423'); +INSERT INTO `yoshop_region` VALUES ('1095', '1093', '狮子山', '狮子山区', '中国,安徽省,铜陵市,狮子山区', '3', 'shizishan', '0562', '244000', 'S', '117.89178', '30.92631'); +INSERT INTO `yoshop_region` VALUES ('1096', '1093', '郊区', '郊区', '中国,安徽省,铜陵市,郊区', '3', 'jiaoqu', '0562', '244000', 'J', '117.80868', '30.91976'); +INSERT INTO `yoshop_region` VALUES ('1097', '1093', '铜陵', '铜陵县', '中国,安徽省,铜陵市,铜陵县', '3', 'tongling', '0562', '244100', 'T', '117.79113', '30.95365'); +INSERT INTO `yoshop_region` VALUES ('1098', '1046', '安庆', '安庆市', '中国,安徽省,安庆市', '2', 'anqing', '0556', '246001', 'A', '117.053571', '30.524816'); +INSERT INTO `yoshop_region` VALUES ('1099', '1098', '迎江', '迎江区', '中国,安徽省,安庆市,迎江区', '3', 'yingjiang', '0556', '246001', 'Y', '117.0493', '30.50421'); +INSERT INTO `yoshop_region` VALUES ('1100', '1098', '大观', '大观区', '中国,安徽省,安庆市,大观区', '3', 'daguan', '0556', '246002', 'D', '117.03426', '30.51216'); +INSERT INTO `yoshop_region` VALUES ('1101', '1098', '宜秀', '宜秀区', '中国,安徽省,安庆市,宜秀区', '3', 'yixiu', '0556', '246003', 'Y', '117.06127', '30.50783'); +INSERT INTO `yoshop_region` VALUES ('1102', '1098', '怀宁', '怀宁县', '中国,安徽省,安庆市,怀宁县', '3', 'huaining', '0556', '246100', 'H', '116.82968', '30.73376'); +INSERT INTO `yoshop_region` VALUES ('1103', '1098', '枞阳', '枞阳县', '中国,安徽省,安庆市,枞阳县', '3', 'zongyang', '0556', '246700', null, '117.22015', '30.69956'); +INSERT INTO `yoshop_region` VALUES ('1104', '1098', '潜山', '潜山县', '中国,安徽省,安庆市,潜山县', '3', 'qianshan', '0556', '246300', 'Q', '116.57574', '30.63037'); +INSERT INTO `yoshop_region` VALUES ('1105', '1098', '太湖', '太湖县', '中国,安徽省,安庆市,太湖县', '3', 'taihu', '0556', '246400', 'T', '116.3088', '30.4541'); +INSERT INTO `yoshop_region` VALUES ('1106', '1098', '宿松', '宿松县', '中国,安徽省,安庆市,宿松县', '3', 'susong', '0556', '246500', 'S', '116.12915', '30.1536'); +INSERT INTO `yoshop_region` VALUES ('1107', '1098', '望江', '望江县', '中国,安徽省,安庆市,望江县', '3', 'wangjiang', '0556', '246200', 'W', '116.68814', '30.12585'); +INSERT INTO `yoshop_region` VALUES ('1108', '1098', '岳西', '岳西县', '中国,安徽省,安庆市,岳西县', '3', 'yuexi', '0556', '246600', 'Y', '116.35995', '30.84983'); +INSERT INTO `yoshop_region` VALUES ('1109', '1098', '桐城', '桐城市', '中国,安徽省,安庆市,桐城市', '3', 'tongcheng', '0556', '231400', 'T', '116.95071', '31.05216'); +INSERT INTO `yoshop_region` VALUES ('1110', '1046', '黄山', '黄山市', '中国,安徽省,黄山市', '2', 'huangshan', '0559', '245000', 'H', '118.317325', '29.709239'); +INSERT INTO `yoshop_region` VALUES ('1111', '1110', '屯溪', '屯溪区', '中国,安徽省,黄山市,屯溪区', '3', 'tunxi', '0559', '245000', 'T', '118.33368', '29.71138'); +INSERT INTO `yoshop_region` VALUES ('1112', '1110', '黄山', '黄山区', '中国,安徽省,黄山市,黄山区', '3', 'huangshan', '0559', '242700', 'H', '118.1416', '30.2729'); +INSERT INTO `yoshop_region` VALUES ('1113', '1110', '徽州', '徽州区', '中国,安徽省,黄山市,徽州区', '3', 'huizhou', '0559', '245061', 'H', '118.33654', '29.82784'); +INSERT INTO `yoshop_region` VALUES ('1114', '1110', '歙县', '歙县', '中国,安徽省,黄山市,歙县', '3', 'shexian', '0559', '245200', null, '118.43676', '29.86745'); +INSERT INTO `yoshop_region` VALUES ('1115', '1110', '休宁', '休宁县', '中国,安徽省,黄山市,休宁县', '3', 'xiuning', '0559', '245400', 'X', '118.18136', '29.78607'); +INSERT INTO `yoshop_region` VALUES ('1116', '1110', '黟县', '黟县', '中国,安徽省,黄山市,黟县', '3', 'yixian', '0559', '245500', null, '117.94137', '29.92588'); +INSERT INTO `yoshop_region` VALUES ('1117', '1110', '祁门', '祁门县', '中国,安徽省,黄山市,祁门县', '3', 'qimen', '0559', '245600', 'Q', '117.71847', '29.85723'); +INSERT INTO `yoshop_region` VALUES ('1118', '1046', '滁州', '滁州市', '中国,安徽省,滁州市', '2', 'chuzhou', '0550', '239000', 'C', '118.316264', '32.303627'); +INSERT INTO `yoshop_region` VALUES ('1119', '1118', '琅琊', '琅琊区', '中国,安徽省,滁州市,琅琊区', '3', 'langya', '0550', '239000', 'L', '118.30538', '32.29521'); +INSERT INTO `yoshop_region` VALUES ('1120', '1118', '南谯', '南谯区', '中国,安徽省,滁州市,南谯区', '3', 'nanqiao', '0550', '239000', 'N', '118.31222', '32.31861'); +INSERT INTO `yoshop_region` VALUES ('1121', '1118', '来安', '来安县', '中国,安徽省,滁州市,来安县', '3', 'lai\'an', '0550', '239200', 'L', '118.43438', '32.45176'); +INSERT INTO `yoshop_region` VALUES ('1122', '1118', '全椒', '全椒县', '中国,安徽省,滁州市,全椒县', '3', 'quanjiao', '0550', '239500', 'Q', '118.27291', '32.08524'); +INSERT INTO `yoshop_region` VALUES ('1123', '1118', '定远', '定远县', '中国,安徽省,滁州市,定远县', '3', 'dingyuan', '0550', '233200', 'D', '117.68035', '32.52488'); +INSERT INTO `yoshop_region` VALUES ('1124', '1118', '凤阳', '凤阳县', '中国,安徽省,滁州市,凤阳县', '3', 'fengyang', '0550', '233100', 'F', '117.56454', '32.86507'); +INSERT INTO `yoshop_region` VALUES ('1125', '1118', '天长', '天长市', '中国,安徽省,滁州市,天长市', '3', 'tianchang', '0550', '239300', 'T', '118.99868', '32.69124'); +INSERT INTO `yoshop_region` VALUES ('1126', '1118', '明光', '明光市', '中国,安徽省,滁州市,明光市', '3', 'mingguang', '0550', '239400', 'M', '117.99093', '32.77819'); +INSERT INTO `yoshop_region` VALUES ('1127', '1046', '阜阳', '阜阳市', '中国,安徽省,阜阳市', '2', 'fuyang', '0558', '236033', 'F', '115.819729', '32.896969'); +INSERT INTO `yoshop_region` VALUES ('1128', '1127', '颍州', '颍州区', '中国,安徽省,阜阳市,颍州区', '3', 'yingzhou', '0558', '236001', null, '115.80694', '32.88346'); +INSERT INTO `yoshop_region` VALUES ('1129', '1127', '颍东', '颍东区', '中国,安徽省,阜阳市,颍东区', '3', 'yingdong', '0558', '236058', null, '115.85659', '32.91296'); +INSERT INTO `yoshop_region` VALUES ('1130', '1127', '颍泉', '颍泉区', '中国,安徽省,阜阳市,颍泉区', '3', 'yingquan', '0558', '236045', null, '115.80712', '32.9249'); +INSERT INTO `yoshop_region` VALUES ('1131', '1127', '临泉', '临泉县', '中国,安徽省,阜阳市,临泉县', '3', 'linquan', '0558', '236400', 'L', '115.26232', '33.06758'); +INSERT INTO `yoshop_region` VALUES ('1132', '1127', '太和', '太和县', '中国,安徽省,阜阳市,太和县', '3', 'taihe', '0558', '236600', 'T', '115.62191', '33.16025'); +INSERT INTO `yoshop_region` VALUES ('1133', '1127', '阜南', '阜南县', '中国,安徽省,阜阳市,阜南县', '3', 'funan', '0558', '236300', 'F', '115.58563', '32.63551'); +INSERT INTO `yoshop_region` VALUES ('1134', '1127', '颍上', '颍上县', '中国,安徽省,阜阳市,颍上县', '3', 'yingshang', '0558', '236200', null, '116.26458', '32.62998'); +INSERT INTO `yoshop_region` VALUES ('1135', '1127', '界首', '界首市', '中国,安徽省,阜阳市,界首市', '3', 'jieshou', '0558', '236500', 'J', '115.37445', '33.25714'); +INSERT INTO `yoshop_region` VALUES ('1136', '1046', '宿州', '宿州市', '中国,安徽省,宿州市', '2', 'suzhou', '0557', '234000', 'S', '116.984084', '33.633891'); +INSERT INTO `yoshop_region` VALUES ('1137', '1136', '埇桥', '埇桥区', '中国,安徽省,宿州市,埇桥区', '3', 'yongqiao', '0557', '234000', null, '116.97731', '33.64058'); +INSERT INTO `yoshop_region` VALUES ('1138', '1136', '砀山', '砀山县', '中国,安徽省,宿州市,砀山县', '3', 'dangshan', '0557', '235300', null, '116.35363', '34.42356'); +INSERT INTO `yoshop_region` VALUES ('1139', '1136', '萧县', '萧县', '中国,安徽省,宿州市,萧县', '3', 'xiaoxian', '0557', '235200', 'X', '116.94546', '34.1879'); +INSERT INTO `yoshop_region` VALUES ('1140', '1136', '灵璧', '灵璧县', '中国,安徽省,宿州市,灵璧县', '3', 'lingbi', '0557', '234200', 'L', '117.55813', '33.54339'); +INSERT INTO `yoshop_region` VALUES ('1141', '1136', '泗县', '泗县', '中国,安徽省,宿州市,泗县', '3', 'sixian', '0557', '234300', null, '117.91033', '33.48295'); +INSERT INTO `yoshop_region` VALUES ('1142', '1046', '六安', '六安市', '中国,安徽省,六安市', '2', 'lu\'an', '0564', '237000', 'L', '116.507676', '31.752889'); +INSERT INTO `yoshop_region` VALUES ('1143', '1142', '金安', '金安区', '中国,安徽省,六安市,金安区', '3', 'jin\'an', '0564', '237005', 'J', '116.50912', '31.75573'); +INSERT INTO `yoshop_region` VALUES ('1144', '1142', '裕安', '裕安区', '中国,安徽省,六安市,裕安区', '3', 'yu\'an', '0564', '237010', 'Y', '116.47985', '31.73787'); +INSERT INTO `yoshop_region` VALUES ('1145', '1142', '寿县', '寿县', '中国,安徽省,六安市,寿县', '3', 'shouxian', '0564', '232200', 'S', '116.78466', '32.57653'); +INSERT INTO `yoshop_region` VALUES ('1146', '1142', '霍邱', '霍邱县', '中国,安徽省,六安市,霍邱县', '3', 'huoqiu', '0564', '237400', 'H', '116.27795', '32.353'); +INSERT INTO `yoshop_region` VALUES ('1147', '1142', '舒城', '舒城县', '中国,安徽省,六安市,舒城县', '3', 'shucheng', '0564', '231300', 'S', '116.94491', '31.46413'); +INSERT INTO `yoshop_region` VALUES ('1148', '1142', '金寨', '金寨县', '中国,安徽省,六安市,金寨县', '3', 'jinzhai', '0564', '237300', 'J', '115.93463', '31.7351'); +INSERT INTO `yoshop_region` VALUES ('1149', '1142', '霍山', '霍山县', '中国,安徽省,六安市,霍山县', '3', 'huoshan', '0564', '237200', 'H', '116.33291', '31.3929'); +INSERT INTO `yoshop_region` VALUES ('1150', '1046', '亳州', '亳州市', '中国,安徽省,亳州市', '2', 'bozhou', '0558', '236802', null, '115.782939', '33.869338'); +INSERT INTO `yoshop_region` VALUES ('1151', '1150', '谯城', '谯城区', '中国,安徽省,亳州市,谯城区', '3', 'qiaocheng', '0558', '236800', null, '115.77941', '33.87532'); +INSERT INTO `yoshop_region` VALUES ('1152', '1150', '涡阳', '涡阳县', '中国,安徽省,亳州市,涡阳县', '3', 'guoyang', '0558', '233600', 'W', '116.21682', '33.50911'); +INSERT INTO `yoshop_region` VALUES ('1153', '1150', '蒙城', '蒙城县', '中国,安徽省,亳州市,蒙城县', '3', 'mengcheng', '0558', '233500', 'M', '116.5646', '33.26477'); +INSERT INTO `yoshop_region` VALUES ('1154', '1150', '利辛', '利辛县', '中国,安徽省,亳州市,利辛县', '3', 'lixin', '0558', '236700', 'L', '116.208', '33.14198'); +INSERT INTO `yoshop_region` VALUES ('1155', '1046', '池州', '池州市', '中国,安徽省,池州市', '2', 'chizhou', '0566', '247100', 'C', '117.489157', '30.656037'); +INSERT INTO `yoshop_region` VALUES ('1156', '1155', '贵池', '贵池区', '中国,安徽省,池州市,贵池区', '3', 'guichi', '0566', '247100', 'G', '117.48722', '30.65283'); +INSERT INTO `yoshop_region` VALUES ('1157', '1155', '东至', '东至县', '中国,安徽省,池州市,东至县', '3', 'dongzhi', '0566', '247200', 'D', '117.02719', '30.0969'); +INSERT INTO `yoshop_region` VALUES ('1158', '1155', '石台', '石台县', '中国,安徽省,池州市,石台县', '3', 'shitai', '0566', '245100', 'S', '117.48666', '30.21042'); +INSERT INTO `yoshop_region` VALUES ('1159', '1155', '青阳', '青阳县', '中国,安徽省,池州市,青阳县', '3', 'qingyang', '0566', '242800', 'Q', '117.84744', '30.63932'); +INSERT INTO `yoshop_region` VALUES ('1160', '1046', '宣城', '宣城市', '中国,安徽省,宣城市', '2', 'xuancheng', '0563', '242000', 'X', '118.757995', '30.945667'); +INSERT INTO `yoshop_region` VALUES ('1161', '1160', '宣州', '宣州区', '中国,安徽省,宣城市,宣州区', '3', 'xuanzhou', '0563', '242000', 'X', '118.75462', '30.94439'); +INSERT INTO `yoshop_region` VALUES ('1162', '1160', '郎溪', '郎溪县', '中国,安徽省,宣城市,郎溪县', '3', 'langxi', '0563', '242100', 'L', '119.17923', '31.12599'); +INSERT INTO `yoshop_region` VALUES ('1163', '1160', '广德', '广德县', '中国,安徽省,宣城市,广德县', '3', 'guangde', '0563', '242200', 'G', '119.41769', '30.89371'); +INSERT INTO `yoshop_region` VALUES ('1164', '1160', '泾县', '泾县', '中国,安徽省,宣城市,泾县', '3', 'jingxian', '0563', '242500', null, '118.41964', '30.69498'); +INSERT INTO `yoshop_region` VALUES ('1165', '1160', '绩溪', '绩溪县', '中国,安徽省,宣城市,绩溪县', '3', 'jixi', '0563', '245300', 'J', '118.59765', '30.07069'); +INSERT INTO `yoshop_region` VALUES ('1166', '1160', '旌德', '旌德县', '中国,安徽省,宣城市,旌德县', '3', 'jingde', '0563', '242600', null, '118.54299', '30.28898'); +INSERT INTO `yoshop_region` VALUES ('1167', '1160', '宁国', '宁国市', '中国,安徽省,宣城市,宁国市', '3', 'ningguo', '0563', '242300', 'N', '118.98349', '30.6238'); +INSERT INTO `yoshop_region` VALUES ('1168', '0', '福建', '福建省', '中国,福建省', '1', 'fujian', '', '', 'F', '119.306239', '26.075302'); +INSERT INTO `yoshop_region` VALUES ('1169', '1168', '福州', '福州市', '中国,福建省,福州市', '2', 'fuzhou', '0591', '350001', 'F', '119.306239', '26.075302'); +INSERT INTO `yoshop_region` VALUES ('1170', '1169', '鼓楼', '鼓楼区', '中国,福建省,福州市,鼓楼区', '3', 'gulou', '0591', '350001', 'G', '119.30384', '26.08225'); +INSERT INTO `yoshop_region` VALUES ('1171', '1169', '台江', '台江区', '中国,福建省,福州市,台江区', '3', 'taijiang', '0591', '350004', 'T', '119.30899', '26.06204'); +INSERT INTO `yoshop_region` VALUES ('1172', '1169', '仓山', '仓山区', '中国,福建省,福州市,仓山区', '3', 'cangshan', '0591', '350007', 'C', '119.31543', '26.04335'); +INSERT INTO `yoshop_region` VALUES ('1173', '1169', '马尾', '马尾区', '中国,福建省,福州市,马尾区', '3', 'mawei', '0591', '350015', 'M', '119.4555', '25.98942'); +INSERT INTO `yoshop_region` VALUES ('1174', '1169', '晋安', '晋安区', '中国,福建省,福州市,晋安区', '3', 'jin\'an', '0591', '350011', 'J', '119.32828', '26.0818'); +INSERT INTO `yoshop_region` VALUES ('1175', '1169', '闽侯', '闽侯县', '中国,福建省,福州市,闽侯县', '3', 'minhou', '0591', '350100', 'M', '119.13388', '26.15014'); +INSERT INTO `yoshop_region` VALUES ('1176', '1169', '连江', '连江县', '中国,福建省,福州市,连江县', '3', 'lianjiang', '0591', '350500', 'L', '119.53433', '26.19466'); +INSERT INTO `yoshop_region` VALUES ('1177', '1169', '罗源', '罗源县', '中国,福建省,福州市,罗源县', '3', 'luoyuan', '0591', '350600', 'L', '119.5509', '26.48752'); +INSERT INTO `yoshop_region` VALUES ('1178', '1169', '闽清', '闽清县', '中国,福建省,福州市,闽清县', '3', 'minqing', '0591', '350800', 'M', '118.8623', '26.21901'); +INSERT INTO `yoshop_region` VALUES ('1179', '1169', '永泰', '永泰县', '中国,福建省,福州市,永泰县', '3', 'yongtai', '0591', '350700', 'Y', '118.936', '25.86816'); +INSERT INTO `yoshop_region` VALUES ('1180', '1169', '平潭', '平潭县', '中国,福建省,福州市,平潭县', '3', 'pingtan', '0591', '350400', 'P', '119.791197', '25.503672'); +INSERT INTO `yoshop_region` VALUES ('1181', '1169', '福清', '福清市', '中国,福建省,福州市,福清市', '3', 'fuqing', '0591', '350300', 'F', '119.38507', '25.72086'); +INSERT INTO `yoshop_region` VALUES ('1182', '1169', '长乐', '长乐市', '中国,福建省,福州市,长乐市', '3', 'changle', '0591', '350200', 'C', '119.52313', '25.96276'); +INSERT INTO `yoshop_region` VALUES ('1183', '1168', '厦门', '厦门市', '中国,福建省,厦门市', '2', 'xiamen', '0592', '361003', 'X', '118.11022', '24.490474'); +INSERT INTO `yoshop_region` VALUES ('1184', '1183', '思明', '思明区', '中国,福建省,厦门市,思明区', '3', 'siming', '0592', '361001', 'S', '118.08233', '24.44543'); +INSERT INTO `yoshop_region` VALUES ('1185', '1183', '海沧', '海沧区', '中国,福建省,厦门市,海沧区', '3', 'haicang', '0592', '361026', 'H', '118.03289', '24.48461'); +INSERT INTO `yoshop_region` VALUES ('1186', '1183', '湖里', '湖里区', '中国,福建省,厦门市,湖里区', '3', 'huli', '0592', '361006', 'H', '118.14621', '24.51253'); +INSERT INTO `yoshop_region` VALUES ('1187', '1183', '集美', '集美区', '中国,福建省,厦门市,集美区', '3', 'jimei', '0592', '361021', 'J', '118.09719', '24.57584'); +INSERT INTO `yoshop_region` VALUES ('1188', '1183', '同安', '同安区', '中国,福建省,厦门市,同安区', '3', 'tong\'an', '0592', '361100', 'T', '118.15197', '24.72308'); +INSERT INTO `yoshop_region` VALUES ('1189', '1183', '翔安', '翔安区', '中国,福建省,厦门市,翔安区', '3', 'xiang\'an', '0592', '361101', 'X', '118.24783', '24.61863'); +INSERT INTO `yoshop_region` VALUES ('1190', '1168', '莆田', '莆田市', '中国,福建省,莆田市', '2', 'putian', '0594', '351100', 'P', '119.007558', '25.431011'); +INSERT INTO `yoshop_region` VALUES ('1191', '1190', '城厢', '城厢区', '中国,福建省,莆田市,城厢区', '3', 'chengxiang', '0594', '351100', 'C', '118.99462', '25.41872'); +INSERT INTO `yoshop_region` VALUES ('1192', '1190', '涵江', '涵江区', '中国,福建省,莆田市,涵江区', '3', 'hanjiang', '0594', '351111', 'H', '119.11621', '25.45876'); +INSERT INTO `yoshop_region` VALUES ('1193', '1190', '荔城', '荔城区', '中国,福建省,莆田市,荔城区', '3', 'licheng', '0594', '351100', 'L', '119.01339', '25.43369'); +INSERT INTO `yoshop_region` VALUES ('1194', '1190', '秀屿', '秀屿区', '中国,福建省,莆田市,秀屿区', '3', 'xiuyu', '0594', '351152', 'X', '119.10553', '25.31831'); +INSERT INTO `yoshop_region` VALUES ('1195', '1190', '仙游', '仙游县', '中国,福建省,莆田市,仙游县', '3', 'xianyou', '0594', '351200', 'X', '118.69177', '25.36214'); +INSERT INTO `yoshop_region` VALUES ('1196', '1168', '三明', '三明市', '中国,福建省,三明市', '2', 'sanming', '0598', '365000', 'S', '117.635001', '26.265444'); +INSERT INTO `yoshop_region` VALUES ('1197', '1196', '梅列', '梅列区', '中国,福建省,三明市,梅列区', '3', 'meilie', '0598', '365000', 'M', '117.64585', '26.27171'); +INSERT INTO `yoshop_region` VALUES ('1198', '1196', '三元', '三元区', '中国,福建省,三明市,三元区', '3', 'sanyuan', '0598', '365001', 'S', '117.60788', '26.23372'); +INSERT INTO `yoshop_region` VALUES ('1199', '1196', '明溪', '明溪县', '中国,福建省,三明市,明溪县', '3', 'mingxi', '0598', '365200', 'M', '117.20498', '26.35294'); +INSERT INTO `yoshop_region` VALUES ('1200', '1196', '清流', '清流县', '中国,福建省,三明市,清流县', '3', 'qingliu', '0598', '365300', 'Q', '116.8146', '26.17144'); +INSERT INTO `yoshop_region` VALUES ('1201', '1196', '宁化', '宁化县', '中国,福建省,三明市,宁化县', '3', 'ninghua', '0598', '365400', 'N', '116.66101', '26.25874'); +INSERT INTO `yoshop_region` VALUES ('1202', '1196', '大田', '大田县', '中国,福建省,三明市,大田县', '3', 'datian', '0598', '366100', 'D', '117.8471', '25.6926'); +INSERT INTO `yoshop_region` VALUES ('1203', '1196', '尤溪', '尤溪县', '中国,福建省,三明市,尤溪县', '3', 'youxi', '0598', '365100', 'Y', '118.19049', '26.17002'); +INSERT INTO `yoshop_region` VALUES ('1204', '1196', '沙县', '沙县', '中国,福建省,三明市,沙县', '3', 'shaxian', '0598', '365500', 'S', '117.79266', '26.39615'); +INSERT INTO `yoshop_region` VALUES ('1205', '1196', '将乐', '将乐县', '中国,福建省,三明市,将乐县', '3', 'jiangle', '0598', '353300', 'J', '117.47317', '26.72837'); +INSERT INTO `yoshop_region` VALUES ('1206', '1196', '泰宁', '泰宁县', '中国,福建省,三明市,泰宁县', '3', 'taining', '0598', '354400', 'T', '117.17578', '26.9001'); +INSERT INTO `yoshop_region` VALUES ('1207', '1196', '建宁', '建宁县', '中国,福建省,三明市,建宁县', '3', 'jianning', '0598', '354500', 'J', '116.84603', '26.83091'); +INSERT INTO `yoshop_region` VALUES ('1208', '1196', '永安', '永安市', '中国,福建省,三明市,永安市', '3', 'yong\'an', '0598', '366000', 'Y', '117.36517', '25.94136'); +INSERT INTO `yoshop_region` VALUES ('1209', '1168', '泉州', '泉州市', '中国,福建省,泉州市', '2', 'quanzhou', '0595', '362000', 'Q', '118.589421', '24.908853'); +INSERT INTO `yoshop_region` VALUES ('1210', '1209', '鲤城', '鲤城区', '中国,福建省,泉州市,鲤城区', '3', 'licheng', '0595', '362000', 'L', '118.56591', '24.88741'); +INSERT INTO `yoshop_region` VALUES ('1211', '1209', '丰泽', '丰泽区', '中国,福建省,泉州市,丰泽区', '3', 'fengze', '0595', '362000', 'F', '118.61328', '24.89119'); +INSERT INTO `yoshop_region` VALUES ('1212', '1209', '洛江', '洛江区', '中国,福建省,泉州市,洛江区', '3', 'luojiang', '0595', '362011', 'L', '118.67111', '24.93984'); +INSERT INTO `yoshop_region` VALUES ('1213', '1209', '泉港', '泉港区', '中国,福建省,泉州市,泉港区', '3', 'quangang', '0595', '362114', 'Q', '118.91586', '25.12005'); +INSERT INTO `yoshop_region` VALUES ('1214', '1209', '惠安', '惠安县', '中国,福建省,泉州市,惠安县', '3', 'hui\'an', '0595', '362100', 'H', '118.79687', '25.03059'); +INSERT INTO `yoshop_region` VALUES ('1215', '1209', '安溪', '安溪县', '中国,福建省,泉州市,安溪县', '3', 'anxi', '0595', '362400', 'A', '118.18719', '25.05627'); +INSERT INTO `yoshop_region` VALUES ('1216', '1209', '永春', '永春县', '中国,福建省,泉州市,永春县', '3', 'yongchun', '0595', '362600', 'Y', '118.29437', '25.32183'); +INSERT INTO `yoshop_region` VALUES ('1217', '1209', '德化', '德化县', '中国,福建省,泉州市,德化县', '3', 'dehua', '0595', '362500', 'D', '118.24176', '25.49224'); +INSERT INTO `yoshop_region` VALUES ('1218', '1209', '金门', '金门县', '中国,福建省,泉州市,金门县', '3', 'jinmen', '', '', 'J', '118.32263', '24.42922'); +INSERT INTO `yoshop_region` VALUES ('1219', '1209', '石狮', '石狮市', '中国,福建省,泉州市,石狮市', '3', 'shishi', '0595', '362700', 'S', '118.64779', '24.73242'); +INSERT INTO `yoshop_region` VALUES ('1220', '1209', '晋江', '晋江市', '中国,福建省,泉州市,晋江市', '3', 'jinjiang', '0595', '362200', 'J', '118.55194', '24.78141'); +INSERT INTO `yoshop_region` VALUES ('1221', '1209', '南安', '南安市', '中国,福建省,泉州市,南安市', '3', 'nan\'an', '0595', '362300', 'N', '118.38589', '24.96055'); +INSERT INTO `yoshop_region` VALUES ('1222', '1168', '漳州', '漳州市', '中国,福建省,漳州市', '2', 'zhangzhou', '0596', '363005', 'Z', '117.661801', '24.510897'); +INSERT INTO `yoshop_region` VALUES ('1223', '1222', '芗城', '芗城区', '中国,福建省,漳州市,芗城区', '3', 'xiangcheng', '0596', '363000', null, '117.65402', '24.51081'); +INSERT INTO `yoshop_region` VALUES ('1224', '1222', '龙文', '龙文区', '中国,福建省,漳州市,龙文区', '3', 'longwen', '0596', '363005', 'L', '117.70971', '24.50323'); +INSERT INTO `yoshop_region` VALUES ('1225', '1222', '云霄', '云霄县', '中国,福建省,漳州市,云霄县', '3', 'yunxiao', '0596', '363300', 'Y', '117.34051', '23.95534'); +INSERT INTO `yoshop_region` VALUES ('1226', '1222', '漳浦', '漳浦县', '中国,福建省,漳州市,漳浦县', '3', 'zhangpu', '0596', '363200', 'Z', '117.61367', '24.11706'); +INSERT INTO `yoshop_region` VALUES ('1227', '1222', '诏安', '诏安县', '中国,福建省,漳州市,诏安县', '3', 'zhao\'an', '0596', '363500', null, '117.17501', '23.71148'); +INSERT INTO `yoshop_region` VALUES ('1228', '1222', '长泰', '长泰县', '中国,福建省,漳州市,长泰县', '3', 'changtai', '0596', '363900', 'C', '117.75924', '24.62526'); +INSERT INTO `yoshop_region` VALUES ('1229', '1222', '东山', '东山县', '中国,福建省,漳州市,东山县', '3', 'dongshan', '0596', '363400', 'D', '117.42822', '23.70109'); +INSERT INTO `yoshop_region` VALUES ('1230', '1222', '南靖', '南靖县', '中国,福建省,漳州市,南靖县', '3', 'nanjing', '0596', '363600', 'N', '117.35736', '24.51448'); +INSERT INTO `yoshop_region` VALUES ('1231', '1222', '平和', '平和县', '中国,福建省,漳州市,平和县', '3', 'pinghe', '0596', '363700', 'P', '117.3124', '24.36395'); +INSERT INTO `yoshop_region` VALUES ('1232', '1222', '华安', '华安县', '中国,福建省,漳州市,华安县', '3', 'hua\'an', '0596', '363800', 'H', '117.54077', '25.00563'); +INSERT INTO `yoshop_region` VALUES ('1233', '1222', '龙海', '龙海市', '中国,福建省,漳州市,龙海市', '3', 'longhai', '0596', '363100', 'L', '117.81802', '24.44655'); +INSERT INTO `yoshop_region` VALUES ('1234', '1168', '南平', '南平市', '中国,福建省,南平市', '2', 'nanping', '0599', '353000', 'N', '118.178459', '26.635627'); +INSERT INTO `yoshop_region` VALUES ('1235', '1234', '延平', '延平区', '中国,福建省,南平市,延平区', '3', 'yanping', '0600', '353000', 'Y', '118.18189', '26.63745'); +INSERT INTO `yoshop_region` VALUES ('1236', '1234', '建阳', '建阳区', '中国,福建省,南平市,建阳区', '3', 'jianyang', '0599', '354200', 'J', '118.12267', '27.332067'); +INSERT INTO `yoshop_region` VALUES ('1237', '1234', '顺昌', '顺昌县', '中国,福建省,南平市,顺昌县', '3', 'shunchang', '0605', '353200', 'S', '117.8103', '26.79298'); +INSERT INTO `yoshop_region` VALUES ('1238', '1234', '浦城', '浦城县', '中国,福建省,南平市,浦城县', '3', 'pucheng', '0606', '353400', 'P', '118.54007', '27.91888'); +INSERT INTO `yoshop_region` VALUES ('1239', '1234', '光泽', '光泽县', '中国,福建省,南平市,光泽县', '3', 'guangze', '0607', '354100', 'G', '117.33346', '27.54231'); +INSERT INTO `yoshop_region` VALUES ('1240', '1234', '松溪', '松溪县', '中国,福建省,南平市,松溪县', '3', 'songxi', '0608', '353500', 'S', '118.78533', '27.52624'); +INSERT INTO `yoshop_region` VALUES ('1241', '1234', '政和', '政和县', '中国,福建省,南平市,政和县', '3', 'zhenghe', '0609', '353600', 'Z', '118.85571', '27.36769'); +INSERT INTO `yoshop_region` VALUES ('1242', '1234', '邵武', '邵武市', '中国,福建省,南平市,邵武市', '3', 'shaowu', '0601', '354000', 'S', '117.4924', '27.34033'); +INSERT INTO `yoshop_region` VALUES ('1243', '1234', '武夷山', '武夷山市', '中国,福建省,南平市,武夷山市', '3', 'wuyishan', '0602', '354300', 'W', '118.03665', '27.75543'); +INSERT INTO `yoshop_region` VALUES ('1244', '1234', '建瓯', '建瓯市', '中国,福建省,南平市,建瓯市', '3', 'jianou', '0603', '353100', 'J', '118.29766', '27.02301'); +INSERT INTO `yoshop_region` VALUES ('1245', '1168', '龙岩', '龙岩市', '中国,福建省,龙岩市', '2', 'longyan', '0597', '364000', 'L', '117.02978', '25.091603'); +INSERT INTO `yoshop_region` VALUES ('1246', '1245', '新罗', '新罗区', '中国,福建省,龙岩市,新罗区', '3', 'xinluo', '0597', '364000', 'X', '117.03693', '25.09834'); +INSERT INTO `yoshop_region` VALUES ('1247', '1245', '长汀', '长汀县', '中国,福建省,龙岩市,长汀县', '3', 'changting', '0597', '366300', 'C', '116.35888', '25.82773'); +INSERT INTO `yoshop_region` VALUES ('1248', '1245', '永定', '永定区', '中国,福建省,龙岩市,永定区', '3', 'yongding', '0597', '364100', 'Y', '116.73199', '24.72302'); +INSERT INTO `yoshop_region` VALUES ('1249', '1245', '上杭', '上杭县', '中国,福建省,龙岩市,上杭县', '3', 'shanghang', '0597', '364200', 'S', '116.42022', '25.04943'); +INSERT INTO `yoshop_region` VALUES ('1250', '1245', '武平', '武平县', '中国,福建省,龙岩市,武平县', '3', 'wuping', '0597', '364300', 'W', '116.10229', '25.09244'); +INSERT INTO `yoshop_region` VALUES ('1251', '1245', '连城', '连城县', '中国,福建省,龙岩市,连城县', '3', 'liancheng', '0597', '366200', 'L', '116.75454', '25.7103'); +INSERT INTO `yoshop_region` VALUES ('1252', '1245', '漳平', '漳平市', '中国,福建省,龙岩市,漳平市', '3', 'zhangping', '0597', '364400', 'Z', '117.41992', '25.29109'); +INSERT INTO `yoshop_region` VALUES ('1253', '1168', '宁德', '宁德市', '中国,福建省,宁德市', '2', 'ningde', '0593', '352100', 'N', '119.527082', '26.65924'); +INSERT INTO `yoshop_region` VALUES ('1254', '1253', '蕉城', '蕉城区', '中国,福建省,宁德市,蕉城区', '3', 'jiaocheng', '0593', '352100', 'J', '119.52643', '26.66048'); +INSERT INTO `yoshop_region` VALUES ('1255', '1253', '霞浦', '霞浦县', '中国,福建省,宁德市,霞浦县', '3', 'xiapu', '0593', '355100', 'X', '119.99893', '26.88578'); +INSERT INTO `yoshop_region` VALUES ('1256', '1253', '古田', '古田县', '中国,福建省,宁德市,古田县', '3', 'gutian', '0593', '352200', 'G', '118.74688', '26.57682'); +INSERT INTO `yoshop_region` VALUES ('1257', '1253', '屏南', '屏南县', '中国,福建省,宁德市,屏南县', '3', 'pingnan', '0593', '352300', 'P', '118.98861', '26.91099'); +INSERT INTO `yoshop_region` VALUES ('1258', '1253', '寿宁', '寿宁县', '中国,福建省,宁德市,寿宁县', '3', 'shouning', '0593', '355500', 'S', '119.5039', '27.45996'); +INSERT INTO `yoshop_region` VALUES ('1259', '1253', '周宁', '周宁县', '中国,福建省,宁德市,周宁县', '3', 'zhouning', '0593', '355400', 'Z', '119.33837', '27.10664'); +INSERT INTO `yoshop_region` VALUES ('1260', '1253', '柘荣', '柘荣县', '中国,福建省,宁德市,柘荣县', '3', 'zherong', '0593', '355300', null, '119.89971', '27.23543'); +INSERT INTO `yoshop_region` VALUES ('1261', '1253', '福安', '福安市', '中国,福建省,宁德市,福安市', '3', 'fu\'an', '0593', '355000', 'F', '119.6495', '27.08673'); +INSERT INTO `yoshop_region` VALUES ('1262', '1253', '福鼎', '福鼎市', '中国,福建省,宁德市,福鼎市', '3', 'fuding', '0593', '355200', 'F', '120.21664', '27.3243'); +INSERT INTO `yoshop_region` VALUES ('1263', '0', '江西', '江西省', '中国,江西省', '1', 'jiangxi', '', '', 'J', '115.892151', '28.676493'); +INSERT INTO `yoshop_region` VALUES ('1264', '1263', '南昌', '南昌市', '中国,江西省,南昌市', '2', 'nanchang', '0791', '330008', 'N', '115.892151', '28.676493'); +INSERT INTO `yoshop_region` VALUES ('1265', '1264', '东湖', '东湖区', '中国,江西省,南昌市,东湖区', '3', 'donghu', '0791', '330006', 'D', '115.8988', '28.68505'); +INSERT INTO `yoshop_region` VALUES ('1266', '1264', '西湖', '西湖区', '中国,江西省,南昌市,西湖区', '3', 'xihu', '0791', '330009', 'X', '115.87728', '28.65688'); +INSERT INTO `yoshop_region` VALUES ('1267', '1264', '青云谱', '青云谱区', '中国,江西省,南昌市,青云谱区', '3', 'qingyunpu', '0791', '330001', 'Q', '115.915', '28.63199'); +INSERT INTO `yoshop_region` VALUES ('1268', '1264', '湾里', '湾里区', '中国,江西省,南昌市,湾里区', '3', 'wanli', '0791', '330004', 'W', '115.73104', '28.71529'); +INSERT INTO `yoshop_region` VALUES ('1269', '1264', '青山湖', '青山湖区', '中国,江西省,南昌市,青山湖区', '3', 'qingshanhu', '0791', '330029', 'Q', '115.9617', '28.68206'); +INSERT INTO `yoshop_region` VALUES ('1270', '1264', '南昌', '南昌县', '中国,江西省,南昌市,南昌县', '3', 'nanchang', '0791', '330200', 'N', '115.94393', '28.54559'); +INSERT INTO `yoshop_region` VALUES ('1271', '1264', '新建', '新建县', '中国,江西省,南昌市,新建县', '3', 'xinjian', '0791', '330100', 'X', '115.81546', '28.69248'); +INSERT INTO `yoshop_region` VALUES ('1272', '1264', '安义', '安义县', '中国,江西省,南昌市,安义县', '3', 'anyi', '0791', '330500', 'A', '115.54879', '28.84602'); +INSERT INTO `yoshop_region` VALUES ('1273', '1264', '进贤', '进贤县', '中国,江西省,南昌市,进贤县', '3', 'jinxian', '0791', '331700', 'J', '116.24087', '28.37679'); +INSERT INTO `yoshop_region` VALUES ('1274', '1263', '景德镇', '景德镇市', '中国,江西省,景德镇市', '2', 'jingdezhen', '0798', '333000', 'J', '117.214664', '29.29256'); +INSERT INTO `yoshop_region` VALUES ('1275', '1274', '昌江', '昌江区', '中国,江西省,景德镇市,昌江区', '3', 'changjiang', '0799', '333000', 'C', '117.18359', '29.27321'); +INSERT INTO `yoshop_region` VALUES ('1276', '1274', '珠山', '珠山区', '中国,江西省,景德镇市,珠山区', '3', 'zhushan', '0800', '333000', 'Z', '117.20233', '29.30127'); +INSERT INTO `yoshop_region` VALUES ('1277', '1274', '浮梁', '浮梁县', '中国,江西省,景德镇市,浮梁县', '3', 'fuliang', '0802', '333400', 'F', '117.21517', '29.35156'); +INSERT INTO `yoshop_region` VALUES ('1278', '1274', '乐平', '乐平市', '中国,江西省,景德镇市,乐平市', '3', 'leping', '0801', '333300', 'L', '117.12887', '28.96295'); +INSERT INTO `yoshop_region` VALUES ('1279', '1263', '萍乡', '萍乡市', '中国,江西省,萍乡市', '2', 'pingxiang', '0799', '337000', 'P', '113.852186', '27.622946'); +INSERT INTO `yoshop_region` VALUES ('1280', '1279', '安源', '安源区', '中国,江西省,萍乡市,安源区', '3', 'anyuan', '0800', '337000', 'A', '113.89135', '27.61653'); +INSERT INTO `yoshop_region` VALUES ('1281', '1279', '湘东', '湘东区', '中国,江西省,萍乡市,湘东区', '3', 'xiangdong', '0801', '337016', 'X', '113.73294', '27.64007'); +INSERT INTO `yoshop_region` VALUES ('1282', '1279', '莲花', '莲花县', '中国,江西省,萍乡市,莲花县', '3', 'lianhua', '0802', '337100', 'L', '113.96142', '27.12866'); +INSERT INTO `yoshop_region` VALUES ('1283', '1279', '上栗', '上栗县', '中国,江西省,萍乡市,上栗县', '3', 'shangli', '0803', '337009', 'S', '113.79403', '27.87467'); +INSERT INTO `yoshop_region` VALUES ('1284', '1279', '芦溪', '芦溪县', '中国,江西省,萍乡市,芦溪县', '3', 'luxi', '0804', '337053', 'L', '114.02951', '27.63063'); +INSERT INTO `yoshop_region` VALUES ('1285', '1263', '九江', '九江市', '中国,江西省,九江市', '2', 'jiujiang', '0792', '332000', 'J', '115.992811', '29.712034'); +INSERT INTO `yoshop_region` VALUES ('1286', '1285', '庐山', '庐山区', '中国,江西省,九江市,庐山区', '3', 'lushan', '0792', '332005', 'L', '115.98904', '29.67177'); +INSERT INTO `yoshop_region` VALUES ('1287', '1285', '浔阳', '浔阳区', '中国,江西省,九江市,浔阳区', '3', 'xunyang', '0792', '332000', null, '115.98986', '29.72786'); +INSERT INTO `yoshop_region` VALUES ('1288', '1285', '九江', '九江县', '中国,江西省,九江市,九江县', '3', 'jiujiang', '0792', '332100', 'J', '115.91128', '29.60852'); +INSERT INTO `yoshop_region` VALUES ('1289', '1285', '武宁', '武宁县', '中国,江西省,九江市,武宁县', '3', 'wuning', '0792', '332300', 'W', '115.10061', '29.2584'); +INSERT INTO `yoshop_region` VALUES ('1290', '1285', '修水', '修水县', '中国,江西省,九江市,修水县', '3', 'xiushui', '0792', '332400', 'X', '114.54684', '29.02539'); +INSERT INTO `yoshop_region` VALUES ('1291', '1285', '永修', '永修县', '中国,江西省,九江市,永修县', '3', 'yongxiu', '0792', '330300', 'Y', '115.80911', '29.02093'); +INSERT INTO `yoshop_region` VALUES ('1292', '1285', '德安', '德安县', '中国,江西省,九江市,德安县', '3', 'de\'an', '0792', '330400', 'D', '115.75601', '29.31341'); +INSERT INTO `yoshop_region` VALUES ('1293', '1285', '星子', '星子县', '中国,江西省,九江市,星子县', '3', 'xingzi', '0792', '332800', 'X', '116.04492', '29.44608'); +INSERT INTO `yoshop_region` VALUES ('1294', '1285', '都昌', '都昌县', '中国,江西省,九江市,都昌县', '3', 'duchang', '0792', '332600', 'D', '116.20401', '29.27327'); +INSERT INTO `yoshop_region` VALUES ('1295', '1285', '湖口', '湖口县', '中国,江西省,九江市,湖口县', '3', 'hukou', '0792', '332500', 'H', '116.21853', '29.73818'); +INSERT INTO `yoshop_region` VALUES ('1296', '1285', '彭泽', '彭泽县', '中国,江西省,九江市,彭泽县', '3', 'pengze', '0792', '332700', 'P', '116.55011', '29.89589'); +INSERT INTO `yoshop_region` VALUES ('1297', '1285', '瑞昌', '瑞昌市', '中国,江西省,九江市,瑞昌市', '3', 'ruichang', '0792', '332200', 'R', '115.66705', '29.67183'); +INSERT INTO `yoshop_region` VALUES ('1298', '1285', '共青城', '共青城市', '中国,江西省,九江市,共青城市', '3', 'gongqingcheng', '0792', '332020', 'G', '115.801939', '29.238785'); +INSERT INTO `yoshop_region` VALUES ('1299', '1263', '新余', '新余市', '中国,江西省,新余市', '2', 'xinyu', '0790', '338025', 'X', '114.930835', '27.810834'); +INSERT INTO `yoshop_region` VALUES ('1300', '1299', '渝水', '渝水区', '中国,江西省,新余市,渝水区', '3', 'yushui', '0790', '338025', 'Y', '114.944', '27.80098'); +INSERT INTO `yoshop_region` VALUES ('1301', '1299', '分宜', '分宜县', '中国,江西省,新余市,分宜县', '3', 'fenyi', '0790', '336600', 'F', '114.69189', '27.81475'); +INSERT INTO `yoshop_region` VALUES ('1302', '1263', '鹰潭', '鹰潭市', '中国,江西省,鹰潭市', '2', 'yingtan', '0701', '335000', 'Y', '117.033838', '28.238638'); +INSERT INTO `yoshop_region` VALUES ('1303', '1302', '月湖', '月湖区', '中国,江西省,鹰潭市,月湖区', '3', 'yuehu', '0701', '335000', 'Y', '117.03732', '28.23913'); +INSERT INTO `yoshop_region` VALUES ('1304', '1302', '余江', '余江县', '中国,江西省,鹰潭市,余江县', '3', 'yujiang', '0701', '335200', 'Y', '116.81851', '28.21034'); +INSERT INTO `yoshop_region` VALUES ('1305', '1302', '贵溪', '贵溪市', '中国,江西省,鹰潭市,贵溪市', '3', 'guixi', '0701', '335400', 'G', '117.24246', '28.2926'); +INSERT INTO `yoshop_region` VALUES ('1306', '1263', '赣州', '赣州市', '中国,江西省,赣州市', '2', 'ganzhou', '0797', '341000', 'G', '114.940278', '25.85097'); +INSERT INTO `yoshop_region` VALUES ('1307', '1306', '章贡', '章贡区', '中国,江西省,赣州市,章贡区', '3', 'zhanggong', '0797', '341000', 'Z', '114.94284', '25.8624'); +INSERT INTO `yoshop_region` VALUES ('1308', '1306', '南康', '南康区', '中国,江西省,赣州市,南康区', '3', 'nankang', '0797', '341400', 'N', '114.756933', '25.661721'); +INSERT INTO `yoshop_region` VALUES ('1309', '1306', '赣县', '赣县', '中国,江西省,赣州市,赣县', '3', 'ganxian', '0797', '341100', 'G', '115.01171', '25.86149'); +INSERT INTO `yoshop_region` VALUES ('1310', '1306', '信丰', '信丰县', '中国,江西省,赣州市,信丰县', '3', 'xinfeng', '0797', '341600', 'X', '114.92279', '25.38612'); +INSERT INTO `yoshop_region` VALUES ('1311', '1306', '大余', '大余县', '中国,江西省,赣州市,大余县', '3', 'dayu', '0797', '341500', 'D', '114.35757', '25.39561'); +INSERT INTO `yoshop_region` VALUES ('1312', '1306', '上犹', '上犹县', '中国,江西省,赣州市,上犹县', '3', 'shangyou', '0797', '341200', 'S', '114.54138', '25.79567'); +INSERT INTO `yoshop_region` VALUES ('1313', '1306', '崇义', '崇义县', '中国,江西省,赣州市,崇义县', '3', 'chongyi', '0797', '341300', 'C', '114.30835', '25.68186'); +INSERT INTO `yoshop_region` VALUES ('1314', '1306', '安远', '安远县', '中国,江西省,赣州市,安远县', '3', 'anyuan', '0797', '342100', 'A', '115.39483', '25.1371'); +INSERT INTO `yoshop_region` VALUES ('1315', '1306', '龙南', '龙南县', '中国,江西省,赣州市,龙南县', '3', 'longnan', '0797', '341700', 'L', '114.78994', '24.91086'); +INSERT INTO `yoshop_region` VALUES ('1316', '1306', '定南', '定南县', '中国,江西省,赣州市,定南县', '3', 'dingnan', '0797', '341900', 'D', '115.02713', '24.78395'); +INSERT INTO `yoshop_region` VALUES ('1317', '1306', '全南', '全南县', '中国,江西省,赣州市,全南县', '3', 'quannan', '0797', '341800', 'Q', '114.5292', '24.74324'); +INSERT INTO `yoshop_region` VALUES ('1318', '1306', '宁都', '宁都县', '中国,江西省,赣州市,宁都县', '3', 'ningdu', '0797', '342800', 'N', '116.01565', '26.47227'); +INSERT INTO `yoshop_region` VALUES ('1319', '1306', '于都', '于都县', '中国,江西省,赣州市,于都县', '3', 'yudu', '0797', '342300', 'Y', '115.41415', '25.95257'); +INSERT INTO `yoshop_region` VALUES ('1320', '1306', '兴国', '兴国县', '中国,江西省,赣州市,兴国县', '3', 'xingguo', '0797', '342400', 'X', '115.36309', '26.33776'); +INSERT INTO `yoshop_region` VALUES ('1321', '1306', '会昌', '会昌县', '中国,江西省,赣州市,会昌县', '3', 'huichang', '0797', '342600', 'H', '115.78555', '25.60068'); +INSERT INTO `yoshop_region` VALUES ('1322', '1306', '寻乌', '寻乌县', '中国,江西省,赣州市,寻乌县', '3', 'xunwu', '0797', '342200', 'X', '115.64852', '24.95513'); +INSERT INTO `yoshop_region` VALUES ('1323', '1306', '石城', '石城县', '中国,江西省,赣州市,石城县', '3', 'shicheng', '0797', '342700', 'S', '116.3442', '26.32617'); +INSERT INTO `yoshop_region` VALUES ('1324', '1306', '瑞金', '瑞金市', '中国,江西省,赣州市,瑞金市', '3', 'ruijin', '0797', '342500', 'R', '116.02703', '25.88557'); +INSERT INTO `yoshop_region` VALUES ('1325', '1263', '吉安', '吉安市', '中国,江西省,吉安市', '2', 'ji\'an', '0796', '343000', 'J', '114.986373', '27.111699'); +INSERT INTO `yoshop_region` VALUES ('1326', '1325', '吉州', '吉州区', '中国,江西省,吉安市,吉州区', '3', 'jizhou', '0796', '343000', 'J', '114.97598', '27.10669'); +INSERT INTO `yoshop_region` VALUES ('1327', '1325', '青原', '青原区', '中国,江西省,吉安市,青原区', '3', 'qingyuan', '0796', '343009', 'Q', '115.01747', '27.10577'); +INSERT INTO `yoshop_region` VALUES ('1328', '1325', '吉安', '吉安县', '中国,江西省,吉安市,吉安县', '3', 'ji\'an', '0796', '343100', 'J', '114.90695', '27.04048'); +INSERT INTO `yoshop_region` VALUES ('1329', '1325', '吉水', '吉水县', '中国,江西省,吉安市,吉水县', '3', 'jishui', '0796', '331600', 'J', '115.1343', '27.21071'); +INSERT INTO `yoshop_region` VALUES ('1330', '1325', '峡江', '峡江县', '中国,江西省,吉安市,峡江县', '3', 'xiajiang', '0796', '331409', 'X', '115.31723', '27.576'); +INSERT INTO `yoshop_region` VALUES ('1331', '1325', '新干', '新干县', '中国,江西省,吉安市,新干县', '3', 'xingan', '0796', '331300', 'X', '115.39306', '27.74092'); +INSERT INTO `yoshop_region` VALUES ('1332', '1325', '永丰', '永丰县', '中国,江西省,吉安市,永丰县', '3', 'yongfeng', '0796', '331500', 'Y', '115.44238', '27.31785'); +INSERT INTO `yoshop_region` VALUES ('1333', '1325', '泰和', '泰和县', '中国,江西省,吉安市,泰和县', '3', 'taihe', '0796', '343700', 'T', '114.90789', '26.79113'); +INSERT INTO `yoshop_region` VALUES ('1334', '1325', '遂川', '遂川县', '中国,江西省,吉安市,遂川县', '3', 'suichuan', '0796', '343900', 'S', '114.51629', '26.32598'); +INSERT INTO `yoshop_region` VALUES ('1335', '1325', '万安', '万安县', '中国,江西省,吉安市,万安县', '3', 'wan\'an', '0796', '343800', 'W', '114.78659', '26.45931'); +INSERT INTO `yoshop_region` VALUES ('1336', '1325', '安福', '安福县', '中国,江西省,吉安市,安福县', '3', 'anfu', '0796', '343200', 'A', '114.61956', '27.39276'); +INSERT INTO `yoshop_region` VALUES ('1337', '1325', '永新', '永新县', '中国,江西省,吉安市,永新县', '3', 'yongxin', '0796', '343400', 'Y', '114.24246', '26.94488'); +INSERT INTO `yoshop_region` VALUES ('1338', '1325', '井冈山', '井冈山市', '中国,江西省,吉安市,井冈山市', '3', 'jinggangshan', '0796', '343600', 'J', '114.28949', '26.74804'); +INSERT INTO `yoshop_region` VALUES ('1339', '1263', '宜春', '宜春市', '中国,江西省,宜春市', '2', 'yichun', '0795', '336000', 'Y', '114.391136', '27.8043'); +INSERT INTO `yoshop_region` VALUES ('1340', '1339', '袁州', '袁州区', '中国,江西省,宜春市,袁州区', '3', 'yuanzhou', '0795', '336000', 'Y', '114.38246', '27.79649'); +INSERT INTO `yoshop_region` VALUES ('1341', '1339', '奉新', '奉新县', '中国,江西省,宜春市,奉新县', '3', 'fengxin', '0795', '330700', 'F', '115.40036', '28.6879'); +INSERT INTO `yoshop_region` VALUES ('1342', '1339', '万载', '万载县', '中国,江西省,宜春市,万载县', '3', 'wanzai', '0795', '336100', 'W', '114.4458', '28.10656'); +INSERT INTO `yoshop_region` VALUES ('1343', '1339', '上高', '上高县', '中国,江西省,宜春市,上高县', '3', 'shanggao', '0795', '336400', 'S', '114.92459', '28.23423'); +INSERT INTO `yoshop_region` VALUES ('1344', '1339', '宜丰', '宜丰县', '中国,江西省,宜春市,宜丰县', '3', 'yifeng', '0795', '336300', 'Y', '114.7803', '28.38555'); +INSERT INTO `yoshop_region` VALUES ('1345', '1339', '靖安', '靖安县', '中国,江西省,宜春市,靖安县', '3', 'jing\'an', '0795', '330600', 'J', '115.36279', '28.86167'); +INSERT INTO `yoshop_region` VALUES ('1346', '1339', '铜鼓', '铜鼓县', '中国,江西省,宜春市,铜鼓县', '3', 'tonggu', '0795', '336200', 'T', '114.37036', '28.52311'); +INSERT INTO `yoshop_region` VALUES ('1347', '1339', '丰城', '丰城市', '中国,江西省,宜春市,丰城市', '3', 'fengcheng', '0795', '331100', 'F', '115.77114', '28.15918'); +INSERT INTO `yoshop_region` VALUES ('1348', '1339', '樟树', '樟树市', '中国,江西省,宜春市,樟树市', '3', 'zhangshu', '0795', '331200', 'Z', '115.5465', '28.05332'); +INSERT INTO `yoshop_region` VALUES ('1349', '1339', '高安', '高安市', '中国,江西省,宜春市,高安市', '3', 'gao\'an', '0795', '330800', 'G', '115.3753', '28.4178'); +INSERT INTO `yoshop_region` VALUES ('1350', '1263', '抚州', '抚州市', '中国,江西省,抚州市', '2', 'fuzhou', '0794', '344000', 'F', '116.358351', '27.98385'); +INSERT INTO `yoshop_region` VALUES ('1351', '1350', '临川', '临川区', '中国,江西省,抚州市,临川区', '3', 'linchuan', '0794', '344000', 'L', '116.35919', '27.97721'); +INSERT INTO `yoshop_region` VALUES ('1352', '1350', '南城', '南城县', '中国,江西省,抚州市,南城县', '3', 'nancheng', '0794', '344700', 'N', '116.64419', '27.55381'); +INSERT INTO `yoshop_region` VALUES ('1353', '1350', '黎川', '黎川县', '中国,江西省,抚州市,黎川县', '3', 'lichuan', '0794', '344600', 'L', '116.90771', '27.28232'); +INSERT INTO `yoshop_region` VALUES ('1354', '1350', '南丰', '南丰县', '中国,江西省,抚州市,南丰县', '3', 'nanfeng', '0794', '344500', 'N', '116.5256', '27.21842'); +INSERT INTO `yoshop_region` VALUES ('1355', '1350', '崇仁', '崇仁县', '中国,江西省,抚州市,崇仁县', '3', 'chongren', '0794', '344200', 'C', '116.06021', '27.75962'); +INSERT INTO `yoshop_region` VALUES ('1356', '1350', '乐安', '乐安县', '中国,江西省,抚州市,乐安县', '3', 'le\'an', '0794', '344300', 'L', '115.83108', '27.42812'); +INSERT INTO `yoshop_region` VALUES ('1357', '1350', '宜黄', '宜黄县', '中国,江西省,抚州市,宜黄县', '3', 'yihuang', '0794', '344400', 'Y', '116.23626', '27.55487'); +INSERT INTO `yoshop_region` VALUES ('1358', '1350', '金溪', '金溪县', '中国,江西省,抚州市,金溪县', '3', 'jinxi', '0794', '344800', 'J', '116.77392', '27.90753'); +INSERT INTO `yoshop_region` VALUES ('1359', '1350', '资溪', '资溪县', '中国,江西省,抚州市,资溪县', '3', 'zixi', '0794', '335300', 'Z', '117.06939', '27.70493'); +INSERT INTO `yoshop_region` VALUES ('1360', '1350', '东乡', '东乡县', '中国,江西省,抚州市,东乡县', '3', 'dongxiang', '0794', '331800', 'D', '116.59039', '28.23614'); +INSERT INTO `yoshop_region` VALUES ('1361', '1350', '广昌', '广昌县', '中国,江西省,抚州市,广昌县', '3', 'guangchang', '0794', '344900', 'G', '116.32547', '26.8341'); +INSERT INTO `yoshop_region` VALUES ('1362', '1263', '上饶', '上饶市', '中国,江西省,上饶市', '2', 'shangrao', '0793', '334000', 'S', '117.971185', '28.44442'); +INSERT INTO `yoshop_region` VALUES ('1363', '1362', '信州', '信州区', '中国,江西省,上饶市,信州区', '3', 'xinzhou', '0793', '334000', 'X', '117.96682', '28.43121'); +INSERT INTO `yoshop_region` VALUES ('1364', '1362', '上饶', '上饶县', '中国,江西省,上饶市,上饶县', '3', 'shangrao', '0793', '334100', 'S', '117.90884', '28.44856'); +INSERT INTO `yoshop_region` VALUES ('1365', '1362', '广丰', '广丰县', '中国,江西省,上饶市,广丰县', '3', 'guangfeng', '0793', '334600', 'G', '118.19158', '28.43766'); +INSERT INTO `yoshop_region` VALUES ('1366', '1362', '玉山', '玉山县', '中国,江西省,上饶市,玉山县', '3', 'yushan', '0793', '334700', 'Y', '118.24462', '28.6818'); +INSERT INTO `yoshop_region` VALUES ('1367', '1362', '铅山', '铅山县', '中国,江西省,上饶市,铅山县', '3', 'yanshan', '0793', '334500', 'Q', '117.70996', '28.31549'); +INSERT INTO `yoshop_region` VALUES ('1368', '1362', '横峰', '横峰县', '中国,江西省,上饶市,横峰县', '3', 'hengfeng', '0793', '334300', 'H', '117.5964', '28.40716'); +INSERT INTO `yoshop_region` VALUES ('1369', '1362', '弋阳', '弋阳县', '中国,江西省,上饶市,弋阳县', '3', 'yiyang', '0793', '334400', null, '117.45929', '28.37451'); +INSERT INTO `yoshop_region` VALUES ('1370', '1362', '余干', '余干县', '中国,江西省,上饶市,余干县', '3', 'yugan', '0793', '335100', 'Y', '116.69555', '28.70206'); +INSERT INTO `yoshop_region` VALUES ('1371', '1362', '鄱阳', '鄱阳县', '中国,江西省,上饶市,鄱阳县', '3', 'poyang', '0793', '333100', null, '116.69967', '29.0118'); +INSERT INTO `yoshop_region` VALUES ('1372', '1362', '万年', '万年县', '中国,江西省,上饶市,万年县', '3', 'wannian', '0793', '335500', 'W', '117.06884', '28.69537'); +INSERT INTO `yoshop_region` VALUES ('1373', '1362', '婺源', '婺源县', '中国,江西省,上饶市,婺源县', '3', 'wuyuan', '0793', '333200', null, '117.86105', '29.24841'); +INSERT INTO `yoshop_region` VALUES ('1374', '1362', '德兴', '德兴市', '中国,江西省,上饶市,德兴市', '3', 'dexing', '0793', '334200', 'D', '117.57919', '28.94736'); +INSERT INTO `yoshop_region` VALUES ('1375', '0', '山东', '山东省', '中国,山东省', '1', 'shandong', '', '', 'S', '117.000923', '36.675807'); +INSERT INTO `yoshop_region` VALUES ('1376', '1375', '济南', '济南市', '中国,山东省,济南市', '2', 'jinan', '0531', '250001', 'J', '117.000923', '36.675807'); +INSERT INTO `yoshop_region` VALUES ('1377', '1376', '历下', '历下区', '中国,山东省,济南市,历下区', '3', 'lixia', '0531', '250014', 'L', '117.0768', '36.66661'); +INSERT INTO `yoshop_region` VALUES ('1378', '1376', '市中区', '市中区', '中国,山东省,济南市,市中区', '3', 'shizhongqu', '0531', '250001', 'S', '116.99741', '36.65101'); +INSERT INTO `yoshop_region` VALUES ('1379', '1376', '槐荫', '槐荫区', '中国,山东省,济南市,槐荫区', '3', 'huaiyin', '0531', '250117', 'H', '116.90075', '36.65136'); +INSERT INTO `yoshop_region` VALUES ('1380', '1376', '天桥', '天桥区', '中国,山东省,济南市,天桥区', '3', 'tianqiao', '0531', '250031', 'T', '116.98749', '36.67801'); +INSERT INTO `yoshop_region` VALUES ('1381', '1376', '历城', '历城区', '中国,山东省,济南市,历城区', '3', 'licheng', '0531', '250100', 'L', '117.06509', '36.67995'); +INSERT INTO `yoshop_region` VALUES ('1382', '1376', '长清', '长清区', '中国,山东省,济南市,长清区', '3', 'changqing', '0531', '250300', 'C', '116.75192', '36.55352'); +INSERT INTO `yoshop_region` VALUES ('1383', '1376', '平阴', '平阴县', '中国,山东省,济南市,平阴县', '3', 'pingyin', '0531', '250400', 'P', '116.45587', '36.28955'); +INSERT INTO `yoshop_region` VALUES ('1384', '1376', '济阳', '济阳县', '中国,山东省,济南市,济阳县', '3', 'jiyang', '0531', '251400', 'J', '117.17327', '36.97845'); +INSERT INTO `yoshop_region` VALUES ('1385', '1376', '商河', '商河县', '中国,山东省,济南市,商河县', '3', 'shanghe', '0531', '251600', 'S', '117.15722', '37.31119'); +INSERT INTO `yoshop_region` VALUES ('1386', '1376', '章丘', '章丘市', '中国,山东省,济南市,章丘市', '3', 'zhangqiu', '0531', '250200', 'Z', '117.53677', '36.71392'); +INSERT INTO `yoshop_region` VALUES ('1387', '1375', '青岛', '青岛市', '中国,山东省,青岛市', '2', 'qingdao', '0532', '266001', 'Q', '120.369557', '36.094406'); +INSERT INTO `yoshop_region` VALUES ('1388', '1387', '市南', '市南区', '中国,山东省,青岛市,市南区', '3', 'shinan', '0532', '266001', 'S', '120.38773', '36.06671'); +INSERT INTO `yoshop_region` VALUES ('1389', '1387', '市北', '市北区', '中国,山东省,青岛市,市北区', '3', 'shibei', '0532', '266011', 'S', '120.37469', '36.08734'); +INSERT INTO `yoshop_region` VALUES ('1390', '1387', '黄岛', '黄岛区', '中国,山东省,青岛市,黄岛区', '3', 'huangdao', '0532', '266500', 'H', '120.19775', '35.96065'); +INSERT INTO `yoshop_region` VALUES ('1391', '1387', '崂山', '崂山区', '中国,山东省,青岛市,崂山区', '3', 'laoshan', '0532', '266100', null, '120.46923', '36.10717'); +INSERT INTO `yoshop_region` VALUES ('1392', '1387', '李沧', '李沧区', '中国,山东省,青岛市,李沧区', '3', 'licang', '0532', '266021', 'L', '120.43286', '36.14502'); +INSERT INTO `yoshop_region` VALUES ('1393', '1387', '城阳', '城阳区', '中国,山东省,青岛市,城阳区', '3', 'chengyang', '0532', '266041', 'C', '120.39621', '36.30735'); +INSERT INTO `yoshop_region` VALUES ('1394', '1387', '胶州', '胶州市', '中国,山东省,青岛市,胶州市', '3', 'jiaozhou', '0532', '266300', 'J', '120.0335', '36.26442'); +INSERT INTO `yoshop_region` VALUES ('1395', '1387', '即墨', '即墨市', '中国,山东省,青岛市,即墨市', '3', 'jimo', '0532', '266200', 'J', '120.44699', '36.38907'); +INSERT INTO `yoshop_region` VALUES ('1396', '1387', '平度', '平度市', '中国,山东省,青岛市,平度市', '3', 'pingdu', '0532', '266700', 'P', '119.95996', '36.78688'); +INSERT INTO `yoshop_region` VALUES ('1397', '1387', '莱西', '莱西市', '中国,山东省,青岛市,莱西市', '3', 'laixi', '0532', '266600', 'L', '120.51773', '36.88804'); +INSERT INTO `yoshop_region` VALUES ('1398', '1387', '西海岸', '西海岸新区', '中国,山东省,青岛市,西海岸新区', '3', 'xihai\'an', '0532', '266500', 'X', '120.19775', '35.96065'); +INSERT INTO `yoshop_region` VALUES ('1399', '1375', '淄博', '淄博市', '中国,山东省,淄博市', '2', 'zibo', '0533', '255039', 'Z', '118.047648', '36.814939'); +INSERT INTO `yoshop_region` VALUES ('1400', '1399', '淄川', '淄川区', '中国,山东省,淄博市,淄川区', '3', 'zichuan', '0533', '255100', 'Z', '117.96655', '36.64339'); +INSERT INTO `yoshop_region` VALUES ('1401', '1399', '张店', '张店区', '中国,山东省,淄博市,张店区', '3', 'zhangdian', '0533', '255022', 'Z', '118.01788', '36.80676'); +INSERT INTO `yoshop_region` VALUES ('1402', '1399', '博山', '博山区', '中国,山东省,淄博市,博山区', '3', 'boshan', '0533', '255200', 'B', '117.86166', '36.49469'); +INSERT INTO `yoshop_region` VALUES ('1403', '1399', '临淄', '临淄区', '中国,山东省,淄博市,临淄区', '3', 'linzi', '0533', '255400', 'L', '118.30966', '36.8259'); +INSERT INTO `yoshop_region` VALUES ('1404', '1399', '周村', '周村区', '中国,山东省,淄博市,周村区', '3', 'zhoucun', '0533', '255300', 'Z', '117.86969', '36.80322'); +INSERT INTO `yoshop_region` VALUES ('1405', '1399', '桓台', '桓台县', '中国,山东省,淄博市,桓台县', '3', 'huantai', '0533', '256400', 'H', '118.09698', '36.96036'); +INSERT INTO `yoshop_region` VALUES ('1406', '1399', '高青', '高青县', '中国,山东省,淄博市,高青县', '3', 'gaoqing', '0533', '256300', 'G', '117.82708', '37.17197'); +INSERT INTO `yoshop_region` VALUES ('1407', '1399', '沂源', '沂源县', '中国,山东省,淄博市,沂源县', '3', 'yiyuan', '0533', '256100', 'Y', '118.17105', '36.18536'); +INSERT INTO `yoshop_region` VALUES ('1408', '1375', '枣庄', '枣庄市', '中国,山东省,枣庄市', '2', 'zaozhuang', '0632', '277101', 'Z', '117.557964', '34.856424'); +INSERT INTO `yoshop_region` VALUES ('1409', '1408', '市中区', '市中区', '中国,山东省,枣庄市,市中区', '3', 'shizhongqu', '0632', '277101', 'S', '117.55603', '34.86391'); +INSERT INTO `yoshop_region` VALUES ('1410', '1408', '薛城', '薛城区', '中国,山东省,枣庄市,薛城区', '3', 'xuecheng', '0632', '277000', 'X', '117.26318', '34.79498'); +INSERT INTO `yoshop_region` VALUES ('1411', '1408', '峄城', '峄城区', '中国,山东省,枣庄市,峄城区', '3', 'yicheng', '0632', '277300', null, '117.59057', '34.77225'); +INSERT INTO `yoshop_region` VALUES ('1412', '1408', '台儿庄', '台儿庄区', '中国,山东省,枣庄市,台儿庄区', '3', 'taierzhuang', '0632', '277400', 'T', '117.73452', '34.56363'); +INSERT INTO `yoshop_region` VALUES ('1413', '1408', '山亭', '山亭区', '中国,山东省,枣庄市,山亭区', '3', 'shanting', '0632', '277200', 'S', '117.4663', '35.09541'); +INSERT INTO `yoshop_region` VALUES ('1414', '1408', '滕州', '滕州市', '中国,山东省,枣庄市,滕州市', '3', 'tengzhou', '0632', '277500', null, '117.165', '35.10534'); +INSERT INTO `yoshop_region` VALUES ('1415', '1375', '东营', '东营市', '中国,山东省,东营市', '2', 'dongying', '0546', '257093', 'D', '118.4963', '37.461266'); +INSERT INTO `yoshop_region` VALUES ('1416', '1415', '东营', '东营区', '中国,山东省,东营市,东营区', '3', 'dongying', '0546', '257029', 'D', '118.5816', '37.44875'); +INSERT INTO `yoshop_region` VALUES ('1417', '1415', '河口', '河口区', '中国,山东省,东营市,河口区', '3', 'hekou', '0546', '257200', 'H', '118.5249', '37.88541'); +INSERT INTO `yoshop_region` VALUES ('1418', '1415', '垦利', '垦利县', '中国,山东省,东营市,垦利县', '3', 'kenli', '0546', '257500', 'K', '118.54815', '37.58825'); +INSERT INTO `yoshop_region` VALUES ('1419', '1415', '利津', '利津县', '中国,山东省,东营市,利津县', '3', 'lijin', '0546', '257400', 'L', '118.25637', '37.49157'); +INSERT INTO `yoshop_region` VALUES ('1420', '1415', '广饶', '广饶县', '中国,山东省,东营市,广饶县', '3', 'guangrao', '0546', '257300', 'G', '118.40704', '37.05381'); +INSERT INTO `yoshop_region` VALUES ('1421', '1375', '烟台', '烟台市', '中国,山东省,烟台市', '2', 'yantai', '0635', '264010', 'Y', '121.391382', '37.539297'); +INSERT INTO `yoshop_region` VALUES ('1422', '1421', '芝罘', '芝罘区', '中国,山东省,烟台市,芝罘区', '3', 'zhifu', '0635', '264001', 'Z', '121.40023', '37.54064'); +INSERT INTO `yoshop_region` VALUES ('1423', '1421', '福山', '福山区', '中国,山东省,烟台市,福山区', '3', 'fushan', '0635', '265500', 'F', '121.26812', '37.49841'); +INSERT INTO `yoshop_region` VALUES ('1424', '1421', '牟平', '牟平区', '中国,山东省,烟台市,牟平区', '3', 'muping', '0635', '264100', 'M', '121.60067', '37.38846'); +INSERT INTO `yoshop_region` VALUES ('1425', '1421', '莱山', '莱山区', '中国,山东省,烟台市,莱山区', '3', 'laishan', '0635', '264600', 'L', '121.44512', '37.51165'); +INSERT INTO `yoshop_region` VALUES ('1426', '1421', '长岛', '长岛县', '中国,山东省,烟台市,长岛县', '3', 'changdao', '0635', '265800', 'C', '120.738', '37.91754'); +INSERT INTO `yoshop_region` VALUES ('1427', '1421', '龙口', '龙口市', '中国,山东省,烟台市,龙口市', '3', 'longkou', '0635', '265700', 'L', '120.50634', '37.64064'); +INSERT INTO `yoshop_region` VALUES ('1428', '1421', '莱阳', '莱阳市', '中国,山东省,烟台市,莱阳市', '3', 'laiyang', '0635', '265200', 'L', '120.71066', '36.98012'); +INSERT INTO `yoshop_region` VALUES ('1429', '1421', '莱州', '莱州市', '中国,山东省,烟台市,莱州市', '3', 'laizhou', '0635', '261400', 'L', '119.94137', '37.17806'); +INSERT INTO `yoshop_region` VALUES ('1430', '1421', '蓬莱', '蓬莱市', '中国,山东省,烟台市,蓬莱市', '3', 'penglai', '0635', '265600', 'P', '120.75988', '37.81119'); +INSERT INTO `yoshop_region` VALUES ('1431', '1421', '招远', '招远市', '中国,山东省,烟台市,招远市', '3', 'zhaoyuan', '0635', '265400', 'Z', '120.40481', '37.36269'); +INSERT INTO `yoshop_region` VALUES ('1432', '1421', '栖霞', '栖霞市', '中国,山东省,烟台市,栖霞市', '3', 'qixia', '0635', '265300', 'Q', '120.85025', '37.33571'); +INSERT INTO `yoshop_region` VALUES ('1433', '1421', '海阳', '海阳市', '中国,山东省,烟台市,海阳市', '3', 'haiyang', '0635', '265100', 'H', '121.15976', '36.77622'); +INSERT INTO `yoshop_region` VALUES ('1434', '1375', '潍坊', '潍坊市', '中国,山东省,潍坊市', '2', 'weifang', '0536', '261041', 'W', '119.107078', '36.70925'); +INSERT INTO `yoshop_region` VALUES ('1435', '1434', '潍城', '潍城区', '中国,山东省,潍坊市,潍城区', '3', 'weicheng', '0536', '261021', 'W', '119.10582', '36.7139'); +INSERT INTO `yoshop_region` VALUES ('1436', '1434', '寒亭', '寒亭区', '中国,山东省,潍坊市,寒亭区', '3', 'hanting', '0536', '261100', 'H', '119.21832', '36.77504'); +INSERT INTO `yoshop_region` VALUES ('1437', '1434', '坊子', '坊子区', '中国,山东省,潍坊市,坊子区', '3', 'fangzi', '0536', '261200', 'F', '119.16476', '36.65218'); +INSERT INTO `yoshop_region` VALUES ('1438', '1434', '奎文', '奎文区', '中国,山东省,潍坊市,奎文区', '3', 'kuiwen', '0536', '261031', 'K', '119.12532', '36.70723'); +INSERT INTO `yoshop_region` VALUES ('1439', '1434', '临朐', '临朐县', '中国,山东省,潍坊市,临朐县', '3', 'linqu', '0536', '262600', 'L', '118.544', '36.51216'); +INSERT INTO `yoshop_region` VALUES ('1440', '1434', '昌乐', '昌乐县', '中国,山东省,潍坊市,昌乐县', '3', 'changle', '0536', '262400', 'C', '118.83017', '36.7078'); +INSERT INTO `yoshop_region` VALUES ('1441', '1434', '青州', '青州市', '中国,山东省,潍坊市,青州市', '3', 'qingzhou', '0536', '262500', 'Q', '118.47915', '36.68505'); +INSERT INTO `yoshop_region` VALUES ('1442', '1434', '诸城', '诸城市', '中国,山东省,潍坊市,诸城市', '3', 'zhucheng', '0536', '262200', 'Z', '119.40988', '35.99662'); +INSERT INTO `yoshop_region` VALUES ('1443', '1434', '寿光', '寿光市', '中国,山东省,潍坊市,寿光市', '3', 'shouguang', '0536', '262700', 'S', '118.74047', '36.88128'); +INSERT INTO `yoshop_region` VALUES ('1444', '1434', '安丘', '安丘市', '中国,山东省,潍坊市,安丘市', '3', 'anqiu', '0536', '262100', 'A', '119.2189', '36.47847'); +INSERT INTO `yoshop_region` VALUES ('1445', '1434', '高密', '高密市', '中国,山东省,潍坊市,高密市', '3', 'gaomi', '0536', '261500', 'G', '119.75701', '36.38397'); +INSERT INTO `yoshop_region` VALUES ('1446', '1434', '昌邑', '昌邑市', '中国,山东省,潍坊市,昌邑市', '3', 'changyi', '0536', '261300', 'C', '119.39767', '36.86008'); +INSERT INTO `yoshop_region` VALUES ('1447', '1375', '济宁', '济宁市', '中国,山东省,济宁市', '2', 'jining', '0537', '272119', 'J', '116.587245', '35.415393'); +INSERT INTO `yoshop_region` VALUES ('1448', '1447', '任城', '任城区', '中国,山东省,济宁市,任城区', '3', 'rencheng', '0537', '272113', 'R', '116.59504', '35.40659'); +INSERT INTO `yoshop_region` VALUES ('1449', '1447', '兖州', '兖州区', '中国,山东省,济宁市,兖州区', '3', 'yanzhou', '0537', '272000', null, '116.826546', '35.552305'); +INSERT INTO `yoshop_region` VALUES ('1450', '1447', '微山', '微山县', '中国,山东省,济宁市,微山县', '3', 'weishan', '0537', '277600', 'W', '117.12875', '34.80712'); +INSERT INTO `yoshop_region` VALUES ('1451', '1447', '鱼台', '鱼台县', '中国,山东省,济宁市,鱼台县', '3', 'yutai', '0537', '272300', 'Y', '116.64761', '34.99674'); +INSERT INTO `yoshop_region` VALUES ('1452', '1447', '金乡', '金乡县', '中国,山东省,济宁市,金乡县', '3', 'jinxiang', '0537', '272200', 'J', '116.31146', '35.065'); +INSERT INTO `yoshop_region` VALUES ('1453', '1447', '嘉祥', '嘉祥县', '中国,山东省,济宁市,嘉祥县', '3', 'jiaxiang', '0537', '272400', 'J', '116.34249', '35.40836'); +INSERT INTO `yoshop_region` VALUES ('1454', '1447', '汶上', '汶上县', '中国,山东省,济宁市,汶上县', '3', 'wenshang', '0537', '272501', null, '116.48742', '35.73295'); +INSERT INTO `yoshop_region` VALUES ('1455', '1447', '泗水', '泗水县', '中国,山东省,济宁市,泗水县', '3', 'sishui', '0537', '273200', null, '117.27948', '35.66113'); +INSERT INTO `yoshop_region` VALUES ('1456', '1447', '梁山', '梁山县', '中国,山东省,济宁市,梁山县', '3', 'liangshan', '0537', '272600', 'L', '116.09683', '35.80322'); +INSERT INTO `yoshop_region` VALUES ('1457', '1447', '曲阜', '曲阜市', '中国,山东省,济宁市,曲阜市', '3', 'qufu', '0537', '273100', 'Q', '116.98645', '35.58091'); +INSERT INTO `yoshop_region` VALUES ('1458', '1447', '邹城', '邹城市', '中国,山东省,济宁市,邹城市', '3', 'zoucheng', '0537', '273500', 'Z', '116.97335', '35.40531'); +INSERT INTO `yoshop_region` VALUES ('1459', '1375', '泰安', '泰安市', '中国,山东省,泰安市', '2', 'tai\'an', '0538', '271000', 'T', '117.129063', '36.194968'); +INSERT INTO `yoshop_region` VALUES ('1460', '1459', '泰山', '泰山区', '中国,山东省,泰安市,泰山区', '3', 'taishan', '0538', '271000', 'T', '117.13446', '36.19411'); +INSERT INTO `yoshop_region` VALUES ('1461', '1459', '岱岳', '岱岳区', '中国,山东省,泰安市,岱岳区', '3', 'daiyue', '0538', '271000', null, '117.04174', '36.1875'); +INSERT INTO `yoshop_region` VALUES ('1462', '1459', '宁阳', '宁阳县', '中国,山东省,泰安市,宁阳县', '3', 'ningyang', '0538', '271400', 'N', '116.80542', '35.7599'); +INSERT INTO `yoshop_region` VALUES ('1463', '1459', '东平', '东平县', '中国,山东省,泰安市,东平县', '3', 'dongping', '0538', '271500', 'D', '116.47113', '35.93792'); +INSERT INTO `yoshop_region` VALUES ('1464', '1459', '新泰', '新泰市', '中国,山东省,泰安市,新泰市', '3', 'xintai', '0538', '271200', 'X', '117.76959', '35.90887'); +INSERT INTO `yoshop_region` VALUES ('1465', '1459', '肥城', '肥城市', '中国,山东省,泰安市,肥城市', '3', 'feicheng', '0538', '271600', 'F', '116.76815', '36.18247'); +INSERT INTO `yoshop_region` VALUES ('1466', '1375', '威海', '威海市', '中国,山东省,威海市', '2', 'weihai', '0631', '264200', 'W', '122.116394', '37.509691'); +INSERT INTO `yoshop_region` VALUES ('1467', '1466', '环翠', '环翠区', '中国,山东省,威海市,环翠区', '3', 'huancui', '0631', '264200', 'H', '122.12344', '37.50199'); +INSERT INTO `yoshop_region` VALUES ('1468', '1466', '文登', '文登区', '中国,山东省,威海市,文登区', '3', 'wendeng', '0631', '266440', 'W', '122.057139', '37.196211'); +INSERT INTO `yoshop_region` VALUES ('1469', '1466', '荣成', '荣成市', '中国,山东省,威海市,荣成市', '3', 'rongcheng', '0631', '264300', 'R', '122.48773', '37.1652'); +INSERT INTO `yoshop_region` VALUES ('1470', '1466', '乳山', '乳山市', '中国,山东省,威海市,乳山市', '3', 'rushan', '0631', '264500', 'R', '121.53814', '36.91918'); +INSERT INTO `yoshop_region` VALUES ('1471', '1375', '日照', '日照市', '中国,山东省,日照市', '2', 'rizhao', '0633', '276800', 'R', '119.461208', '35.428588'); +INSERT INTO `yoshop_region` VALUES ('1472', '1471', '东港', '东港区', '中国,山东省,日照市,东港区', '3', 'donggang', '0633', '276800', 'D', '119.46237', '35.42541'); +INSERT INTO `yoshop_region` VALUES ('1473', '1471', '岚山', '岚山区', '中国,山东省,日照市,岚山区', '3', 'lanshan', '0633', '276808', null, '119.31884', '35.12203'); +INSERT INTO `yoshop_region` VALUES ('1474', '1471', '五莲', '五莲县', '中国,山东省,日照市,五莲县', '3', 'wulian', '0633', '262300', 'W', '119.207', '35.75004'); +INSERT INTO `yoshop_region` VALUES ('1475', '1471', '莒县', '莒县', '中国,山东省,日照市,莒县', '3', 'juxian', '0633', '276500', null, '118.83789', '35.58054'); +INSERT INTO `yoshop_region` VALUES ('1476', '1375', '莱芜', '莱芜市', '中国,山东省,莱芜市', '2', 'laiwu', '0634', '271100', 'L', '117.677736', '36.214397'); +INSERT INTO `yoshop_region` VALUES ('1477', '1476', '莱城', '莱城区', '中国,山东省,莱芜市,莱城区', '3', 'laicheng', '0634', '271199', 'L', '117.65986', '36.2032'); +INSERT INTO `yoshop_region` VALUES ('1478', '1476', '钢城', '钢城区', '中国,山东省,莱芜市,钢城区', '3', 'gangcheng', '0634', '271100', 'G', '117.8049', '36.06319'); +INSERT INTO `yoshop_region` VALUES ('1479', '1375', '临沂', '临沂市', '中国,山东省,临沂市', '2', 'linyi', '0539', '253000', 'L', '118.326443', '35.065282'); +INSERT INTO `yoshop_region` VALUES ('1480', '1479', '兰山', '兰山区', '中国,山东省,临沂市,兰山区', '3', 'lanshan', '0539', '276002', 'L', '118.34817', '35.06872'); +INSERT INTO `yoshop_region` VALUES ('1481', '1479', '罗庄', '罗庄区', '中国,山东省,临沂市,罗庄区', '3', 'luozhuang', '0539', '276022', 'L', '118.28466', '34.99627'); +INSERT INTO `yoshop_region` VALUES ('1482', '1479', '河东', '河东区', '中国,山东省,临沂市,河东区', '3', 'hedong', '0539', '276034', 'H', '118.41055', '35.08803'); +INSERT INTO `yoshop_region` VALUES ('1483', '1479', '沂南', '沂南县', '中国,山东省,临沂市,沂南县', '3', 'yinan', '0539', '276300', 'Y', '118.47061', '35.55131'); +INSERT INTO `yoshop_region` VALUES ('1484', '1479', '郯城', '郯城县', '中国,山东省,临沂市,郯城县', '3', 'tancheng', '0539', '276100', null, '118.36712', '34.61354'); +INSERT INTO `yoshop_region` VALUES ('1485', '1479', '沂水', '沂水县', '中国,山东省,临沂市,沂水县', '3', 'yishui', '0539', '276400', 'Y', '118.63009', '35.78731'); +INSERT INTO `yoshop_region` VALUES ('1486', '1479', '兰陵', '兰陵县', '中国,山东省,临沂市,兰陵县', '3', 'lanling', '0539', '277700', 'L', '117.856592', '34.738315'); +INSERT INTO `yoshop_region` VALUES ('1487', '1479', '费县', '费县', '中国,山东省,临沂市,费县', '3', 'feixian', '0539', '273400', 'F', '117.97836', '35.26562'); +INSERT INTO `yoshop_region` VALUES ('1488', '1479', '平邑', '平邑县', '中国,山东省,临沂市,平邑县', '3', 'pingyi', '0539', '273300', 'P', '117.63867', '35.50573'); +INSERT INTO `yoshop_region` VALUES ('1489', '1479', '莒南', '莒南县', '中国,山东省,临沂市,莒南县', '3', 'junan', '0539', '276600', null, '118.83227', '35.17539'); +INSERT INTO `yoshop_region` VALUES ('1490', '1479', '蒙阴', '蒙阴县', '中国,山东省,临沂市,蒙阴县', '3', 'mengyin', '0539', '276200', 'M', '117.94592', '35.70996'); +INSERT INTO `yoshop_region` VALUES ('1491', '1479', '临沭', '临沭县', '中国,山东省,临沂市,临沭县', '3', 'linshu', '0539', '276700', 'L', '118.65267', '34.92091'); +INSERT INTO `yoshop_region` VALUES ('1492', '1375', '德州', '德州市', '中国,山东省,德州市', '2', 'dezhou', '0534', '253000', 'D', '116.307428', '37.453968'); +INSERT INTO `yoshop_region` VALUES ('1493', '1492', '德城', '德城区', '中国,山东省,德州市,德城区', '3', 'decheng', '0534', '253012', 'D', '116.29943', '37.45126'); +INSERT INTO `yoshop_region` VALUES ('1494', '1492', '陵城', '陵城区', '中国,山东省,德州市,陵城区', '3', 'lingcheng', '0534', '253500', 'L', '116.57601', '37.33571'); +INSERT INTO `yoshop_region` VALUES ('1495', '1492', '宁津', '宁津县', '中国,山东省,德州市,宁津县', '3', 'ningjin', '0534', '253400', 'N', '116.79702', '37.65301'); +INSERT INTO `yoshop_region` VALUES ('1496', '1492', '庆云', '庆云县', '中国,山东省,德州市,庆云县', '3', 'qingyun', '0534', '253700', 'Q', '117.38635', '37.77616'); +INSERT INTO `yoshop_region` VALUES ('1497', '1492', '临邑', '临邑县', '中国,山东省,德州市,临邑县', '3', 'linyi', '0534', '251500', 'L', '116.86547', '37.19053'); +INSERT INTO `yoshop_region` VALUES ('1498', '1492', '齐河', '齐河县', '中国,山东省,德州市,齐河县', '3', 'qihe', '0534', '251100', 'Q', '116.75515', '36.79532'); +INSERT INTO `yoshop_region` VALUES ('1499', '1492', '平原', '平原县', '中国,山东省,德州市,平原县', '3', 'pingyuan', '0534', '253100', 'P', '116.43432', '37.16632'); +INSERT INTO `yoshop_region` VALUES ('1500', '1492', '夏津', '夏津县', '中国,山东省,德州市,夏津县', '3', 'xiajin', '0534', '253200', 'X', '116.0017', '36.94852'); +INSERT INTO `yoshop_region` VALUES ('1501', '1492', '武城', '武城县', '中国,山东省,德州市,武城县', '3', 'wucheng', '0534', '253300', 'W', '116.07009', '37.21403'); +INSERT INTO `yoshop_region` VALUES ('1502', '1492', '乐陵', '乐陵市', '中国,山东省,德州市,乐陵市', '3', 'leling', '0534', '253600', 'L', '117.23141', '37.73164'); +INSERT INTO `yoshop_region` VALUES ('1503', '1492', '禹城', '禹城市', '中国,山东省,德州市,禹城市', '3', 'yucheng', '0534', '251200', 'Y', '116.64309', '36.93444'); +INSERT INTO `yoshop_region` VALUES ('1504', '1375', '聊城', '聊城市', '中国,山东省,聊城市', '2', 'liaocheng', '0635', '252052', 'L', '115.980367', '36.456013'); +INSERT INTO `yoshop_region` VALUES ('1505', '1504', '东昌府', '东昌府区', '中国,山东省,聊城市,东昌府区', '3', 'dongchangfu', '0635', '252000', 'D', '115.97383', '36.44458'); +INSERT INTO `yoshop_region` VALUES ('1506', '1504', '阳谷', '阳谷县', '中国,山东省,聊城市,阳谷县', '3', 'yanggu', '0635', '252300', 'Y', '115.79126', '36.11444'); +INSERT INTO `yoshop_region` VALUES ('1507', '1504', '莘县', '莘县', '中国,山东省,聊城市,莘县', '3', 'shenxian', '0635', '252400', null, '115.6697', '36.23423'); +INSERT INTO `yoshop_region` VALUES ('1508', '1504', '茌平', '茌平县', '中国,山东省,聊城市,茌平县', '3', 'chiping', '0635', '252100', null, '116.25491', '36.57969'); +INSERT INTO `yoshop_region` VALUES ('1509', '1504', '东阿', '东阿县', '中国,山东省,聊城市,东阿县', '3', 'dong\'e', '0635', '252200', 'D', '116.25012', '36.33209'); +INSERT INTO `yoshop_region` VALUES ('1510', '1504', '冠县', '冠县', '中国,山东省,聊城市,冠县', '3', 'guanxian', '0635', '252500', 'G', '115.44195', '36.48429'); +INSERT INTO `yoshop_region` VALUES ('1511', '1504', '高唐', '高唐县', '中国,山东省,聊城市,高唐县', '3', 'gaotang', '0635', '252800', 'G', '116.23172', '36.86535'); +INSERT INTO `yoshop_region` VALUES ('1512', '1504', '临清', '临清市', '中国,山东省,聊城市,临清市', '3', 'linqing', '0635', '252600', 'L', '115.70629', '36.83945'); +INSERT INTO `yoshop_region` VALUES ('1513', '1375', '滨州', '滨州市', '中国,山东省,滨州市', '2', 'binzhou', '0543', '256619', 'B', '118.016974', '37.383542'); +INSERT INTO `yoshop_region` VALUES ('1514', '1513', '滨城', '滨城区', '中国,山东省,滨州市,滨城区', '3', 'bincheng', '0543', '256613', 'B', '118.02026', '37.38524'); +INSERT INTO `yoshop_region` VALUES ('1515', '1513', '沾化', '沾化区', '中国,山东省,滨州市,沾化区', '3', 'zhanhua', '0543', '256800', 'Z', '118.13214', '37.69832'); +INSERT INTO `yoshop_region` VALUES ('1516', '1513', '惠民', '惠民县', '中国,山东省,滨州市,惠民县', '3', 'huimin', '0543', '251700', 'H', '117.51113', '37.49013'); +INSERT INTO `yoshop_region` VALUES ('1517', '1513', '阳信', '阳信县', '中国,山东省,滨州市,阳信县', '3', 'yangxin', '0543', '251800', 'Y', '117.58139', '37.64198'); +INSERT INTO `yoshop_region` VALUES ('1518', '1513', '无棣', '无棣县', '中国,山东省,滨州市,无棣县', '3', 'wudi', '0543', '251900', 'W', '117.61395', '37.74009'); +INSERT INTO `yoshop_region` VALUES ('1519', '1513', '博兴', '博兴县', '中国,山东省,滨州市,博兴县', '3', 'boxing', '0543', '256500', 'B', '118.1336', '37.14316'); +INSERT INTO `yoshop_region` VALUES ('1520', '1513', '邹平', '邹平县', '中国,山东省,滨州市,邹平县', '3', 'zouping', '0543', '256200', 'Z', '117.74307', '36.86295'); +INSERT INTO `yoshop_region` VALUES ('1521', '1513', '北海新区', '北海新区', '中国,山东省,滨州市,北海新区', '3', 'beihaixinqu', '0543', '256200', 'B', '118.016974', '37.383542'); +INSERT INTO `yoshop_region` VALUES ('1522', '1375', '菏泽', '菏泽市', '中国,山东省,菏泽市', '2', 'heze', '0530', '274020', 'H', '115.469381', '35.246531'); +INSERT INTO `yoshop_region` VALUES ('1523', '1522', '牡丹', '牡丹区', '中国,山东省,菏泽市,牡丹区', '3', 'mudan', '0530', '274009', 'M', '115.41662', '35.25091'); +INSERT INTO `yoshop_region` VALUES ('1524', '1522', '曹县', '曹县', '中国,山东省,菏泽市,曹县', '3', 'caoxian', '0530', '274400', 'C', '115.54226', '34.82659'); +INSERT INTO `yoshop_region` VALUES ('1525', '1522', '单县', '单县', '中国,山东省,菏泽市,单县', '3', 'shanxian', '0530', '273700', 'D', '116.08703', '34.79514'); +INSERT INTO `yoshop_region` VALUES ('1526', '1522', '成武', '成武县', '中国,山东省,菏泽市,成武县', '3', 'chengwu', '0530', '274200', 'C', '115.8897', '34.95332'); +INSERT INTO `yoshop_region` VALUES ('1527', '1522', '巨野', '巨野县', '中国,山东省,菏泽市,巨野县', '3', 'juye', '0530', '274900', 'J', '116.09497', '35.39788'); +INSERT INTO `yoshop_region` VALUES ('1528', '1522', '郓城', '郓城县', '中国,山东省,菏泽市,郓城县', '3', 'yuncheng', '0530', '274700', null, '115.94439', '35.60044'); +INSERT INTO `yoshop_region` VALUES ('1529', '1522', '鄄城', '鄄城县', '中国,山东省,菏泽市,鄄城县', '3', 'juancheng', '0530', '274600', null, '115.50997', '35.56412'); +INSERT INTO `yoshop_region` VALUES ('1530', '1522', '定陶', '定陶县', '中国,山东省,菏泽市,定陶县', '3', 'dingtao', '0530', '274100', 'D', '115.57287', '35.07118'); +INSERT INTO `yoshop_region` VALUES ('1531', '1522', '东明', '东明县', '中国,山东省,菏泽市,东明县', '3', 'dongming', '0530', '274500', 'D', '115.09079', '35.28906'); +INSERT INTO `yoshop_region` VALUES ('1532', '0', '河南', '河南省', '中国,河南省', '1', 'henan', '', '', 'H', '113.665412', '34.757975'); +INSERT INTO `yoshop_region` VALUES ('1533', '1532', '郑州', '郑州市', '中国,河南省,郑州市', '2', 'zhengzhou', '0371', '450000', 'Z', '113.665412', '34.757975'); +INSERT INTO `yoshop_region` VALUES ('1534', '1533', '中原', '中原区', '中国,河南省,郑州市,中原区', '3', 'zhongyuan', '0371', '450007', 'Z', '113.61333', '34.74827'); +INSERT INTO `yoshop_region` VALUES ('1535', '1533', '二七', '二七区', '中国,河南省,郑州市,二七区', '3', 'erqi', '0371', '450052', 'E', '113.63931', '34.72336'); +INSERT INTO `yoshop_region` VALUES ('1536', '1533', '管城', '管城回族区', '中国,河南省,郑州市,管城回族区', '3', 'guancheng', '0371', '450000', 'G', '113.67734', '34.75383'); +INSERT INTO `yoshop_region` VALUES ('1537', '1533', '金水', '金水区', '中国,河南省,郑州市,金水区', '3', 'jinshui', '0371', '450003', 'J', '113.66057', '34.80028'); +INSERT INTO `yoshop_region` VALUES ('1538', '1533', '上街', '上街区', '中国,河南省,郑州市,上街区', '3', 'shangjie', '0371', '450041', 'S', '113.30897', '34.80276'); +INSERT INTO `yoshop_region` VALUES ('1539', '1533', '惠济', '惠济区', '中国,河南省,郑州市,惠济区', '3', 'huiji', '0371', '450053', 'H', '113.61688', '34.86735'); +INSERT INTO `yoshop_region` VALUES ('1540', '1533', '中牟', '中牟县', '中国,河南省,郑州市,中牟县', '3', 'zhongmu', '0371', '451450', 'Z', '113.97619', '34.71899'); +INSERT INTO `yoshop_region` VALUES ('1541', '1533', '巩义', '巩义市', '中国,河南省,郑州市,巩义市', '3', 'gongyi', '0371', '451200', 'G', '113.022', '34.74794'); +INSERT INTO `yoshop_region` VALUES ('1542', '1533', '荥阳', '荥阳市', '中国,河南省,郑州市,荥阳市', '3', 'xingyang', '0371', '450100', null, '113.38345', '34.78759'); +INSERT INTO `yoshop_region` VALUES ('1543', '1533', '新密', '新密市', '中国,河南省,郑州市,新密市', '3', 'xinmi', '0371', '452300', 'X', '113.3869', '34.53704'); +INSERT INTO `yoshop_region` VALUES ('1544', '1533', '新郑', '新郑市', '中国,河南省,郑州市,新郑市', '3', 'xinzheng', '0371', '451100', 'X', '113.73645', '34.3955'); +INSERT INTO `yoshop_region` VALUES ('1545', '1533', '登封', '登封市', '中国,河南省,郑州市,登封市', '3', 'dengfeng', '0371', '452470', 'D', '113.05023', '34.45345'); +INSERT INTO `yoshop_region` VALUES ('1546', '1532', '开封', '开封市', '中国,河南省,开封市', '2', 'kaifeng', '0378', '475001', 'K', '114.341447', '34.797049'); +INSERT INTO `yoshop_region` VALUES ('1547', '1546', '龙亭', '龙亭区', '中国,河南省,开封市,龙亭区', '3', 'longting', '0378', '475100', 'L', '114.35484', '34.79995'); +INSERT INTO `yoshop_region` VALUES ('1548', '1546', '顺河', '顺河回族区', '中国,河南省,开封市,顺河回族区', '3', 'shunhe', '0378', '475000', 'S', '114.36123', '34.79586'); +INSERT INTO `yoshop_region` VALUES ('1549', '1546', '鼓楼', '鼓楼区', '中国,河南省,开封市,鼓楼区', '3', 'gulou', '0378', '475000', 'G', '114.35559', '34.79517'); +INSERT INTO `yoshop_region` VALUES ('1550', '1546', '禹王台', '禹王台区', '中国,河南省,开封市,禹王台区', '3', 'yuwangtai', '0378', '475003', 'Y', '114.34787', '34.77693'); +INSERT INTO `yoshop_region` VALUES ('1551', '1546', '祥符', '祥符区', '中国,河南省,开封市,祥符区', '3', 'xiangfu', '0378', '475100', 'X', '114.43859', '34.75874'); +INSERT INTO `yoshop_region` VALUES ('1552', '1546', '杞县', '杞县', '中国,河南省,开封市,杞县', '3', 'qixian', '0378', '475200', null, '114.7828', '34.55033'); +INSERT INTO `yoshop_region` VALUES ('1553', '1546', '通许', '通许县', '中国,河南省,开封市,通许县', '3', 'tongxu', '0378', '475400', 'T', '114.46716', '34.47522'); +INSERT INTO `yoshop_region` VALUES ('1554', '1546', '尉氏', '尉氏县', '中国,河南省,开封市,尉氏县', '3', 'weishi', '0378', '475500', 'W', '114.19284', '34.41223'); +INSERT INTO `yoshop_region` VALUES ('1555', '1546', '兰考', '兰考县', '中国,河南省,开封市,兰考县', '3', 'lankao', '0378', '475300', 'L', '114.81961', '34.8235'); +INSERT INTO `yoshop_region` VALUES ('1556', '1532', '洛阳', '洛阳市', '中国,河南省,洛阳市', '2', 'luoyang', '0379', '471000', 'L', '112.434468', '34.663041'); +INSERT INTO `yoshop_region` VALUES ('1557', '1556', '老城', '老城区', '中国,河南省,洛阳市,老城区', '3', 'laocheng', '0379', '471002', 'L', '112.46902', '34.68364'); +INSERT INTO `yoshop_region` VALUES ('1558', '1556', '西工', '西工区', '中国,河南省,洛阳市,西工区', '3', 'xigong', '0379', '471000', 'X', '112.4371', '34.67'); +INSERT INTO `yoshop_region` VALUES ('1559', '1556', '瀍河', '瀍河回族区', '中国,河南省,洛阳市,瀍河回族区', '3', 'chanhe', '0379', '471002', null, '112.50018', '34.67985'); +INSERT INTO `yoshop_region` VALUES ('1560', '1556', '涧西', '涧西区', '中国,河南省,洛阳市,涧西区', '3', 'jianxi', '0379', '471003', 'J', '112.39588', '34.65823'); +INSERT INTO `yoshop_region` VALUES ('1561', '1556', '吉利', '吉利区', '中国,河南省,洛阳市,吉利区', '3', 'jili', '0379', '471012', 'J', '112.58905', '34.90088'); +INSERT INTO `yoshop_region` VALUES ('1562', '1556', '洛龙', '洛龙区', '中国,河南省,洛阳市,洛龙区', '3', 'luolong', '0379', '471000', 'L', '112.46412', '34.61866'); +INSERT INTO `yoshop_region` VALUES ('1563', '1556', '孟津', '孟津县', '中国,河南省,洛阳市,孟津县', '3', 'mengjin', '0379', '471100', 'M', '112.44351', '34.826'); +INSERT INTO `yoshop_region` VALUES ('1564', '1556', '新安', '新安县', '中国,河南省,洛阳市,新安县', '3', 'xin\'an', '0379', '471800', 'X', '112.13238', '34.72814'); +INSERT INTO `yoshop_region` VALUES ('1565', '1556', '栾川', '栾川县', '中国,河南省,洛阳市,栾川县', '3', 'luanchuan', '0379', '471500', null, '111.61779', '33.78576'); +INSERT INTO `yoshop_region` VALUES ('1566', '1556', '嵩县', '嵩县', '中国,河南省,洛阳市,嵩县', '3', 'songxian', '0379', '471400', null, '112.08526', '34.13466'); +INSERT INTO `yoshop_region` VALUES ('1567', '1556', '汝阳', '汝阳县', '中国,河南省,洛阳市,汝阳县', '3', 'ruyang', '0379', '471200', 'R', '112.47314', '34.15387'); +INSERT INTO `yoshop_region` VALUES ('1568', '1556', '宜阳', '宜阳县', '中国,河南省,洛阳市,宜阳县', '3', 'yiyang', '0379', '471600', 'Y', '112.17907', '34.51523'); +INSERT INTO `yoshop_region` VALUES ('1569', '1556', '洛宁', '洛宁县', '中国,河南省,洛阳市,洛宁县', '3', 'luoning', '0379', '471700', 'L', '111.65087', '34.38913'); +INSERT INTO `yoshop_region` VALUES ('1570', '1556', '伊川', '伊川县', '中国,河南省,洛阳市,伊川县', '3', 'yichuan', '0379', '471300', 'Y', '112.42947', '34.42205'); +INSERT INTO `yoshop_region` VALUES ('1571', '1556', '偃师', '偃师市', '中国,河南省,洛阳市,偃师市', '3', 'yanshi', '0379', '471900', null, '112.7922', '34.7281'); +INSERT INTO `yoshop_region` VALUES ('1572', '1532', '平顶山', '平顶山市', '中国,河南省,平顶山市', '2', 'pingdingshan', '0375', '467000', 'P', '113.307718', '33.735241'); +INSERT INTO `yoshop_region` VALUES ('1573', '1572', '新华', '新华区', '中国,河南省,平顶山市,新华区', '3', 'xinhua', '0375', '467002', 'X', '113.29402', '33.7373'); +INSERT INTO `yoshop_region` VALUES ('1574', '1572', '卫东', '卫东区', '中国,河南省,平顶山市,卫东区', '3', 'weidong', '0375', '467021', 'W', '113.33511', '33.73472'); +INSERT INTO `yoshop_region` VALUES ('1575', '1572', '石龙', '石龙区', '中国,河南省,平顶山市,石龙区', '3', 'shilong', '0375', '467045', 'S', '112.89879', '33.89878'); +INSERT INTO `yoshop_region` VALUES ('1576', '1572', '湛河', '湛河区', '中国,河南省,平顶山市,湛河区', '3', 'zhanhe', '0375', '467000', 'Z', '113.29252', '33.7362'); +INSERT INTO `yoshop_region` VALUES ('1577', '1572', '宝丰', '宝丰县', '中国,河南省,平顶山市,宝丰县', '3', 'baofeng', '0375', '467400', 'B', '113.05493', '33.86916'); +INSERT INTO `yoshop_region` VALUES ('1578', '1572', '叶县', '叶县', '中国,河南省,平顶山市,叶县', '3', 'yexian', '0375', '467200', 'Y', '113.35104', '33.62225'); +INSERT INTO `yoshop_region` VALUES ('1579', '1572', '鲁山', '鲁山县', '中国,河南省,平顶山市,鲁山县', '3', 'lushan', '0375', '467300', 'L', '112.9057', '33.73879'); +INSERT INTO `yoshop_region` VALUES ('1580', '1572', '郏县', '郏县', '中国,河南省,平顶山市,郏县', '3', 'jiaxian', '0375', '467100', null, '113.21588', '33.97072'); +INSERT INTO `yoshop_region` VALUES ('1581', '1572', '舞钢', '舞钢市', '中国,河南省,平顶山市,舞钢市', '3', 'wugang', '0375', '462500', 'W', '113.52417', '33.2938'); +INSERT INTO `yoshop_region` VALUES ('1582', '1572', '汝州', '汝州市', '中国,河南省,平顶山市,汝州市', '3', 'ruzhou', '0375', '467500', 'R', '112.84301', '34.16135'); +INSERT INTO `yoshop_region` VALUES ('1583', '1532', '安阳', '安阳市', '中国,河南省,安阳市', '2', 'anyang', '0372', '455000', 'A', '114.352482', '36.103442'); +INSERT INTO `yoshop_region` VALUES ('1584', '1583', '文峰', '文峰区', '中国,河南省,安阳市,文峰区', '3', 'wenfeng', '0372', '455000', 'W', '114.35708', '36.09046'); +INSERT INTO `yoshop_region` VALUES ('1585', '1583', '北关', '北关区', '中国,河南省,安阳市,北关区', '3', 'beiguan', '0372', '455001', 'B', '114.35735', '36.11872'); +INSERT INTO `yoshop_region` VALUES ('1586', '1583', '殷都', '殷都区', '中国,河南省,安阳市,殷都区', '3', 'yindu', '0372', '455004', 'Y', '114.3034', '36.1099'); +INSERT INTO `yoshop_region` VALUES ('1587', '1583', '龙安', '龙安区', '中国,河南省,安阳市,龙安区', '3', 'long\'an', '0372', '455001', 'L', '114.34814', '36.11904'); +INSERT INTO `yoshop_region` VALUES ('1588', '1583', '安阳', '安阳县', '中国,河南省,安阳市,安阳县', '3', 'anyang', '0372', '455000', 'A', '114.36605', '36.06695'); +INSERT INTO `yoshop_region` VALUES ('1589', '1583', '汤阴', '汤阴县', '中国,河南省,安阳市,汤阴县', '3', 'tangyin', '0372', '456150', 'T', '114.35839', '35.92152'); +INSERT INTO `yoshop_region` VALUES ('1590', '1583', '滑县', '滑县', '中国,河南省,安阳市,滑县', '3', 'huaxian', '0372', '456400', 'H', '114.52066', '35.5807'); +INSERT INTO `yoshop_region` VALUES ('1591', '1583', '内黄', '内黄县', '中国,河南省,安阳市,内黄县', '3', 'neihuang', '0372', '456350', 'N', '114.90673', '35.95269'); +INSERT INTO `yoshop_region` VALUES ('1592', '1583', '林州', '林州市', '中国,河南省,安阳市,林州市', '3', 'linzhou', '0372', '456550', 'L', '113.81558', '36.07804'); +INSERT INTO `yoshop_region` VALUES ('1593', '1532', '鹤壁', '鹤壁市', '中国,河南省,鹤壁市', '2', 'hebi', '0392', '458030', 'H', '114.295444', '35.748236'); +INSERT INTO `yoshop_region` VALUES ('1594', '1593', '鹤山', '鹤山区', '中国,河南省,鹤壁市,鹤山区', '3', 'heshan', '0392', '458010', 'H', '114.16336', '35.95458'); +INSERT INTO `yoshop_region` VALUES ('1595', '1593', '山城', '山城区', '中国,河南省,鹤壁市,山城区', '3', 'shancheng', '0392', '458000', 'S', '114.18443', '35.89773'); +INSERT INTO `yoshop_region` VALUES ('1596', '1593', '淇滨', '淇滨区', '中国,河南省,鹤壁市,淇滨区', '3', 'qibin', '0392', '458000', null, '114.29867', '35.74127'); +INSERT INTO `yoshop_region` VALUES ('1597', '1593', '浚县', '浚县', '中国,河南省,鹤壁市,浚县', '3', 'xunxian', '0392', '456250', 'J', '114.54879', '35.67085'); +INSERT INTO `yoshop_region` VALUES ('1598', '1593', '淇县', '淇县', '中国,河南省,鹤壁市,淇县', '3', 'qixian', '0392', '456750', null, '114.1976', '35.60782'); +INSERT INTO `yoshop_region` VALUES ('1599', '1532', '新乡', '新乡市', '中国,河南省,新乡市', '2', 'xinxiang', '0373', '453000', 'X', '113.883991', '35.302616'); +INSERT INTO `yoshop_region` VALUES ('1600', '1599', '红旗', '红旗区', '中国,河南省,新乡市,红旗区', '3', 'hongqi', '0373', '453000', 'H', '113.87523', '35.30367'); +INSERT INTO `yoshop_region` VALUES ('1601', '1599', '卫滨', '卫滨区', '中国,河南省,新乡市,卫滨区', '3', 'weibin', '0373', '453000', 'W', '113.86578', '35.30211'); +INSERT INTO `yoshop_region` VALUES ('1602', '1599', '凤泉', '凤泉区', '中国,河南省,新乡市,凤泉区', '3', 'fengquan', '0373', '453011', 'F', '113.91507', '35.38399'); +INSERT INTO `yoshop_region` VALUES ('1603', '1599', '牧野', '牧野区', '中国,河南省,新乡市,牧野区', '3', 'muye', '0373', '453002', 'M', '113.9086', '35.3149'); +INSERT INTO `yoshop_region` VALUES ('1604', '1599', '新乡', '新乡县', '中国,河南省,新乡市,新乡县', '3', 'xinxiang', '0373', '453700', 'X', '113.80511', '35.19075'); +INSERT INTO `yoshop_region` VALUES ('1605', '1599', '获嘉', '获嘉县', '中国,河南省,新乡市,获嘉县', '3', 'huojia', '0373', '453800', 'H', '113.66159', '35.26521'); +INSERT INTO `yoshop_region` VALUES ('1606', '1599', '原阳', '原阳县', '中国,河南省,新乡市,原阳县', '3', 'yuanyang', '0373', '453500', 'Y', '113.93994', '35.06565'); +INSERT INTO `yoshop_region` VALUES ('1607', '1599', '延津', '延津县', '中国,河南省,新乡市,延津县', '3', 'yanjin', '0373', '453200', 'Y', '114.20266', '35.14327'); +INSERT INTO `yoshop_region` VALUES ('1608', '1599', '封丘', '封丘县', '中国,河南省,新乡市,封丘县', '3', 'fengqiu', '0373', '453300', 'F', '114.41915', '35.04166'); +INSERT INTO `yoshop_region` VALUES ('1609', '1599', '长垣', '长垣县', '中国,河南省,新乡市,长垣县', '3', 'changyuan', '0373', '453400', 'C', '114.66882', '35.20046'); +INSERT INTO `yoshop_region` VALUES ('1610', '1599', '卫辉', '卫辉市', '中国,河南省,新乡市,卫辉市', '3', 'weihui', '0373', '453100', 'W', '114.06454', '35.39843'); +INSERT INTO `yoshop_region` VALUES ('1611', '1599', '辉县', '辉县市', '中国,河南省,新乡市,辉县市', '3', 'huixian', '0373', '453600', 'H', '113.8067', '35.46307'); +INSERT INTO `yoshop_region` VALUES ('1612', '1532', '焦作', '焦作市', '中国,河南省,焦作市', '2', 'jiaozuo', '0391', '454002', 'J', '113.238266', '35.23904'); +INSERT INTO `yoshop_region` VALUES ('1613', '1612', '解放', '解放区', '中国,河南省,焦作市,解放区', '3', 'jiefang', '0391', '454000', 'J', '113.22933', '35.24023'); +INSERT INTO `yoshop_region` VALUES ('1614', '1612', '中站', '中站区', '中国,河南省,焦作市,中站区', '3', 'zhongzhan', '0391', '454191', 'Z', '113.18315', '35.23665'); +INSERT INTO `yoshop_region` VALUES ('1615', '1612', '马村', '马村区', '中国,河南省,焦作市,马村区', '3', 'macun', '0391', '454171', 'M', '113.3187', '35.26908'); +INSERT INTO `yoshop_region` VALUES ('1616', '1612', '山阳', '山阳区', '中国,河南省,焦作市,山阳区', '3', 'shanyang', '0391', '454002', 'S', '113.25464', '35.21436'); +INSERT INTO `yoshop_region` VALUES ('1617', '1612', '修武', '修武县', '中国,河南省,焦作市,修武县', '3', 'xiuwu', '0391', '454350', 'X', '113.44775', '35.22357'); +INSERT INTO `yoshop_region` VALUES ('1618', '1612', '博爱', '博爱县', '中国,河南省,焦作市,博爱县', '3', 'boai', '0391', '454450', 'B', '113.06698', '35.16943'); +INSERT INTO `yoshop_region` VALUES ('1619', '1612', '武陟', '武陟县', '中国,河南省,焦作市,武陟县', '3', 'wuzhi', '0391', '454950', 'W', '113.39718', '35.09505'); +INSERT INTO `yoshop_region` VALUES ('1620', '1612', '温县', '温县', '中国,河南省,焦作市,温县', '3', 'wenxian', '0391', '454850', 'W', '113.08065', '34.94022'); +INSERT INTO `yoshop_region` VALUES ('1621', '1612', '沁阳', '沁阳市', '中国,河南省,焦作市,沁阳市', '3', 'qinyang', '0391', '454550', 'Q', '112.94494', '35.08935'); +INSERT INTO `yoshop_region` VALUES ('1622', '1612', '孟州', '孟州市', '中国,河南省,焦作市,孟州市', '3', 'mengzhou', '0391', '454750', 'M', '112.79138', '34.9071'); +INSERT INTO `yoshop_region` VALUES ('1623', '1532', '濮阳', '濮阳市', '中国,河南省,濮阳市', '2', 'puyang', '0393', '457000', null, '115.041299', '35.768234'); +INSERT INTO `yoshop_region` VALUES ('1624', '1623', '华龙', '华龙区', '中国,河南省,濮阳市,华龙区', '3', 'hualong', '0393', '457001', 'H', '115.07446', '35.77736'); +INSERT INTO `yoshop_region` VALUES ('1625', '1623', '清丰', '清丰县', '中国,河南省,濮阳市,清丰县', '3', 'qingfeng', '0393', '457300', 'Q', '115.10415', '35.88507'); +INSERT INTO `yoshop_region` VALUES ('1626', '1623', '南乐', '南乐县', '中国,河南省,濮阳市,南乐县', '3', 'nanle', '0393', '457400', 'N', '115.20639', '36.07686'); +INSERT INTO `yoshop_region` VALUES ('1627', '1623', '范县', '范县', '中国,河南省,濮阳市,范县', '3', 'fanxian', '0393', '457500', 'F', '115.50405', '35.85178'); +INSERT INTO `yoshop_region` VALUES ('1628', '1623', '台前', '台前县', '中国,河南省,濮阳市,台前县', '3', 'taiqian', '0393', '457600', 'T', '115.87158', '35.96923'); +INSERT INTO `yoshop_region` VALUES ('1629', '1623', '濮阳', '濮阳县', '中国,河南省,濮阳市,濮阳县', '3', 'puyang', '0393', '457100', null, '115.03057', '35.70745'); +INSERT INTO `yoshop_region` VALUES ('1630', '1532', '许昌', '许昌市', '中国,河南省,许昌市', '2', 'xuchang', '0374', '461000', 'X', '113.826063', '34.022956'); +INSERT INTO `yoshop_region` VALUES ('1631', '1630', '魏都', '魏都区', '中国,河南省,许昌市,魏都区', '3', 'weidu', '0374', '461000', 'W', '113.8227', '34.02544'); +INSERT INTO `yoshop_region` VALUES ('1632', '1630', '许昌', '许昌县', '中国,河南省,许昌市,许昌县', '3', 'xuchang', '0374', '461100', 'X', '113.84707', '34.00406'); +INSERT INTO `yoshop_region` VALUES ('1633', '1630', '鄢陵', '鄢陵县', '中国,河南省,许昌市,鄢陵县', '3', 'yanling', '0374', '461200', null, '114.18795', '34.10317'); +INSERT INTO `yoshop_region` VALUES ('1634', '1630', '襄城', '襄城县', '中国,河南省,许昌市,襄城县', '3', 'xiangcheng', '0374', '461700', 'X', '113.48196', '33.84928'); +INSERT INTO `yoshop_region` VALUES ('1635', '1630', '禹州', '禹州市', '中国,河南省,许昌市,禹州市', '3', 'yuzhou', '0374', '461670', 'Y', '113.48803', '34.14054'); +INSERT INTO `yoshop_region` VALUES ('1636', '1630', '长葛', '长葛市', '中国,河南省,许昌市,长葛市', '3', 'changge', '0374', '461500', 'C', '113.77328', '34.21846'); +INSERT INTO `yoshop_region` VALUES ('1637', '1532', '漯河', '漯河市', '中国,河南省,漯河市', '2', 'luohe', '0395', '462000', null, '114.026405', '33.575855'); +INSERT INTO `yoshop_region` VALUES ('1638', '1637', '源汇', '源汇区', '中国,河南省,漯河市,源汇区', '3', 'yuanhui', '0395', '462000', 'Y', '114.00647', '33.55627'); +INSERT INTO `yoshop_region` VALUES ('1639', '1637', '郾城', '郾城区', '中国,河南省,漯河市,郾城区', '3', 'yancheng', '0395', '462300', null, '114.00694', '33.58723'); +INSERT INTO `yoshop_region` VALUES ('1640', '1637', '召陵', '召陵区', '中国,河南省,漯河市,召陵区', '3', 'zhaoling', '0395', '462300', 'Z', '114.09399', '33.58601'); +INSERT INTO `yoshop_region` VALUES ('1641', '1637', '舞阳', '舞阳县', '中国,河南省,漯河市,舞阳县', '3', 'wuyang', '0395', '462400', 'W', '113.59848', '33.43243'); +INSERT INTO `yoshop_region` VALUES ('1642', '1637', '临颍', '临颍县', '中国,河南省,漯河市,临颍县', '3', 'linying', '0395', '462600', 'L', '113.93661', '33.81123'); +INSERT INTO `yoshop_region` VALUES ('1643', '1532', '三门峡', '三门峡市', '中国,河南省,三门峡市', '2', 'sanmenxia', '0398', '472000', 'S', '111.194099', '34.777338'); +INSERT INTO `yoshop_region` VALUES ('1644', '1643', '湖滨', '湖滨区', '中国,河南省,三门峡市,湖滨区', '3', 'hubin', '0398', '472000', 'H', '111.20006', '34.77872'); +INSERT INTO `yoshop_region` VALUES ('1645', '1643', '渑池', '渑池县', '中国,河南省,三门峡市,渑池县', '3', 'mianchi', '0398', '472400', null, '111.76184', '34.76725'); +INSERT INTO `yoshop_region` VALUES ('1646', '1643', '陕县', '陕县', '中国,河南省,三门峡市,陕县', '3', 'shanxian', '0398', '472100', 'S', '111.10333', '34.72052'); +INSERT INTO `yoshop_region` VALUES ('1647', '1643', '卢氏', '卢氏县', '中国,河南省,三门峡市,卢氏县', '3', 'lushi', '0398', '472200', 'L', '111.04782', '34.05436'); +INSERT INTO `yoshop_region` VALUES ('1648', '1643', '义马', '义马市', '中国,河南省,三门峡市,义马市', '3', 'yima', '0398', '472300', 'Y', '111.87445', '34.74721'); +INSERT INTO `yoshop_region` VALUES ('1649', '1643', '灵宝', '灵宝市', '中国,河南省,三门峡市,灵宝市', '3', 'lingbao', '0398', '472500', 'L', '110.8945', '34.51682'); +INSERT INTO `yoshop_region` VALUES ('1650', '1532', '南阳', '南阳市', '中国,河南省,南阳市', '2', 'nanyang', '0377', '473002', 'N', '112.540918', '32.999082'); +INSERT INTO `yoshop_region` VALUES ('1651', '1650', '宛城', '宛城区', '中国,河南省,南阳市,宛城区', '3', 'wancheng', '0377', '473001', 'W', '112.53955', '33.00378'); +INSERT INTO `yoshop_region` VALUES ('1652', '1650', '卧龙', '卧龙区', '中国,河南省,南阳市,卧龙区', '3', 'wolong', '0377', '473003', 'W', '112.53479', '32.98615'); +INSERT INTO `yoshop_region` VALUES ('1653', '1650', '南召', '南召县', '中国,河南省,南阳市,南召县', '3', 'nanzhao', '0377', '474650', 'N', '112.43194', '33.49098'); +INSERT INTO `yoshop_region` VALUES ('1654', '1650', '方城', '方城县', '中国,河南省,南阳市,方城县', '3', 'fangcheng', '0377', '473200', 'F', '113.01269', '33.25453'); +INSERT INTO `yoshop_region` VALUES ('1655', '1650', '西峡', '西峡县', '中国,河南省,南阳市,西峡县', '3', 'xixia', '0377', '474550', 'X', '111.48187', '33.29772'); +INSERT INTO `yoshop_region` VALUES ('1656', '1650', '镇平', '镇平县', '中国,河南省,南阳市,镇平县', '3', 'zhenping', '0377', '474250', 'Z', '112.2398', '33.03629'); +INSERT INTO `yoshop_region` VALUES ('1657', '1650', '内乡', '内乡县', '中国,河南省,南阳市,内乡县', '3', 'neixiang', '0377', '474350', 'N', '111.84957', '33.04671'); +INSERT INTO `yoshop_region` VALUES ('1658', '1650', '淅川', '淅川县', '中国,河南省,南阳市,淅川县', '3', 'xichuan', '0377', '474450', null, '111.48663', '33.13708'); +INSERT INTO `yoshop_region` VALUES ('1659', '1650', '社旗', '社旗县', '中国,河南省,南阳市,社旗县', '3', 'sheqi', '0377', '473300', 'S', '112.94656', '33.05503'); +INSERT INTO `yoshop_region` VALUES ('1660', '1650', '唐河', '唐河县', '中国,河南省,南阳市,唐河县', '3', 'tanghe', '0377', '473400', 'T', '112.83609', '32.69453'); +INSERT INTO `yoshop_region` VALUES ('1661', '1650', '新野', '新野县', '中国,河南省,南阳市,新野县', '3', 'xinye', '0377', '473500', 'X', '112.36151', '32.51698'); +INSERT INTO `yoshop_region` VALUES ('1662', '1650', '桐柏', '桐柏县', '中国,河南省,南阳市,桐柏县', '3', 'tongbai', '0377', '474750', 'T', '113.42886', '32.37917'); +INSERT INTO `yoshop_region` VALUES ('1663', '1650', '邓州', '邓州市', '中国,河南省,南阳市,邓州市', '3', 'dengzhou', '0377', '474150', 'D', '112.0896', '32.68577'); +INSERT INTO `yoshop_region` VALUES ('1664', '1532', '商丘', '商丘市', '中国,河南省,商丘市', '2', 'shangqiu', '0370', '476000', 'S', '115.650497', '34.437054'); +INSERT INTO `yoshop_region` VALUES ('1665', '1664', '梁园', '梁园区', '中国,河南省,商丘市,梁园区', '3', 'liangyuan', '0370', '476000', 'L', '115.64487', '34.44341'); +INSERT INTO `yoshop_region` VALUES ('1666', '1664', '睢阳', '睢阳区', '中国,河南省,商丘市,睢阳区', '3', 'suiyang', '0370', '476100', null, '115.65338', '34.38804'); +INSERT INTO `yoshop_region` VALUES ('1667', '1664', '民权', '民权县', '中国,河南省,商丘市,民权县', '3', 'minquan', '0370', '476800', 'M', '115.14621', '34.64931'); +INSERT INTO `yoshop_region` VALUES ('1668', '1664', '睢县', '睢县', '中国,河南省,商丘市,睢县', '3', 'suixian', '0370', '476900', null, '115.07168', '34.44539'); +INSERT INTO `yoshop_region` VALUES ('1669', '1664', '宁陵', '宁陵县', '中国,河南省,商丘市,宁陵县', '3', 'ningling', '0370', '476700', 'N', '115.30511', '34.45463'); +INSERT INTO `yoshop_region` VALUES ('1670', '1664', '柘城', '柘城县', '中国,河南省,商丘市,柘城县', '3', 'zhecheng', '0370', '476200', null, '115.30538', '34.0911'); +INSERT INTO `yoshop_region` VALUES ('1671', '1664', '虞城', '虞城县', '中国,河南省,商丘市,虞城县', '3', 'yucheng', '0370', '476300', 'Y', '115.86337', '34.40189'); +INSERT INTO `yoshop_region` VALUES ('1672', '1664', '夏邑', '夏邑县', '中国,河南省,商丘市,夏邑县', '3', 'xiayi', '0370', '476400', 'X', '116.13348', '34.23242'); +INSERT INTO `yoshop_region` VALUES ('1673', '1664', '永城', '永城市', '中国,河南省,商丘市,永城市', '3', 'yongcheng', '0370', '476600', 'Y', '116.44943', '33.92911'); +INSERT INTO `yoshop_region` VALUES ('1674', '1532', '信阳', '信阳市', '中国,河南省,信阳市', '2', 'xinyang', '0376', '464000', 'X', '114.075031', '32.123274'); +INSERT INTO `yoshop_region` VALUES ('1675', '1674', '浉河', '浉河区', '中国,河南省,信阳市,浉河区', '3', 'shihe', '0376', '464000', null, '114.05871', '32.1168'); +INSERT INTO `yoshop_region` VALUES ('1676', '1674', '平桥', '平桥区', '中国,河南省,信阳市,平桥区', '3', 'pingqiao', '0376', '464100', 'P', '114.12435', '32.10095'); +INSERT INTO `yoshop_region` VALUES ('1677', '1674', '罗山', '罗山县', '中国,河南省,信阳市,罗山县', '3', 'luoshan', '0376', '464200', 'L', '114.5314', '32.20277'); +INSERT INTO `yoshop_region` VALUES ('1678', '1674', '光山', '光山县', '中国,河南省,信阳市,光山县', '3', 'guangshan', '0376', '465450', 'G', '114.91873', '32.00992'); +INSERT INTO `yoshop_region` VALUES ('1679', '1674', '新县', '新县', '中国,河南省,信阳市,新县', '3', 'xinxian', '0376', '465550', 'X', '114.87924', '31.64386'); +INSERT INTO `yoshop_region` VALUES ('1680', '1674', '商城', '商城县', '中国,河南省,信阳市,商城县', '3', 'shangcheng', '0376', '465350', 'S', '115.40856', '31.79986'); +INSERT INTO `yoshop_region` VALUES ('1681', '1674', '固始', '固始县', '中国,河南省,信阳市,固始县', '3', 'gushi', '0376', '465250', 'G', '115.68298', '32.18011'); +INSERT INTO `yoshop_region` VALUES ('1682', '1674', '潢川', '潢川县', '中国,河南省,信阳市,潢川县', '3', 'huangchuan', '0376', '465150', null, '115.04696', '32.13763'); +INSERT INTO `yoshop_region` VALUES ('1683', '1674', '淮滨', '淮滨县', '中国,河南省,信阳市,淮滨县', '3', 'huaibin', '0376', '464400', 'H', '115.4205', '32.46614'); +INSERT INTO `yoshop_region` VALUES ('1684', '1674', '息县', '息县', '中国,河南省,信阳市,息县', '3', 'xixian', '0376', '464300', 'X', '114.7402', '32.34279'); +INSERT INTO `yoshop_region` VALUES ('1685', '1532', '周口', '周口市', '中国,河南省,周口市', '2', 'zhoukou', '0394', '466000', 'Z', '114.649653', '33.620357'); +INSERT INTO `yoshop_region` VALUES ('1686', '1685', '川汇', '川汇区', '中国,河南省,周口市,川汇区', '3', 'chuanhui', '0394', '466000', 'C', '114.64202', '33.6256'); +INSERT INTO `yoshop_region` VALUES ('1687', '1685', '扶沟', '扶沟县', '中国,河南省,周口市,扶沟县', '3', 'fugou', '0394', '461300', 'F', '114.39477', '34.05999'); +INSERT INTO `yoshop_region` VALUES ('1688', '1685', '西华', '西华县', '中国,河南省,周口市,西华县', '3', 'xihua', '0394', '466600', 'X', '114.52279', '33.78548'); +INSERT INTO `yoshop_region` VALUES ('1689', '1685', '商水', '商水县', '中国,河南省,周口市,商水县', '3', 'shangshui', '0394', '466100', 'S', '114.60604', '33.53912'); +INSERT INTO `yoshop_region` VALUES ('1690', '1685', '沈丘', '沈丘县', '中国,河南省,周口市,沈丘县', '3', 'shenqiu', '0394', '466300', 'S', '115.09851', '33.40936'); +INSERT INTO `yoshop_region` VALUES ('1691', '1685', '郸城', '郸城县', '中国,河南省,周口市,郸城县', '3', 'dancheng', '0394', '477150', 'D', '115.17715', '33.64485'); +INSERT INTO `yoshop_region` VALUES ('1692', '1685', '淮阳', '淮阳县', '中国,河南省,周口市,淮阳县', '3', 'huaiyang', '0394', '466700', 'H', '114.88848', '33.73211'); +INSERT INTO `yoshop_region` VALUES ('1693', '1685', '太康', '太康县', '中国,河南省,周口市,太康县', '3', 'taikang', '0394', '461400', 'T', '114.83773', '34.06376'); +INSERT INTO `yoshop_region` VALUES ('1694', '1685', '鹿邑', '鹿邑县', '中国,河南省,周口市,鹿邑县', '3', 'luyi', '0394', '477200', 'L', '115.48553', '33.85931'); +INSERT INTO `yoshop_region` VALUES ('1695', '1685', '项城', '项城市', '中国,河南省,周口市,项城市', '3', 'xiangcheng', '0394', '466200', 'X', '114.87558', '33.4672'); +INSERT INTO `yoshop_region` VALUES ('1696', '1532', '驻马店', '驻马店市', '中国,河南省,驻马店市', '2', 'zhumadian', '0396', '463000', 'Z', '114.024736', '32.980169'); +INSERT INTO `yoshop_region` VALUES ('1697', '1696', '驿城', '驿城区', '中国,河南省,驻马店市,驿城区', '3', 'yicheng', '0396', '463000', null, '113.99377', '32.97316'); +INSERT INTO `yoshop_region` VALUES ('1698', '1696', '西平', '西平县', '中国,河南省,驻马店市,西平县', '3', 'xiping', '0396', '463900', 'X', '114.02322', '33.3845'); +INSERT INTO `yoshop_region` VALUES ('1699', '1696', '上蔡', '上蔡县', '中国,河南省,驻马店市,上蔡县', '3', 'shangcai', '0396', '463800', 'S', '114.26825', '33.26825'); +INSERT INTO `yoshop_region` VALUES ('1700', '1696', '平舆', '平舆县', '中国,河南省,驻马店市,平舆县', '3', 'pingyu', '0396', '463400', 'P', '114.63552', '32.95727'); +INSERT INTO `yoshop_region` VALUES ('1701', '1696', '正阳', '正阳县', '中国,河南省,驻马店市,正阳县', '3', 'zhengyang', '0396', '463600', 'Z', '114.38952', '32.6039'); +INSERT INTO `yoshop_region` VALUES ('1702', '1696', '确山', '确山县', '中国,河南省,驻马店市,确山县', '3', 'queshan', '0396', '463200', 'Q', '114.02917', '32.80281'); +INSERT INTO `yoshop_region` VALUES ('1703', '1696', '泌阳', '泌阳县', '中国,河南省,驻马店市,泌阳县', '3', 'biyang', '0396', '463700', 'M', '113.32681', '32.71781'); +INSERT INTO `yoshop_region` VALUES ('1704', '1696', '汝南', '汝南县', '中国,河南省,驻马店市,汝南县', '3', 'runan', '0396', '463300', 'R', '114.36138', '33.00461'); +INSERT INTO `yoshop_region` VALUES ('1705', '1696', '遂平', '遂平县', '中国,河南省,驻马店市,遂平县', '3', 'suiping', '0396', '463100', 'S', '114.01297', '33.14571'); +INSERT INTO `yoshop_region` VALUES ('1706', '1696', '新蔡', '新蔡县', '中国,河南省,驻马店市,新蔡县', '3', 'xincai', '0396', '463500', 'X', '114.98199', '32.7502'); +INSERT INTO `yoshop_region` VALUES ('1707', '1532', ' ', '直辖县级', '中国,河南省,直辖县级', '2', '', '', '', 'Z', '113.665412', '34.757975'); +INSERT INTO `yoshop_region` VALUES ('1708', '1707', '济源', '济源市', '中国,河南省,直辖县级,济源市', '3', 'jiyuan', '0391', '454650', 'J', '112.590047', '35.090378'); +INSERT INTO `yoshop_region` VALUES ('1709', '0', '湖北', '湖北省', '中国,湖北省', '1', 'hubei', '', '', 'H', '114.298572', '30.584355'); +INSERT INTO `yoshop_region` VALUES ('1710', '1709', '武汉', '武汉市', '中国,湖北省,武汉市', '2', 'wuhan', '', '430014', 'W', '114.298572', '30.584355'); +INSERT INTO `yoshop_region` VALUES ('1711', '1710', '江岸', '江岸区', '中国,湖北省,武汉市,江岸区', '3', 'jiang\'an', '027', '430014', 'J', '114.30943', '30.59982'); +INSERT INTO `yoshop_region` VALUES ('1712', '1710', '江汉', '江汉区', '中国,湖北省,武汉市,江汉区', '3', 'jianghan', '027', '430021', 'J', '114.27093', '30.60146'); +INSERT INTO `yoshop_region` VALUES ('1713', '1710', '硚口', '硚口区', '中国,湖北省,武汉市,硚口区', '3', 'qiaokou', '027', '430033', null, '114.26422', '30.56945'); +INSERT INTO `yoshop_region` VALUES ('1714', '1710', '汉阳', '汉阳区', '中国,湖北省,武汉市,汉阳区', '3', 'hanyang', '027', '430050', 'H', '114.27478', '30.54915'); +INSERT INTO `yoshop_region` VALUES ('1715', '1710', '武昌', '武昌区', '中国,湖北省,武汉市,武昌区', '3', 'wuchang', '027', '430061', 'W', '114.31589', '30.55389'); +INSERT INTO `yoshop_region` VALUES ('1716', '1710', '青山', '青山区', '中国,湖北省,武汉市,青山区', '3', 'qingshan', '027', '430080', 'Q', '114.39117', '30.63427'); +INSERT INTO `yoshop_region` VALUES ('1717', '1710', '洪山', '洪山区', '中国,湖北省,武汉市,洪山区', '3', 'hongshan', '027', '430070', 'H', '114.34375', '30.49989'); +INSERT INTO `yoshop_region` VALUES ('1718', '1710', '东西湖', '东西湖区', '中国,湖北省,武汉市,东西湖区', '3', 'dongxihu', '027', '430040', 'D', '114.13708', '30.61989'); +INSERT INTO `yoshop_region` VALUES ('1719', '1710', '汉南', '汉南区', '中国,湖北省,武汉市,汉南区', '3', 'hannan', '027', '430090', 'H', '114.08462', '30.30879'); +INSERT INTO `yoshop_region` VALUES ('1720', '1710', '蔡甸', '蔡甸区', '中国,湖北省,武汉市,蔡甸区', '3', 'caidian', '027', '430100', 'C', '114.02929', '30.58197'); +INSERT INTO `yoshop_region` VALUES ('1721', '1710', '江夏', '江夏区', '中国,湖北省,武汉市,江夏区', '3', 'jiangxia', '027', '430200', 'J', '114.31301', '30.34653'); +INSERT INTO `yoshop_region` VALUES ('1722', '1710', '黄陂', '黄陂区', '中国,湖北省,武汉市,黄陂区', '3', 'huangpi', '027', '432200', 'H', '114.37512', '30.88151'); +INSERT INTO `yoshop_region` VALUES ('1723', '1710', '新洲', '新洲区', '中国,湖北省,武汉市,新洲区', '3', 'xinzhou', '027', '431400', 'X', '114.80136', '30.84145'); +INSERT INTO `yoshop_region` VALUES ('1724', '1709', '黄石', '黄石市', '中国,湖北省,黄石市', '2', 'huangshi', '0714', '435003', 'H', '115.077048', '30.220074'); +INSERT INTO `yoshop_region` VALUES ('1725', '1724', '黄石港', '黄石港区', '中国,湖北省,黄石市,黄石港区', '3', 'huangshigang', '0714', '435000', 'H', '115.06604', '30.22279'); +INSERT INTO `yoshop_region` VALUES ('1726', '1724', '西塞山', '西塞山区', '中国,湖北省,黄石市,西塞山区', '3', 'xisaishan', '0714', '435001', 'X', '115.11016', '30.20487'); +INSERT INTO `yoshop_region` VALUES ('1727', '1724', '下陆', '下陆区', '中国,湖北省,黄石市,下陆区', '3', 'xialu', '0714', '435005', 'X', '114.96112', '30.17368'); +INSERT INTO `yoshop_region` VALUES ('1728', '1724', '铁山', '铁山区', '中国,湖北省,黄石市,铁山区', '3', 'tieshan', '0714', '435006', 'T', '114.90109', '30.20678'); +INSERT INTO `yoshop_region` VALUES ('1729', '1724', '阳新', '阳新县', '中国,湖北省,黄石市,阳新县', '3', 'yangxin', '0714', '435200', 'Y', '115.21527', '29.83038'); +INSERT INTO `yoshop_region` VALUES ('1730', '1724', '大冶', '大冶市', '中国,湖北省,黄石市,大冶市', '3', 'daye', '0714', '435100', 'D', '114.97174', '30.09438'); +INSERT INTO `yoshop_region` VALUES ('1731', '1709', '十堰', '十堰市', '中国,湖北省,十堰市', '2', 'shiyan', '0719', '442000', 'S', '110.785239', '32.647017'); +INSERT INTO `yoshop_region` VALUES ('1732', '1731', '茅箭', '茅箭区', '中国,湖北省,十堰市,茅箭区', '3', 'maojian', '0719', '442012', 'M', '110.81341', '32.59153'); +INSERT INTO `yoshop_region` VALUES ('1733', '1731', '张湾', '张湾区', '中国,湖北省,十堰市,张湾区', '3', 'zhangwan', '0719', '442001', 'Z', '110.77067', '32.65195'); +INSERT INTO `yoshop_region` VALUES ('1734', '1731', '郧阳', '郧阳区', '中国,湖北省,十堰市,郧阳区', '3', 'yunyang', '0719', '442500', 'Y', '110.81854', '32.83593'); +INSERT INTO `yoshop_region` VALUES ('1735', '1731', '郧西', '郧西县', '中国,湖北省,十堰市,郧西县', '3', 'yunxi', '0719', '442600', 'Y', '110.42556', '32.99349'); +INSERT INTO `yoshop_region` VALUES ('1736', '1731', '竹山', '竹山县', '中国,湖北省,十堰市,竹山县', '3', 'zhushan', '0719', '442200', 'Z', '110.23071', '32.22536'); +INSERT INTO `yoshop_region` VALUES ('1737', '1731', '竹溪', '竹溪县', '中国,湖北省,十堰市,竹溪县', '3', 'zhuxi', '0719', '442300', 'Z', '109.71798', '32.31901'); +INSERT INTO `yoshop_region` VALUES ('1738', '1731', '房县', '房县', '中国,湖北省,十堰市,房县', '3', 'fangxian', '0719', '442100', 'F', '110.74386', '32.05794'); +INSERT INTO `yoshop_region` VALUES ('1739', '1731', '丹江口', '丹江口市', '中国,湖北省,十堰市,丹江口市', '3', 'danjiangkou', '0719', '442700', 'D', '111.51525', '32.54085'); +INSERT INTO `yoshop_region` VALUES ('1740', '1709', '宜昌', '宜昌市', '中国,湖北省,宜昌市', '2', 'yichang', '0717', '443000', 'Y', '111.290843', '30.702636'); +INSERT INTO `yoshop_region` VALUES ('1741', '1740', '西陵', '西陵区', '中国,湖北省,宜昌市,西陵区', '3', 'xiling', '0717', '443000', 'X', '111.28573', '30.71077'); +INSERT INTO `yoshop_region` VALUES ('1742', '1740', '伍家岗', '伍家岗区', '中国,湖北省,宜昌市,伍家岗区', '3', 'wujiagang', '0717', '443001', 'W', '111.3609', '30.64434'); +INSERT INTO `yoshop_region` VALUES ('1743', '1740', '点军', '点军区', '中国,湖北省,宜昌市,点军区', '3', 'dianjun', '0717', '443006', 'D', '111.26828', '30.6934'); +INSERT INTO `yoshop_region` VALUES ('1744', '1740', '猇亭', '猇亭区', '中国,湖北省,宜昌市,猇亭区', '3', 'xiaoting', '0717', '443007', null, '111.44079', '30.52663'); +INSERT INTO `yoshop_region` VALUES ('1745', '1740', '夷陵', '夷陵区', '中国,湖北省,宜昌市,夷陵区', '3', 'yiling', '0717', '443100', 'Y', '111.3262', '30.76881'); +INSERT INTO `yoshop_region` VALUES ('1746', '1740', '远安', '远安县', '中国,湖北省,宜昌市,远安县', '3', 'yuan\'an', '0717', '444200', 'Y', '111.6416', '31.05989'); +INSERT INTO `yoshop_region` VALUES ('1747', '1740', '兴山', '兴山县', '中国,湖北省,宜昌市,兴山县', '3', 'xingshan', '0717', '443711', 'X', '110.74951', '31.34686'); +INSERT INTO `yoshop_region` VALUES ('1748', '1740', '秭归', '秭归县', '中国,湖北省,宜昌市,秭归县', '3', 'zigui', '0717', '443600', null, '110.98156', '30.82702'); +INSERT INTO `yoshop_region` VALUES ('1749', '1740', '长阳', '长阳土家族自治县', '中国,湖北省,宜昌市,长阳土家族自治县', '3', 'changyang', '0717', '443500', 'C', '111.20105', '30.47052'); +INSERT INTO `yoshop_region` VALUES ('1750', '1740', '五峰', '五峰土家族自治县', '中国,湖北省,宜昌市,五峰土家族自治县', '3', 'wufeng', '0717', '443413', 'W', '110.6748', '30.19856'); +INSERT INTO `yoshop_region` VALUES ('1751', '1740', '宜都', '宜都市', '中国,湖北省,宜昌市,宜都市', '3', 'yidu', '0717', '443300', 'Y', '111.45025', '30.37807'); +INSERT INTO `yoshop_region` VALUES ('1752', '1740', '当阳', '当阳市', '中国,湖北省,宜昌市,当阳市', '3', 'dangyang', '0717', '444100', 'D', '111.78912', '30.8208'); +INSERT INTO `yoshop_region` VALUES ('1753', '1740', '枝江', '枝江市', '中国,湖北省,宜昌市,枝江市', '3', 'zhijiang', '0717', '443200', 'Z', '111.76855', '30.42612'); +INSERT INTO `yoshop_region` VALUES ('1754', '1709', '襄阳', '襄阳市', '中国,湖北省,襄阳市', '2', 'xiangyang', '0710', '441021', 'X', '112.144146', '32.042426'); +INSERT INTO `yoshop_region` VALUES ('1755', '1754', '襄城', '襄城区', '中国,湖北省,襄阳市,襄城区', '3', 'xiangcheng', '0710', '441021', 'X', '112.13372', '32.01017'); +INSERT INTO `yoshop_region` VALUES ('1756', '1754', '樊城', '樊城区', '中国,湖北省,襄阳市,樊城区', '3', 'fancheng', '0710', '441001', 'F', '112.13546', '32.04482'); +INSERT INTO `yoshop_region` VALUES ('1757', '1754', '襄州', '襄州区', '中国,湖北省,襄阳市,襄州区', '3', 'xiangzhou', '0710', '441100', 'X', '112.150327', '32.015088'); +INSERT INTO `yoshop_region` VALUES ('1758', '1754', '南漳', '南漳县', '中国,湖北省,襄阳市,南漳县', '3', 'nanzhang', '0710', '441500', 'N', '111.84603', '31.77653'); +INSERT INTO `yoshop_region` VALUES ('1759', '1754', '谷城', '谷城县', '中国,湖北省,襄阳市,谷城县', '3', 'gucheng', '0710', '441700', 'G', '111.65267', '32.26377'); +INSERT INTO `yoshop_region` VALUES ('1760', '1754', '保康', '保康县', '中国,湖北省,襄阳市,保康县', '3', 'baokang', '0710', '441600', 'B', '111.26138', '31.87874'); +INSERT INTO `yoshop_region` VALUES ('1761', '1754', '老河口', '老河口市', '中国,湖北省,襄阳市,老河口市', '3', 'laohekou', '0710', '441800', 'L', '111.67117', '32.38476'); +INSERT INTO `yoshop_region` VALUES ('1762', '1754', '枣阳', '枣阳市', '中国,湖北省,襄阳市,枣阳市', '3', 'zaoyang', '0710', '441200', 'Z', '112.77444', '32.13142'); +INSERT INTO `yoshop_region` VALUES ('1763', '1754', '宜城', '宜城市', '中国,湖北省,襄阳市,宜城市', '3', 'yicheng', '0710', '441400', 'Y', '112.25772', '31.71972'); +INSERT INTO `yoshop_region` VALUES ('1764', '1709', '鄂州', '鄂州市', '中国,湖北省,鄂州市', '2', 'ezhou', '0711', '436000', 'E', '114.890593', '30.396536'); +INSERT INTO `yoshop_region` VALUES ('1765', '1764', '梁子湖', '梁子湖区', '中国,湖北省,鄂州市,梁子湖区', '3', 'liangzihu', '0711', '436064', 'L', '114.68463', '30.10003'); +INSERT INTO `yoshop_region` VALUES ('1766', '1764', '华容', '华容区', '中国,湖北省,鄂州市,华容区', '3', 'huarong', '0711', '436030', 'H', '114.73568', '30.53328'); +INSERT INTO `yoshop_region` VALUES ('1767', '1764', '鄂城', '鄂城区', '中国,湖北省,鄂州市,鄂城区', '3', 'echeng', '0711', '436000', 'E', '114.89158', '30.40024'); +INSERT INTO `yoshop_region` VALUES ('1768', '1709', '荆门', '荆门市', '中国,湖北省,荆门市', '2', 'jingmen', '0724', '448000', 'J', '112.204251', '31.03542'); +INSERT INTO `yoshop_region` VALUES ('1769', '1768', '东宝', '东宝区', '中国,湖北省,荆门市,东宝区', '3', 'dongbao', '0724', '448004', 'D', '112.20147', '31.05192'); +INSERT INTO `yoshop_region` VALUES ('1770', '1768', '掇刀', '掇刀区', '中国,湖北省,荆门市,掇刀区', '3', 'duodao', '0724', '448124', 'D', '112.208', '30.97316'); +INSERT INTO `yoshop_region` VALUES ('1771', '1768', '京山', '京山县', '中国,湖北省,荆门市,京山县', '3', 'jingshan', '0724', '431800', 'J', '113.11074', '31.0224'); +INSERT INTO `yoshop_region` VALUES ('1772', '1768', '沙洋', '沙洋县', '中国,湖北省,荆门市,沙洋县', '3', 'shayang', '0724', '448200', 'S', '112.58853', '30.70916'); +INSERT INTO `yoshop_region` VALUES ('1773', '1768', '钟祥', '钟祥市', '中国,湖北省,荆门市,钟祥市', '3', 'zhongxiang', '0724', '431900', 'Z', '112.58932', '31.1678'); +INSERT INTO `yoshop_region` VALUES ('1774', '1709', '孝感', '孝感市', '中国,湖北省,孝感市', '2', 'xiaogan', '0712', '432100', 'X', '113.926655', '30.926423'); +INSERT INTO `yoshop_region` VALUES ('1775', '1774', '孝南', '孝南区', '中国,湖北省,孝感市,孝南区', '3', 'xiaonan', '0712', '432100', 'X', '113.91111', '30.9168'); +INSERT INTO `yoshop_region` VALUES ('1776', '1774', '孝昌', '孝昌县', '中国,湖北省,孝感市,孝昌县', '3', 'xiaochang', '0712', '432900', 'X', '113.99795', '31.25799'); +INSERT INTO `yoshop_region` VALUES ('1777', '1774', '大悟', '大悟县', '中国,湖北省,孝感市,大悟县', '3', 'dawu', '0712', '432800', 'D', '114.12564', '31.56176'); +INSERT INTO `yoshop_region` VALUES ('1778', '1774', '云梦', '云梦县', '中国,湖北省,孝感市,云梦县', '3', 'yunmeng', '0712', '432500', 'Y', '113.75289', '31.02093'); +INSERT INTO `yoshop_region` VALUES ('1779', '1774', '应城', '应城市', '中国,湖北省,孝感市,应城市', '3', 'yingcheng', '0712', '432400', 'Y', '113.57287', '30.92834'); +INSERT INTO `yoshop_region` VALUES ('1780', '1774', '安陆', '安陆市', '中国,湖北省,孝感市,安陆市', '3', 'anlu', '0712', '432600', 'A', '113.68557', '31.25693'); +INSERT INTO `yoshop_region` VALUES ('1781', '1774', '汉川', '汉川市', '中国,湖北省,孝感市,汉川市', '3', 'hanchuan', '0712', '432300', 'H', '113.83898', '30.66117'); +INSERT INTO `yoshop_region` VALUES ('1782', '1709', '荆州', '荆州市', '中国,湖北省,荆州市', '2', 'jingzhou', '0716', '434000', 'J', '112.23813', '30.326857'); +INSERT INTO `yoshop_region` VALUES ('1783', '1782', '沙市', '沙市区', '中国,湖北省,荆州市,沙市区', '3', 'shashi', '0716', '434000', 'S', '112.25543', '30.31107'); +INSERT INTO `yoshop_region` VALUES ('1784', '1782', '荆州', '荆州区', '中国,湖北省,荆州市,荆州区', '3', 'jingzhou', '0716', '434020', 'J', '112.19006', '30.35264'); +INSERT INTO `yoshop_region` VALUES ('1785', '1782', '公安', '公安县', '中国,湖北省,荆州市,公安县', '3', 'gong\'an', '0716', '434300', 'G', '112.23242', '30.05902'); +INSERT INTO `yoshop_region` VALUES ('1786', '1782', '监利', '监利县', '中国,湖北省,荆州市,监利县', '3', 'jianli', '0716', '433300', 'J', '112.89462', '29.81494'); +INSERT INTO `yoshop_region` VALUES ('1787', '1782', '江陵', '江陵县', '中国,湖北省,荆州市,江陵县', '3', 'jiangling', '0716', '434101', 'J', '112.42468', '30.04174'); +INSERT INTO `yoshop_region` VALUES ('1788', '1782', '石首', '石首市', '中国,湖北省,荆州市,石首市', '3', 'shishou', '0716', '434400', 'S', '112.42636', '29.72127'); +INSERT INTO `yoshop_region` VALUES ('1789', '1782', '洪湖', '洪湖市', '中国,湖北省,荆州市,洪湖市', '3', 'honghu', '0716', '433200', 'H', '113.47598', '29.827'); +INSERT INTO `yoshop_region` VALUES ('1790', '1782', '松滋', '松滋市', '中国,湖北省,荆州市,松滋市', '3', 'songzi', '0716', '434200', 'S', '111.76739', '30.16965'); +INSERT INTO `yoshop_region` VALUES ('1791', '1709', '黄冈', '黄冈市', '中国,湖北省,黄冈市', '2', 'huanggang', '0713', '438000', 'H', '114.879365', '30.447711'); +INSERT INTO `yoshop_region` VALUES ('1792', '1791', '黄州', '黄州区', '中国,湖北省,黄冈市,黄州区', '3', 'huangzhou', '0713', '438000', 'H', '114.88008', '30.43436'); +INSERT INTO `yoshop_region` VALUES ('1793', '1791', '团风', '团风县', '中国,湖北省,黄冈市,团风县', '3', 'tuanfeng', '0713', '438800', 'T', '114.87228', '30.64359'); +INSERT INTO `yoshop_region` VALUES ('1794', '1791', '红安', '红安县', '中国,湖北省,黄冈市,红安县', '3', 'hong\'an', '0713', '438401', 'H', '114.6224', '31.28668'); +INSERT INTO `yoshop_region` VALUES ('1795', '1791', '罗田', '罗田县', '中国,湖北省,黄冈市,罗田县', '3', 'luotian', '0713', '438600', 'L', '115.39971', '30.78255'); +INSERT INTO `yoshop_region` VALUES ('1796', '1791', '英山', '英山县', '中国,湖北省,黄冈市,英山县', '3', 'yingshan', '0713', '438700', 'Y', '115.68142', '30.73516'); +INSERT INTO `yoshop_region` VALUES ('1797', '1791', '浠水', '浠水县', '中国,湖北省,黄冈市,浠水县', '3', 'xishui', '0713', '438200', null, '115.26913', '30.45265'); +INSERT INTO `yoshop_region` VALUES ('1798', '1791', '蕲春', '蕲春县', '中国,湖北省,黄冈市,蕲春县', '3', 'qichun', '0713', '435300', null, '115.43615', '30.22613'); +INSERT INTO `yoshop_region` VALUES ('1799', '1791', '黄梅', '黄梅县', '中国,湖北省,黄冈市,黄梅县', '3', 'huangmei', '0713', '435500', 'H', '115.94427', '30.07033'); +INSERT INTO `yoshop_region` VALUES ('1800', '1791', '麻城', '麻城市', '中国,湖北省,黄冈市,麻城市', '3', 'macheng', '0713', '438300', 'M', '115.00988', '31.17228'); +INSERT INTO `yoshop_region` VALUES ('1801', '1791', '武穴', '武穴市', '中国,湖北省,黄冈市,武穴市', '3', 'wuxue', '0713', '435400', 'W', '115.55975', '29.84446'); +INSERT INTO `yoshop_region` VALUES ('1802', '1709', '咸宁', '咸宁市', '中国,湖北省,咸宁市', '2', 'xianning', '0715', '437000', 'X', '114.328963', '29.832798'); +INSERT INTO `yoshop_region` VALUES ('1803', '1802', '咸安', '咸安区', '中国,湖北省,咸宁市,咸安区', '3', 'xian\'an', '0715', '437000', 'X', '114.29872', '29.8529'); +INSERT INTO `yoshop_region` VALUES ('1804', '1802', '嘉鱼', '嘉鱼县', '中国,湖北省,咸宁市,嘉鱼县', '3', 'jiayu', '0715', '437200', 'J', '113.93927', '29.97054'); +INSERT INTO `yoshop_region` VALUES ('1805', '1802', '通城', '通城县', '中国,湖北省,咸宁市,通城县', '3', 'tongcheng', '0715', '437400', 'T', '113.81582', '29.24568'); +INSERT INTO `yoshop_region` VALUES ('1806', '1802', '崇阳', '崇阳县', '中国,湖北省,咸宁市,崇阳县', '3', 'chongyang', '0715', '437500', 'C', '114.03982', '29.55564'); +INSERT INTO `yoshop_region` VALUES ('1807', '1802', '通山', '通山县', '中国,湖北省,咸宁市,通山县', '3', 'tongshan', '0715', '437600', 'T', '114.48239', '29.6063'); +INSERT INTO `yoshop_region` VALUES ('1808', '1802', '赤壁', '赤壁市', '中国,湖北省,咸宁市,赤壁市', '3', 'chibi', '0715', '437300', 'C', '113.90039', '29.72454'); +INSERT INTO `yoshop_region` VALUES ('1809', '1709', '随州', '随州市', '中国,湖北省,随州市', '2', 'suizhou', '0722', '441300', 'S', '113.37377', '31.717497'); +INSERT INTO `yoshop_region` VALUES ('1810', '1809', '曾都', '曾都区', '中国,湖北省,随州市,曾都区', '3', 'zengdu', '0722', '441300', 'Z', '113.37128', '31.71614'); +INSERT INTO `yoshop_region` VALUES ('1811', '1809', '随县', '随县', '中国,湖北省,随州市,随县', '3', 'suixian', '0722', '441309', 'S', '113.82663', '31.6179'); +INSERT INTO `yoshop_region` VALUES ('1812', '1809', '广水', '广水市', '中国,湖北省,随州市,广水市', '3', 'guangshui', '0722', '432700', 'G', '113.82663', '31.6179'); +INSERT INTO `yoshop_region` VALUES ('1813', '1709', '恩施', '恩施土家族苗族自治州', '中国,湖北省,恩施土家族苗族自治州', '2', 'enshi', '0718', '445000', 'E', '109.48699', '30.283114'); +INSERT INTO `yoshop_region` VALUES ('1814', '1813', '恩施', '恩施市', '中国,湖北省,恩施土家族苗族自治州,恩施市', '3', 'enshi', '0718', '445000', 'E', '109.47942', '30.29502'); +INSERT INTO `yoshop_region` VALUES ('1815', '1813', '利川', '利川市', '中国,湖北省,恩施土家族苗族自治州,利川市', '3', 'lichuan', '0718', '445400', 'L', '108.93591', '30.29117'); +INSERT INTO `yoshop_region` VALUES ('1816', '1813', '建始', '建始县', '中国,湖北省,恩施土家族苗族自治州,建始县', '3', 'jianshi', '0718', '445300', 'J', '109.72207', '30.60209'); +INSERT INTO `yoshop_region` VALUES ('1817', '1813', '巴东', '巴东县', '中国,湖北省,恩施土家族苗族自治州,巴东县', '3', 'badong', '0718', '444300', 'B', '110.34066', '31.04233'); +INSERT INTO `yoshop_region` VALUES ('1818', '1813', '宣恩', '宣恩县', '中国,湖北省,恩施土家族苗族自治州,宣恩县', '3', 'xuanen', '0718', '445500', 'X', '109.49179', '29.98714'); +INSERT INTO `yoshop_region` VALUES ('1819', '1813', '咸丰', '咸丰县', '中国,湖北省,恩施土家族苗族自治州,咸丰县', '3', 'xianfeng', '0718', '445600', 'X', '109.152', '29.67983'); +INSERT INTO `yoshop_region` VALUES ('1820', '1813', '来凤', '来凤县', '中国,湖北省,恩施土家族苗族自治州,来凤县', '3', 'laifeng', '0718', '445700', 'L', '109.40716', '29.49373'); +INSERT INTO `yoshop_region` VALUES ('1821', '1813', '鹤峰', '鹤峰县', '中国,湖北省,恩施土家族苗族自治州,鹤峰县', '3', 'hefeng', '0718', '445800', 'H', '110.03091', '29.89072'); +INSERT INTO `yoshop_region` VALUES ('1822', '1709', ' ', '直辖县级', '中国,湖北省,直辖县级', '2', '', '', '', 'Z', '114.298572', '30.584355'); +INSERT INTO `yoshop_region` VALUES ('1823', '1822', '仙桃', '仙桃市', '中国,湖北省,直辖县级,仙桃市', '3', 'xiantao', '0728', '433000', 'X', '113.453974', '30.364953'); +INSERT INTO `yoshop_region` VALUES ('1824', '1822', '潜江', '潜江市', '中国,湖北省,直辖县级,潜江市', '3', 'qianjiang', '0728', '433100', 'Q', '112.896866', '30.421215'); +INSERT INTO `yoshop_region` VALUES ('1825', '1822', '天门', '天门市', '中国,湖北省,直辖县级,天门市', '3', 'tianmen', '0728', '431700', 'T', '113.165862', '30.653061'); +INSERT INTO `yoshop_region` VALUES ('1826', '1822', '神农架', '神农架林区', '中国,湖北省,直辖县级,神农架林区', '3', 'shennongjia', '0719', '442400', 'S', '110.671525', '31.744449'); +INSERT INTO `yoshop_region` VALUES ('1827', '0', '湖南', '湖南省', '中国,湖南省', '1', 'hunan', '', '', 'H', '112.982279', '28.19409'); +INSERT INTO `yoshop_region` VALUES ('1828', '1827', '长沙', '长沙市', '中国,湖南省,长沙市', '2', 'changsha', '0731', '410005', 'C', '112.982279', '28.19409'); +INSERT INTO `yoshop_region` VALUES ('1829', '1828', '芙蓉', '芙蓉区', '中国,湖南省,长沙市,芙蓉区', '3', 'furong', '0731', '410011', null, '113.03176', '28.1844'); +INSERT INTO `yoshop_region` VALUES ('1830', '1828', '天心', '天心区', '中国,湖南省,长沙市,天心区', '3', 'tianxin', '0731', '410004', 'T', '112.98991', '28.1127'); +INSERT INTO `yoshop_region` VALUES ('1831', '1828', '岳麓', '岳麓区', '中国,湖南省,长沙市,岳麓区', '3', 'yuelu', '0731', '410013', 'Y', '112.93133', '28.2351'); +INSERT INTO `yoshop_region` VALUES ('1832', '1828', '开福', '开福区', '中国,湖南省,长沙市,开福区', '3', 'kaifu', '0731', '410008', 'K', '112.98623', '28.25585'); +INSERT INTO `yoshop_region` VALUES ('1833', '1828', '雨花', '雨花区', '中国,湖南省,长沙市,雨花区', '3', 'yuhua', '0731', '410011', 'Y', '113.03567', '28.13541'); +INSERT INTO `yoshop_region` VALUES ('1834', '1828', '望城', '望城区', '中国,湖南省,长沙市,望城区', '3', 'wangcheng', '0731', '410200', 'W', '112.819549', '28.347458'); +INSERT INTO `yoshop_region` VALUES ('1835', '1828', '长沙', '长沙县', '中国,湖南省,长沙市,长沙县', '3', 'changsha', '0731', '410100', 'C', '113.08071', '28.24595'); +INSERT INTO `yoshop_region` VALUES ('1836', '1828', '宁乡', '宁乡县', '中国,湖南省,长沙市,宁乡县', '3', 'ningxiang', '0731', '410600', 'N', '112.55749', '28.25358'); +INSERT INTO `yoshop_region` VALUES ('1837', '1828', '浏阳', '浏阳市', '中国,湖南省,长沙市,浏阳市', '3', 'liuyang', '0731', '410300', null, '113.64312', '28.16375'); +INSERT INTO `yoshop_region` VALUES ('1838', '1827', '株洲', '株洲市', '中国,湖南省,株洲市', '2', 'zhuzhou', '0731', '412000', 'Z', '113.151737', '27.835806'); +INSERT INTO `yoshop_region` VALUES ('1839', '1838', '荷塘', '荷塘区', '中国,湖南省,株洲市,荷塘区', '3', 'hetang', '0731', '412000', 'H', '113.17315', '27.85569'); +INSERT INTO `yoshop_region` VALUES ('1840', '1838', '芦淞', '芦淞区', '中国,湖南省,株洲市,芦淞区', '3', 'lusong', '0731', '412000', 'L', '113.15562', '27.78525'); +INSERT INTO `yoshop_region` VALUES ('1841', '1838', '石峰', '石峰区', '中国,湖南省,株洲市,石峰区', '3', 'shifeng', '0731', '412005', 'S', '113.11776', '27.87552'); +INSERT INTO `yoshop_region` VALUES ('1842', '1838', '天元', '天元区', '中国,湖南省,株洲市,天元区', '3', 'tianyuan', '0731', '412007', 'T', '113.12335', '27.83103'); +INSERT INTO `yoshop_region` VALUES ('1843', '1838', '株洲', '株洲县', '中国,湖南省,株洲市,株洲县', '3', 'zhuzhou', '0731', '412100', 'Z', '113.14428', '27.69826'); +INSERT INTO `yoshop_region` VALUES ('1844', '1838', '攸县', '攸县', '中国,湖南省,株洲市,攸县', '3', 'youxian', '0731', '412300', null, '113.34365', '27.00352'); +INSERT INTO `yoshop_region` VALUES ('1845', '1838', '茶陵', '茶陵县', '中国,湖南省,株洲市,茶陵县', '3', 'chaling', '0731', '412400', 'C', '113.54364', '26.7915'); +INSERT INTO `yoshop_region` VALUES ('1846', '1838', '炎陵', '炎陵县', '中国,湖南省,株洲市,炎陵县', '3', 'yanling', '0731', '412500', 'Y', '113.77163', '26.48818'); +INSERT INTO `yoshop_region` VALUES ('1847', '1838', '醴陵', '醴陵市', '中国,湖南省,株洲市,醴陵市', '3', 'liling', '0731', '412200', null, '113.49704', '27.64615'); +INSERT INTO `yoshop_region` VALUES ('1848', '1827', '湘潭', '湘潭市', '中国,湖南省,湘潭市', '2', 'xiangtan', '0731', '411100', 'X', '112.925083', '27.846725'); +INSERT INTO `yoshop_region` VALUES ('1849', '1848', '雨湖', '雨湖区', '中国,湖南省,湘潭市,雨湖区', '3', 'yuhu', '0731', '411100', 'Y', '112.90399', '27.86859'); +INSERT INTO `yoshop_region` VALUES ('1850', '1848', '岳塘', '岳塘区', '中国,湖南省,湘潭市,岳塘区', '3', 'yuetang', '0731', '411101', 'Y', '112.9606', '27.85784'); +INSERT INTO `yoshop_region` VALUES ('1851', '1848', '湘潭', '湘潭县', '中国,湖南省,湘潭市,湘潭县', '3', 'xiangtan', '0731', '411228', 'X', '112.9508', '27.77893'); +INSERT INTO `yoshop_region` VALUES ('1852', '1848', '湘乡', '湘乡市', '中国,湖南省,湘潭市,湘乡市', '3', 'xiangxiang', '0731', '411400', 'X', '112.53512', '27.73543'); +INSERT INTO `yoshop_region` VALUES ('1853', '1848', '韶山', '韶山市', '中国,湖南省,湘潭市,韶山市', '3', 'shaoshan', '0731', '411300', 'S', '112.52655', '27.91503'); +INSERT INTO `yoshop_region` VALUES ('1854', '1827', '衡阳', '衡阳市', '中国,湖南省,衡阳市', '2', 'hengyang', '0734', '421001', 'H', '112.607693', '26.900358'); +INSERT INTO `yoshop_region` VALUES ('1855', '1854', '珠晖', '珠晖区', '中国,湖南省,衡阳市,珠晖区', '3', 'zhuhui', '0734', '421002', 'Z', '112.62054', '26.89361'); +INSERT INTO `yoshop_region` VALUES ('1856', '1854', '雁峰', '雁峰区', '中国,湖南省,衡阳市,雁峰区', '3', 'yanfeng', '0734', '421001', 'Y', '112.61654', '26.88866'); +INSERT INTO `yoshop_region` VALUES ('1857', '1854', '石鼓', '石鼓区', '中国,湖南省,衡阳市,石鼓区', '3', 'shigu', '0734', '421005', 'S', '112.61069', '26.90232'); +INSERT INTO `yoshop_region` VALUES ('1858', '1854', '蒸湘', '蒸湘区', '中国,湖南省,衡阳市,蒸湘区', '3', 'zhengxiang', '0734', '421001', 'Z', '112.6033', '26.89651'); +INSERT INTO `yoshop_region` VALUES ('1859', '1854', '南岳', '南岳区', '中国,湖南省,衡阳市,南岳区', '3', 'nanyue', '0734', '421900', 'N', '112.7384', '27.23262'); +INSERT INTO `yoshop_region` VALUES ('1860', '1854', '衡阳', '衡阳县', '中国,湖南省,衡阳市,衡阳县', '3', 'hengyang', '0734', '421200', 'H', '112.37088', '26.9706'); +INSERT INTO `yoshop_region` VALUES ('1861', '1854', '衡南', '衡南县', '中国,湖南省,衡阳市,衡南县', '3', 'hengnan', '0734', '421131', 'H', '112.67788', '26.73828'); +INSERT INTO `yoshop_region` VALUES ('1862', '1854', '衡山', '衡山县', '中国,湖南省,衡阳市,衡山县', '3', 'hengshan', '0734', '421300', 'H', '112.86776', '27.23134'); +INSERT INTO `yoshop_region` VALUES ('1863', '1854', '衡东', '衡东县', '中国,湖南省,衡阳市,衡东县', '3', 'hengdong', '0734', '421400', 'H', '112.94833', '27.08093'); +INSERT INTO `yoshop_region` VALUES ('1864', '1854', '祁东', '祁东县', '中国,湖南省,衡阳市,祁东县', '3', 'qidong', '0734', '421600', 'Q', '112.09039', '26.79964'); +INSERT INTO `yoshop_region` VALUES ('1865', '1854', '耒阳', '耒阳市', '中国,湖南省,衡阳市,耒阳市', '3', 'leiyang', '0734', '421800', null, '112.85998', '26.42132'); +INSERT INTO `yoshop_region` VALUES ('1866', '1854', '常宁', '常宁市', '中国,湖南省,衡阳市,常宁市', '3', 'changning', '0734', '421500', 'C', '112.4009', '26.40692'); +INSERT INTO `yoshop_region` VALUES ('1867', '1827', '邵阳', '邵阳市', '中国,湖南省,邵阳市', '2', 'shaoyang', '0739', '422000', 'S', '111.46923', '27.237842'); +INSERT INTO `yoshop_region` VALUES ('1868', '1867', '双清', '双清区', '中国,湖南省,邵阳市,双清区', '3', 'shuangqing', '0739', '422001', 'S', '111.49715', '27.23291'); +INSERT INTO `yoshop_region` VALUES ('1869', '1867', '大祥', '大祥区', '中国,湖南省,邵阳市,大祥区', '3', 'daxiang', '0739', '422000', 'D', '111.45412', '27.23332'); +INSERT INTO `yoshop_region` VALUES ('1870', '1867', '北塔', '北塔区', '中国,湖南省,邵阳市,北塔区', '3', 'beita', '0739', '422007', 'B', '111.45219', '27.24648'); +INSERT INTO `yoshop_region` VALUES ('1871', '1867', '邵东', '邵东县', '中国,湖南省,邵阳市,邵东县', '3', 'shaodong', '0739', '422800', 'S', '111.74441', '27.2584'); +INSERT INTO `yoshop_region` VALUES ('1872', '1867', '新邵', '新邵县', '中国,湖南省,邵阳市,新邵县', '3', 'xinshao', '0739', '422900', 'X', '111.46066', '27.32169'); +INSERT INTO `yoshop_region` VALUES ('1873', '1867', '邵阳', '邵阳县', '中国,湖南省,邵阳市,邵阳县', '3', 'shaoyang', '0739', '422100', 'S', '111.27459', '26.99143'); +INSERT INTO `yoshop_region` VALUES ('1874', '1867', '隆回', '隆回县', '中国,湖南省,邵阳市,隆回县', '3', 'longhui', '0739', '422200', 'L', '111.03216', '27.10937'); +INSERT INTO `yoshop_region` VALUES ('1875', '1867', '洞口', '洞口县', '中国,湖南省,邵阳市,洞口县', '3', 'dongkou', '0739', '422300', 'D', '110.57388', '27.05462'); +INSERT INTO `yoshop_region` VALUES ('1876', '1867', '绥宁', '绥宁县', '中国,湖南省,邵阳市,绥宁县', '3', 'suining', '0739', '422600', 'S', '110.15576', '26.58636'); +INSERT INTO `yoshop_region` VALUES ('1877', '1867', '新宁', '新宁县', '中国,湖南省,邵阳市,新宁县', '3', 'xinning', '0739', '422700', 'X', '110.85131', '26.42936'); +INSERT INTO `yoshop_region` VALUES ('1878', '1867', '城步', '城步苗族自治县', '中国,湖南省,邵阳市,城步苗族自治县', '3', 'chengbu', '0739', '422500', 'C', '110.3222', '26.39048'); +INSERT INTO `yoshop_region` VALUES ('1879', '1867', '武冈', '武冈市', '中国,湖南省,邵阳市,武冈市', '3', 'wugang', '0739', '422400', 'W', '110.63281', '26.72817'); +INSERT INTO `yoshop_region` VALUES ('1880', '1827', '岳阳', '岳阳市', '中国,湖南省,岳阳市', '2', 'yueyang', '0730', '414000', 'Y', '113.132855', '29.37029'); +INSERT INTO `yoshop_region` VALUES ('1881', '1880', '岳阳楼', '岳阳楼区', '中国,湖南省,岳阳市,岳阳楼区', '3', 'yueyanglou', '0730', '414000', 'Y', '113.12942', '29.3719'); +INSERT INTO `yoshop_region` VALUES ('1882', '1880', '云溪', '云溪区', '中国,湖南省,岳阳市,云溪区', '3', 'yunxi', '0730', '414009', 'Y', '113.27713', '29.47357'); +INSERT INTO `yoshop_region` VALUES ('1883', '1880', '君山', '君山区', '中国,湖南省,岳阳市,君山区', '3', 'junshan', '0730', '414005', 'J', '113.00439', '29.45941'); +INSERT INTO `yoshop_region` VALUES ('1884', '1880', '岳阳', '岳阳县', '中国,湖南省,岳阳市,岳阳县', '3', 'yueyang', '0730', '414100', 'Y', '113.11987', '29.14314'); +INSERT INTO `yoshop_region` VALUES ('1885', '1880', '华容', '华容县', '中国,湖南省,岳阳市,华容县', '3', 'huarong', '0730', '414200', 'H', '112.54089', '29.53019'); +INSERT INTO `yoshop_region` VALUES ('1886', '1880', '湘阴', '湘阴县', '中国,湖南省,岳阳市,湘阴县', '3', 'xiangyin', '0730', '414600', 'X', '112.90911', '28.68922'); +INSERT INTO `yoshop_region` VALUES ('1887', '1880', '平江', '平江县', '中国,湖南省,岳阳市,平江县', '3', 'pingjiang', '0730', '414500', 'P', '113.58105', '28.70664'); +INSERT INTO `yoshop_region` VALUES ('1888', '1880', '汨罗', '汨罗市', '中国,湖南省,岳阳市,汨罗市', '3', 'miluo', '0730', '414400', null, '113.06707', '28.80631'); +INSERT INTO `yoshop_region` VALUES ('1889', '1880', '临湘', '临湘市', '中国,湖南省,岳阳市,临湘市', '3', 'linxiang', '0730', '414300', 'L', '113.4501', '29.47701'); +INSERT INTO `yoshop_region` VALUES ('1890', '1827', '常德', '常德市', '中国,湖南省,常德市', '2', 'changde', '0736', '415000', 'C', '111.691347', '29.040225'); +INSERT INTO `yoshop_region` VALUES ('1891', '1890', '武陵', '武陵区', '中国,湖南省,常德市,武陵区', '3', 'wuling', '0736', '415000', 'W', '111.69791', '29.02876'); +INSERT INTO `yoshop_region` VALUES ('1892', '1890', '鼎城', '鼎城区', '中国,湖南省,常德市,鼎城区', '3', 'dingcheng', '0736', '415101', 'D', '111.68078', '29.01859'); +INSERT INTO `yoshop_region` VALUES ('1893', '1890', '安乡', '安乡县', '中国,湖南省,常德市,安乡县', '3', 'anxiang', '0736', '415600', 'A', '112.16732', '29.41326'); +INSERT INTO `yoshop_region` VALUES ('1894', '1890', '汉寿', '汉寿县', '中国,湖南省,常德市,汉寿县', '3', 'hanshou', '0736', '415900', 'H', '111.96691', '28.90299'); +INSERT INTO `yoshop_region` VALUES ('1895', '1890', '澧县', '澧县', '中国,湖南省,常德市,澧县', '3', 'lixian', '0736', '415500', null, '111.75866', '29.63317'); +INSERT INTO `yoshop_region` VALUES ('1896', '1890', '临澧', '临澧县', '中国,湖南省,常德市,临澧县', '3', 'linli', '0736', '415200', 'L', '111.65161', '29.44163'); +INSERT INTO `yoshop_region` VALUES ('1897', '1890', '桃源', '桃源县', '中国,湖南省,常德市,桃源县', '3', 'taoyuan', '0736', '415700', 'T', '111.48892', '28.90474'); +INSERT INTO `yoshop_region` VALUES ('1898', '1890', '石门', '石门县', '中国,湖南省,常德市,石门县', '3', 'shimen', '0736', '415300', 'S', '111.37966', '29.58424'); +INSERT INTO `yoshop_region` VALUES ('1899', '1890', '津市', '津市市', '中国,湖南省,常德市,津市市', '3', 'jinshi', '0736', '415400', 'J', '111.87756', '29.60563'); +INSERT INTO `yoshop_region` VALUES ('1900', '1827', '张家界', '张家界市', '中国,湖南省,张家界市', '2', 'zhangjiajie', '0744', '427000', 'Z', '110.479921', '29.127401'); +INSERT INTO `yoshop_region` VALUES ('1901', '1900', '永定', '永定区', '中国,湖南省,张家界市,永定区', '3', 'yongding', '0744', '427000', 'Y', '110.47464', '29.13387'); +INSERT INTO `yoshop_region` VALUES ('1902', '1900', '武陵源', '武陵源区', '中国,湖南省,张家界市,武陵源区', '3', 'wulingyuan', '0744', '427400', 'W', '110.55026', '29.34574'); +INSERT INTO `yoshop_region` VALUES ('1903', '1900', '慈利', '慈利县', '中国,湖南省,张家界市,慈利县', '3', 'cili', '0744', '427200', 'C', '111.13946', '29.42989'); +INSERT INTO `yoshop_region` VALUES ('1904', '1900', '桑植', '桑植县', '中国,湖南省,张家界市,桑植县', '3', 'sangzhi', '0744', '427100', 'S', '110.16308', '29.39815'); +INSERT INTO `yoshop_region` VALUES ('1905', '1827', '益阳', '益阳市', '中国,湖南省,益阳市', '2', 'yiyang', '0737', '413000', 'Y', '112.355042', '28.570066'); +INSERT INTO `yoshop_region` VALUES ('1906', '1905', '资阳', '资阳区', '中国,湖南省,益阳市,资阳区', '3', 'ziyang', '0737', '413001', 'Z', '112.32447', '28.59095'); +INSERT INTO `yoshop_region` VALUES ('1907', '1905', '赫山', '赫山区', '中国,湖南省,益阳市,赫山区', '3', 'heshan', '0737', '413002', 'H', '112.37265', '28.57425'); +INSERT INTO `yoshop_region` VALUES ('1908', '1905', '南县', '南县', '中国,湖南省,益阳市,南县', '3', 'nanxian', '0737', '413200', 'N', '112.3963', '29.36159'); +INSERT INTO `yoshop_region` VALUES ('1909', '1905', '桃江', '桃江县', '中国,湖南省,益阳市,桃江县', '3', 'taojiang', '0737', '413400', 'T', '112.1557', '28.51814'); +INSERT INTO `yoshop_region` VALUES ('1910', '1905', '安化', '安化县', '中国,湖南省,益阳市,安化县', '3', 'anhua', '0737', '413500', 'A', '111.21298', '28.37424'); +INSERT INTO `yoshop_region` VALUES ('1911', '1905', '沅江', '沅江市', '中国,湖南省,益阳市,沅江市', '3', 'yuanjiang', '0737', '413100', null, '112.35427', '28.84403'); +INSERT INTO `yoshop_region` VALUES ('1912', '1827', '郴州', '郴州市', '中国,湖南省,郴州市', '2', 'chenzhou', '0735', '423000', 'C', '113.032067', '25.793589'); +INSERT INTO `yoshop_region` VALUES ('1913', '1912', '北湖', '北湖区', '中国,湖南省,郴州市,北湖区', '3', 'beihu', '0735', '423000', 'B', '113.01103', '25.78405'); +INSERT INTO `yoshop_region` VALUES ('1914', '1912', '苏仙', '苏仙区', '中国,湖南省,郴州市,苏仙区', '3', 'suxian', '0735', '423000', 'S', '113.04226', '25.80045'); +INSERT INTO `yoshop_region` VALUES ('1915', '1912', '桂阳', '桂阳县', '中国,湖南省,郴州市,桂阳县', '3', 'guiyang', '0735', '424400', 'G', '112.73364', '25.75406'); +INSERT INTO `yoshop_region` VALUES ('1916', '1912', '宜章', '宜章县', '中国,湖南省,郴州市,宜章县', '3', 'yizhang', '0735', '424200', 'Y', '112.95147', '25.39931'); +INSERT INTO `yoshop_region` VALUES ('1917', '1912', '永兴', '永兴县', '中国,湖南省,郴州市,永兴县', '3', 'yongxing', '0735', '423300', 'Y', '113.11242', '26.12646'); +INSERT INTO `yoshop_region` VALUES ('1918', '1912', '嘉禾', '嘉禾县', '中国,湖南省,郴州市,嘉禾县', '3', 'jiahe', '0735', '424500', 'J', '112.36935', '25.58795'); +INSERT INTO `yoshop_region` VALUES ('1919', '1912', '临武', '临武县', '中国,湖南省,郴州市,临武县', '3', 'linwu', '0735', '424300', 'L', '112.56369', '25.27602'); +INSERT INTO `yoshop_region` VALUES ('1920', '1912', '汝城', '汝城县', '中国,湖南省,郴州市,汝城县', '3', 'rucheng', '0735', '424100', 'R', '113.68582', '25.55204'); +INSERT INTO `yoshop_region` VALUES ('1921', '1912', '桂东', '桂东县', '中国,湖南省,郴州市,桂东县', '3', 'guidong', '0735', '423500', 'G', '113.9468', '26.07987'); +INSERT INTO `yoshop_region` VALUES ('1922', '1912', '安仁', '安仁县', '中国,湖南省,郴州市,安仁县', '3', 'anren', '0735', '423600', 'A', '113.26944', '26.70931'); +INSERT INTO `yoshop_region` VALUES ('1923', '1912', '资兴', '资兴市', '中国,湖南省,郴州市,资兴市', '3', 'zixing', '0735', '423400', 'Z', '113.23724', '25.97668'); +INSERT INTO `yoshop_region` VALUES ('1924', '1827', '永州', '永州市', '中国,湖南省,永州市', '2', 'yongzhou', '0746', '425000', 'Y', '111.608019', '26.434516'); +INSERT INTO `yoshop_region` VALUES ('1925', '1924', '零陵', '零陵区', '中国,湖南省,永州市,零陵区', '3', 'lingling', '0746', '425100', 'L', '111.62103', '26.22109'); +INSERT INTO `yoshop_region` VALUES ('1926', '1924', '冷水滩', '冷水滩区', '中国,湖南省,永州市,冷水滩区', '3', 'lengshuitan', '0746', '425100', 'L', '111.59214', '26.46107'); +INSERT INTO `yoshop_region` VALUES ('1927', '1924', '祁阳', '祁阳县', '中国,湖南省,永州市,祁阳县', '3', 'qiyang', '0746', '426100', 'Q', '111.84011', '26.58009'); +INSERT INTO `yoshop_region` VALUES ('1928', '1924', '东安', '东安县', '中国,湖南省,永州市,东安县', '3', 'dong\'an', '0746', '425900', 'D', '111.3164', '26.39202'); +INSERT INTO `yoshop_region` VALUES ('1929', '1924', '双牌', '双牌县', '中国,湖南省,永州市,双牌县', '3', 'shuangpai', '0746', '425200', 'S', '111.65927', '25.95988'); +INSERT INTO `yoshop_region` VALUES ('1930', '1924', '道县', '道县', '中国,湖南省,永州市,道县', '3', 'daoxian', '0746', '425300', 'D', '111.60195', '25.52766'); +INSERT INTO `yoshop_region` VALUES ('1931', '1924', '江永', '江永县', '中国,湖南省,永州市,江永县', '3', 'jiangyong', '0746', '425400', 'J', '111.34082', '25.27233'); +INSERT INTO `yoshop_region` VALUES ('1932', '1924', '宁远', '宁远县', '中国,湖南省,永州市,宁远县', '3', 'ningyuan', '0746', '425600', 'N', '111.94625', '25.56913'); +INSERT INTO `yoshop_region` VALUES ('1933', '1924', '蓝山', '蓝山县', '中国,湖南省,永州市,蓝山县', '3', 'lanshan', '0746', '425800', 'L', '112.19363', '25.36794'); +INSERT INTO `yoshop_region` VALUES ('1934', '1924', '新田', '新田县', '中国,湖南省,永州市,新田县', '3', 'xintian', '0746', '425700', 'X', '112.22103', '25.9095'); +INSERT INTO `yoshop_region` VALUES ('1935', '1924', '江华', '江华瑶族自治县', '中国,湖南省,永州市,江华瑶族自治县', '3', 'jianghua', '0746', '425500', 'J', '111.58847', '25.1845'); +INSERT INTO `yoshop_region` VALUES ('1936', '1827', '怀化', '怀化市', '中国,湖南省,怀化市', '2', 'huaihua', '0745', '418000', 'H', '109.97824', '27.550082'); +INSERT INTO `yoshop_region` VALUES ('1937', '1936', '鹤城', '鹤城区', '中国,湖南省,怀化市,鹤城区', '3', 'hecheng', '0745', '418000', 'H', '109.96509', '27.54942'); +INSERT INTO `yoshop_region` VALUES ('1938', '1936', '中方', '中方县', '中国,湖南省,怀化市,中方县', '3', 'zhongfang', '0745', '418005', 'Z', '109.94497', '27.43988'); +INSERT INTO `yoshop_region` VALUES ('1939', '1936', '沅陵', '沅陵县', '中国,湖南省,怀化市,沅陵县', '3', 'yuanling', '0745', '419600', null, '110.39633', '28.45548'); +INSERT INTO `yoshop_region` VALUES ('1940', '1936', '辰溪', '辰溪县', '中国,湖南省,怀化市,辰溪县', '3', 'chenxi', '0745', '419500', 'C', '110.18942', '28.00406'); +INSERT INTO `yoshop_region` VALUES ('1941', '1936', '溆浦', '溆浦县', '中国,湖南省,怀化市,溆浦县', '3', 'xupu', '0745', '419300', null, '110.59384', '27.90836'); +INSERT INTO `yoshop_region` VALUES ('1942', '1936', '会同', '会同县', '中国,湖南省,怀化市,会同县', '3', 'huitong', '0745', '418300', 'H', '109.73568', '26.88716'); +INSERT INTO `yoshop_region` VALUES ('1943', '1936', '麻阳', '麻阳苗族自治县', '中国,湖南省,怀化市,麻阳苗族自治县', '3', 'mayang', '0745', '419400', 'M', '109.80194', '27.866'); +INSERT INTO `yoshop_region` VALUES ('1944', '1936', '新晃', '新晃侗族自治县', '中国,湖南省,怀化市,新晃侗族自治县', '3', 'xinhuang', '0745', '419200', 'X', '109.17166', '27.35937'); +INSERT INTO `yoshop_region` VALUES ('1945', '1936', '芷江', '芷江侗族自治县', '中国,湖南省,怀化市,芷江侗族自治县', '3', 'zhijiang', '0745', '419100', null, '109.6849', '27.44297'); +INSERT INTO `yoshop_region` VALUES ('1946', '1936', '靖州', '靖州苗族侗族自治县', '中国,湖南省,怀化市,靖州苗族侗族自治县', '3', 'jingzhou', '0745', '418400', 'J', '109.69821', '26.57651'); +INSERT INTO `yoshop_region` VALUES ('1947', '1936', '通道', '通道侗族自治县', '中国,湖南省,怀化市,通道侗族自治县', '3', 'tongdao', '0745', '418500', 'T', '109.78515', '26.1571'); +INSERT INTO `yoshop_region` VALUES ('1948', '1936', '洪江', '洪江市', '中国,湖南省,怀化市,洪江市', '3', 'hongjiang', '0745', '418100', 'H', '109.83651', '27.20922'); +INSERT INTO `yoshop_region` VALUES ('1949', '1827', '娄底', '娄底市', '中国,湖南省,娄底市', '2', 'loudi', '0738', '417000', 'L', '112.008497', '27.728136'); +INSERT INTO `yoshop_region` VALUES ('1950', '1949', '娄星', '娄星区', '中国,湖南省,娄底市,娄星区', '3', 'louxing', '0738', '417000', 'L', '112.00193', '27.72992'); +INSERT INTO `yoshop_region` VALUES ('1951', '1949', '双峰', '双峰县', '中国,湖南省,娄底市,双峰县', '3', 'shuangfeng', '0738', '417700', 'S', '112.19921', '27.45418'); +INSERT INTO `yoshop_region` VALUES ('1952', '1949', '新化', '新化县', '中国,湖南省,娄底市,新化县', '3', 'xinhua', '0738', '417600', 'X', '111.32739', '27.7266'); +INSERT INTO `yoshop_region` VALUES ('1953', '1949', '冷水江', '冷水江市', '中国,湖南省,娄底市,冷水江市', '3', 'lengshuijiang', '0738', '417500', 'L', '111.43554', '27.68147'); +INSERT INTO `yoshop_region` VALUES ('1954', '1949', '涟源', '涟源市', '中国,湖南省,娄底市,涟源市', '3', 'lianyuan', '0738', '417100', 'L', '111.67233', '27.68831'); +INSERT INTO `yoshop_region` VALUES ('1955', '1827', '湘西', '湘西土家族苗族自治州', '中国,湖南省,湘西土家族苗族自治州', '2', 'xiangxi', '0743', '416000', 'X', '109.739735', '28.314296'); +INSERT INTO `yoshop_region` VALUES ('1956', '1955', '吉首', '吉首市', '中国,湖南省,湘西土家族苗族自治州,吉首市', '3', 'jishou', '0743', '416000', 'J', '109.69799', '28.26247'); +INSERT INTO `yoshop_region` VALUES ('1957', '1955', '泸溪', '泸溪县', '中国,湖南省,湘西土家族苗族自治州,泸溪县', '3', 'luxi', '0743', '416100', null, '110.21682', '28.2205'); +INSERT INTO `yoshop_region` VALUES ('1958', '1955', '凤凰', '凤凰县', '中国,湖南省,湘西土家族苗族自治州,凤凰县', '3', 'fenghuang', '0743', '416200', 'F', '109.60156', '27.94822'); +INSERT INTO `yoshop_region` VALUES ('1959', '1955', '花垣', '花垣县', '中国,湖南省,湘西土家族苗族自治州,花垣县', '3', 'huayuan', '0743', '416400', 'H', '109.48217', '28.5721'); +INSERT INTO `yoshop_region` VALUES ('1960', '1955', '保靖', '保靖县', '中国,湖南省,湘西土家族苗族自治州,保靖县', '3', 'baojing', '0743', '416500', 'B', '109.66049', '28.69997'); +INSERT INTO `yoshop_region` VALUES ('1961', '1955', '古丈', '古丈县', '中国,湖南省,湘西土家族苗族自治州,古丈县', '3', 'guzhang', '0743', '416300', 'G', '109.94812', '28.61944'); +INSERT INTO `yoshop_region` VALUES ('1962', '1955', '永顺', '永顺县', '中国,湖南省,湘西土家族苗族自治州,永顺县', '3', 'yongshun', '0743', '416700', 'Y', '109.85266', '29.00103'); +INSERT INTO `yoshop_region` VALUES ('1963', '1955', '龙山', '龙山县', '中国,湖南省,湘西土家族苗族自治州,龙山县', '3', 'longshan', '0743', '416800', 'L', '109.4432', '29.45693'); +INSERT INTO `yoshop_region` VALUES ('1964', '0', '广东', '广东省', '中国,广东省', '1', 'guangdong', '', '', 'G', '113.280637', '23.125178'); +INSERT INTO `yoshop_region` VALUES ('1965', '1964', '广州', '广州市', '中国,广东省,广州市', '2', 'guangzhou', '020', '510032', 'G', '113.280637', '23.125178'); +INSERT INTO `yoshop_region` VALUES ('1966', '1965', '荔湾', '荔湾区', '中国,广东省,广州市,荔湾区', '3', 'liwan', '020', '510170', 'L', '113.2442', '23.12592'); +INSERT INTO `yoshop_region` VALUES ('1967', '1965', '越秀', '越秀区', '中国,广东省,广州市,越秀区', '3', 'yuexiu', '020', '510030', 'Y', '113.26683', '23.12897'); +INSERT INTO `yoshop_region` VALUES ('1968', '1965', '海珠', '海珠区', '中国,广东省,广州市,海珠区', '3', 'haizhu', '020', '510300', 'H', '113.26197', '23.10379'); +INSERT INTO `yoshop_region` VALUES ('1969', '1965', '天河', '天河区', '中国,广东省,广州市,天河区', '3', 'tianhe', '020', '510665', 'T', '113.36112', '23.12467'); +INSERT INTO `yoshop_region` VALUES ('1970', '1965', '白云', '白云区', '中国,广东省,广州市,白云区', '3', 'baiyun', '020', '510405', 'B', '113.27307', '23.15787'); +INSERT INTO `yoshop_region` VALUES ('1971', '1965', '黄埔', '黄埔区', '中国,广东省,广州市,黄埔区', '3', 'huangpu', '020', '510700', 'H', '113.45895', '23.10642'); +INSERT INTO `yoshop_region` VALUES ('1972', '1965', '番禺', '番禺区', '中国,广东省,广州市,番禺区', '3', 'panyu', '020', '511400', 'F', '113.38397', '22.93599'); +INSERT INTO `yoshop_region` VALUES ('1973', '1965', '花都', '花都区', '中国,广东省,广州市,花都区', '3', 'huadu', '020', '510800', 'H', '113.22033', '23.40358'); +INSERT INTO `yoshop_region` VALUES ('1974', '1965', '南沙', '南沙区', '中国,广东省,广州市,南沙区', '3', 'nansha', '020', '511458', 'N', '113.60845', '22.77144'); +INSERT INTO `yoshop_region` VALUES ('1975', '1965', '从化', '从化区', '中国,广东省,广州市,从化区', '3', 'conghua', '020', '510900', 'C', '113.587386', '23.545283'); +INSERT INTO `yoshop_region` VALUES ('1976', '1965', '增城', '增城区', '中国,广东省,广州市,增城区', '3', 'zengcheng', '020', '511300', 'Z', '113.829579', '23.290497'); +INSERT INTO `yoshop_region` VALUES ('1977', '1964', '韶关', '韶关市', '中国,广东省,韶关市', '2', 'shaoguan', '0751', '512002', 'S', '113.591544', '24.801322'); +INSERT INTO `yoshop_region` VALUES ('1978', '1977', '武江', '武江区', '中国,广东省,韶关市,武江区', '3', 'wujiang', '0751', '512026', 'W', '113.58767', '24.79264'); +INSERT INTO `yoshop_region` VALUES ('1979', '1977', '浈江', '浈江区', '中国,广东省,韶关市,浈江区', '3', 'zhenjiang', '0751', '512023', null, '113.61109', '24.80438'); +INSERT INTO `yoshop_region` VALUES ('1980', '1977', '曲江', '曲江区', '中国,广东省,韶关市,曲江区', '3', 'qujiang', '0751', '512101', 'Q', '113.60165', '24.67915'); +INSERT INTO `yoshop_region` VALUES ('1981', '1977', '始兴', '始兴县', '中国,广东省,韶关市,始兴县', '3', 'shixing', '0751', '512500', 'S', '114.06799', '24.94759'); +INSERT INTO `yoshop_region` VALUES ('1982', '1977', '仁化', '仁化县', '中国,广东省,韶关市,仁化县', '3', 'renhua', '0751', '512300', 'R', '113.74737', '25.08742'); +INSERT INTO `yoshop_region` VALUES ('1983', '1977', '翁源', '翁源县', '中国,广东省,韶关市,翁源县', '3', 'wengyuan', '0751', '512600', 'W', '114.13385', '24.3495'); +INSERT INTO `yoshop_region` VALUES ('1984', '1977', '乳源', '乳源瑶族自治县', '中国,广东省,韶关市,乳源瑶族自治县', '3', 'ruyuan', '0751', '512700', 'R', '113.27734', '24.77803'); +INSERT INTO `yoshop_region` VALUES ('1985', '1977', '新丰', '新丰县', '中国,广东省,韶关市,新丰县', '3', 'xinfeng', '0751', '511100', 'X', '114.20788', '24.05924'); +INSERT INTO `yoshop_region` VALUES ('1986', '1977', '乐昌', '乐昌市', '中国,广东省,韶关市,乐昌市', '3', 'lechang', '0751', '512200', 'L', '113.35653', '25.12799'); +INSERT INTO `yoshop_region` VALUES ('1987', '1977', '南雄', '南雄市', '中国,广东省,韶关市,南雄市', '3', 'nanxiong', '0751', '512400', 'N', '114.30966', '25.11706'); +INSERT INTO `yoshop_region` VALUES ('1988', '1964', '深圳', '深圳市', '中国,广东省,深圳市', '2', 'shenzhen', '0755', '518035', 'S', '114.085947', '22.547'); +INSERT INTO `yoshop_region` VALUES ('1989', '1988', '罗湖', '罗湖区', '中国,广东省,深圳市,罗湖区', '3', 'luohu', '0755', '518021', 'L', '114.13116', '22.54836'); +INSERT INTO `yoshop_region` VALUES ('1990', '1988', '福田', '福田区', '中国,广东省,深圳市,福田区', '3', 'futian', '0755', '518048', 'F', '114.05571', '22.52245'); +INSERT INTO `yoshop_region` VALUES ('1991', '1988', '南山', '南山区', '中国,广东省,深圳市,南山区', '3', 'nanshan', '0755', '518051', 'N', '113.93029', '22.53291'); +INSERT INTO `yoshop_region` VALUES ('1992', '1988', '宝安', '宝安区', '中国,广东省,深圳市,宝安区', '3', 'bao\'an', '0755', '518101', 'B', '113.88311', '22.55371'); +INSERT INTO `yoshop_region` VALUES ('1993', '1988', '龙岗', '龙岗区', '中国,广东省,深圳市,龙岗区', '3', 'longgang', '0755', '518172', 'L', '114.24771', '22.71986'); +INSERT INTO `yoshop_region` VALUES ('1994', '1988', '盐田', '盐田区', '中国,广东省,深圳市,盐田区', '3', 'yantian', '0755', '518081', 'Y', '114.23733', '22.5578'); +INSERT INTO `yoshop_region` VALUES ('1995', '1988', '光明新区', '光明新区', '中国,广东省,深圳市,光明新区', '3', 'guangmingxinqu', '0755', '518100', 'G', '113.896026', '22.777292'); +INSERT INTO `yoshop_region` VALUES ('1996', '1988', '坪山新区', '坪山新区', '中国,广东省,深圳市,坪山新区', '3', 'pingshanxinqu', '0755', '518000', 'P', '114.34637', '22.690529'); +INSERT INTO `yoshop_region` VALUES ('1997', '1988', '大鹏新区', '大鹏新区', '中国,广东省,深圳市,大鹏新区', '3', 'dapengxinqu', '0755', '518000', 'D', '114.479901', '22.587862'); +INSERT INTO `yoshop_region` VALUES ('1998', '1988', '龙华新区', '龙华新区', '中国,广东省,深圳市,龙华新区', '3', 'longhuaxinqu', '0755', '518100', 'L', '114.036585', '22.68695'); +INSERT INTO `yoshop_region` VALUES ('1999', '1964', '珠海', '珠海市', '中国,广东省,珠海市', '2', 'zhuhai', '0756', '519000', 'Z', '113.552724', '22.255899'); +INSERT INTO `yoshop_region` VALUES ('2000', '1999', '香洲', '香洲区', '中国,广东省,珠海市,香洲区', '3', 'xiangzhou', '0756', '519000', 'X', '113.5435', '22.26654'); +INSERT INTO `yoshop_region` VALUES ('2001', '1999', '斗门', '斗门区', '中国,广东省,珠海市,斗门区', '3', 'doumen', '0756', '519110', 'D', '113.29644', '22.20898'); +INSERT INTO `yoshop_region` VALUES ('2002', '1999', '金湾', '金湾区', '中国,广东省,珠海市,金湾区', '3', 'jinwan', '0756', '519040', 'J', '113.36361', '22.14691'); +INSERT INTO `yoshop_region` VALUES ('2003', '1964', '汕头', '汕头市', '中国,广东省,汕头市', '2', 'shantou', '0754', '515041', 'S', '116.708463', '23.37102'); +INSERT INTO `yoshop_region` VALUES ('2004', '2003', '龙湖', '龙湖区', '中国,广东省,汕头市,龙湖区', '3', 'longhu', '0754', '515041', 'L', '116.71641', '23.37166'); +INSERT INTO `yoshop_region` VALUES ('2005', '2003', '金平', '金平区', '中国,广东省,汕头市,金平区', '3', 'jinping', '0754', '515041', 'J', '116.70364', '23.36637'); +INSERT INTO `yoshop_region` VALUES ('2006', '2003', '濠江', '濠江区', '中国,广东省,汕头市,濠江区', '3', 'haojiang', '0754', '515071', null, '116.72659', '23.28588'); +INSERT INTO `yoshop_region` VALUES ('2007', '2003', '潮阳', '潮阳区', '中国,广东省,汕头市,潮阳区', '3', 'chaoyang', '0754', '515100', 'C', '116.6015', '23.26485'); +INSERT INTO `yoshop_region` VALUES ('2008', '2003', '潮南', '潮南区', '中国,广东省,汕头市,潮南区', '3', 'chaonan', '0754', '515144', 'C', '116.43188', '23.25'); +INSERT INTO `yoshop_region` VALUES ('2009', '2003', '澄海', '澄海区', '中国,广东省,汕头市,澄海区', '3', 'chenghai', '0754', '515800', 'C', '116.75589', '23.46728'); +INSERT INTO `yoshop_region` VALUES ('2010', '2003', '南澳', '南澳县', '中国,广东省,汕头市,南澳县', '3', 'nanao', '0754', '515900', 'N', '117.01889', '23.4223'); +INSERT INTO `yoshop_region` VALUES ('2011', '1964', '佛山', '佛山市', '中国,广东省,佛山市', '2', 'foshan', '0757', '528000', 'F', '113.122717', '23.028762'); +INSERT INTO `yoshop_region` VALUES ('2012', '2011', '禅城', '禅城区', '中国,广东省,佛山市,禅城区', '3', 'chancheng', '0757', '528000', null, '113.1228', '23.00842'); +INSERT INTO `yoshop_region` VALUES ('2013', '2011', '南海', '南海区', '中国,广东省,佛山市,南海区', '3', 'nanhai', '0757', '528251', 'N', '113.14299', '23.02877'); +INSERT INTO `yoshop_region` VALUES ('2014', '2011', '顺德', '顺德区', '中国,广东省,佛山市,顺德区', '3', 'shunde', '0757', '528300', 'S', '113.29394', '22.80452'); +INSERT INTO `yoshop_region` VALUES ('2015', '2011', '三水', '三水区', '中国,广东省,佛山市,三水区', '3', 'sanshui', '0757', '528133', 'S', '112.89703', '23.15564'); +INSERT INTO `yoshop_region` VALUES ('2016', '2011', '高明', '高明区', '中国,广东省,佛山市,高明区', '3', 'gaoming', '0757', '528500', 'G', '112.89254', '22.90022'); +INSERT INTO `yoshop_region` VALUES ('2017', '1964', '江门', '江门市', '中国,广东省,江门市', '2', 'jiangmen', '0750', '529000', 'J', '113.094942', '22.590431'); +INSERT INTO `yoshop_region` VALUES ('2018', '2017', '蓬江', '蓬江区', '中国,广东省,江门市,蓬江区', '3', 'pengjiang', '0750', '529000', 'P', '113.07849', '22.59515'); +INSERT INTO `yoshop_region` VALUES ('2019', '2017', '江海', '江海区', '中国,广东省,江门市,江海区', '3', 'jianghai', '0750', '529040', 'J', '113.11099', '22.56024'); +INSERT INTO `yoshop_region` VALUES ('2020', '2017', '新会', '新会区', '中国,广东省,江门市,新会区', '3', 'xinhui', '0750', '529100', 'X', '113.03225', '22.45876'); +INSERT INTO `yoshop_region` VALUES ('2021', '2017', '台山', '台山市', '中国,广东省,江门市,台山市', '3', 'taishan', '0750', '529200', 'T', '112.79382', '22.2515'); +INSERT INTO `yoshop_region` VALUES ('2022', '2017', '开平', '开平市', '中国,广东省,江门市,开平市', '3', 'kaiping', '0750', '529337', 'K', '112.69842', '22.37622'); +INSERT INTO `yoshop_region` VALUES ('2023', '2017', '鹤山', '鹤山市', '中国,广东省,江门市,鹤山市', '3', 'heshan', '0750', '529700', 'H', '112.96429', '22.76523'); +INSERT INTO `yoshop_region` VALUES ('2024', '2017', '恩平', '恩平市', '中国,广东省,江门市,恩平市', '3', 'enping', '0750', '529400', 'E', '112.30496', '22.18288'); +INSERT INTO `yoshop_region` VALUES ('2025', '1964', '湛江', '湛江市', '中国,广东省,湛江市', '2', 'zhanjiang', '0759', '524047', 'Z', '110.405529', '21.195338'); +INSERT INTO `yoshop_region` VALUES ('2026', '2025', '赤坎', '赤坎区', '中国,广东省,湛江市,赤坎区', '3', 'chikan', '0759', '524033', 'C', '110.36592', '21.26606'); +INSERT INTO `yoshop_region` VALUES ('2027', '2025', '霞山', '霞山区', '中国,广东省,湛江市,霞山区', '3', 'xiashan', '0759', '524011', 'X', '110.39822', '21.19181'); +INSERT INTO `yoshop_region` VALUES ('2028', '2025', '坡头', '坡头区', '中国,广东省,湛江市,坡头区', '3', 'potou', '0759', '524057', 'P', '110.45533', '21.24472'); +INSERT INTO `yoshop_region` VALUES ('2029', '2025', '麻章', '麻章区', '中国,广东省,湛江市,麻章区', '3', 'mazhang', '0759', '524094', 'M', '110.3342', '21.26333'); +INSERT INTO `yoshop_region` VALUES ('2030', '2025', '遂溪', '遂溪县', '中国,广东省,湛江市,遂溪县', '3', 'suixi', '0759', '524300', 'S', '110.25003', '21.37721'); +INSERT INTO `yoshop_region` VALUES ('2031', '2025', '徐闻', '徐闻县', '中国,广东省,湛江市,徐闻县', '3', 'xuwen', '0759', '524100', 'X', '110.17379', '20.32812'); +INSERT INTO `yoshop_region` VALUES ('2032', '2025', '廉江', '廉江市', '中国,广东省,湛江市,廉江市', '3', 'lianjiang', '0759', '524400', 'L', '110.28442', '21.60917'); +INSERT INTO `yoshop_region` VALUES ('2033', '2025', '雷州', '雷州市', '中国,广东省,湛江市,雷州市', '3', 'leizhou', '0759', '524200', 'L', '110.10092', '20.91428'); +INSERT INTO `yoshop_region` VALUES ('2034', '2025', '吴川', '吴川市', '中国,广东省,湛江市,吴川市', '3', 'wuchuan', '0759', '524500', 'W', '110.77703', '21.44584'); +INSERT INTO `yoshop_region` VALUES ('2035', '1964', '茂名', '茂名市', '中国,广东省,茂名市', '2', 'maoming', '0668', '525000', 'M', '110.919229', '21.659751'); +INSERT INTO `yoshop_region` VALUES ('2036', '2035', '茂南', '茂南区', '中国,广东省,茂名市,茂南区', '3', 'maonan', '0668', '525000', 'M', '110.9187', '21.64103'); +INSERT INTO `yoshop_region` VALUES ('2037', '2035', '电白', '电白区', '中国,广东省,茂名市,电白区', '3', 'dianbai', '0668', '525400', 'D', '111.007264', '21.507219'); +INSERT INTO `yoshop_region` VALUES ('2038', '2035', '高州', '高州市', '中国,广东省,茂名市,高州市', '3', 'gaozhou', '0668', '525200', 'G', '110.85519', '21.92057'); +INSERT INTO `yoshop_region` VALUES ('2039', '2035', '化州', '化州市', '中国,广东省,茂名市,化州市', '3', 'huazhou', '0668', '525100', 'H', '110.63949', '21.66394'); +INSERT INTO `yoshop_region` VALUES ('2040', '2035', '信宜', '信宜市', '中国,广东省,茂名市,信宜市', '3', 'xinyi', '0668', '525300', 'X', '110.94647', '22.35351'); +INSERT INTO `yoshop_region` VALUES ('2041', '1964', '肇庆', '肇庆市', '中国,广东省,肇庆市', '2', 'zhaoqing', '0758', '526040', 'Z', '112.472529', '23.051546'); +INSERT INTO `yoshop_region` VALUES ('2042', '2041', '端州', '端州区', '中国,广东省,肇庆市,端州区', '3', 'duanzhou', '0758', '526060', 'D', '112.48495', '23.0519'); +INSERT INTO `yoshop_region` VALUES ('2043', '2041', '鼎湖', '鼎湖区', '中国,广东省,肇庆市,鼎湖区', '3', 'dinghu', '0758', '526070', 'D', '112.56643', '23.15846'); +INSERT INTO `yoshop_region` VALUES ('2044', '2041', '广宁', '广宁县', '中国,广东省,肇庆市,广宁县', '3', 'guangning', '0758', '526300', 'G', '112.44064', '23.6346'); +INSERT INTO `yoshop_region` VALUES ('2045', '2041', '怀集', '怀集县', '中国,广东省,肇庆市,怀集县', '3', 'huaiji', '0758', '526400', 'H', '112.18396', '23.90918'); +INSERT INTO `yoshop_region` VALUES ('2046', '2041', '封开', '封开县', '中国,广东省,肇庆市,封开县', '3', 'fengkai', '0758', '526500', 'F', '111.50332', '23.43571'); +INSERT INTO `yoshop_region` VALUES ('2047', '2041', '德庆', '德庆县', '中国,广东省,肇庆市,德庆县', '3', 'deqing', '0758', '526600', 'D', '111.78555', '23.14371'); +INSERT INTO `yoshop_region` VALUES ('2048', '2041', '高要', '高要市', '中国,广东省,肇庆市,高要市', '3', 'gaoyao', '0758', '526100', 'G', '112.45834', '23.02577'); +INSERT INTO `yoshop_region` VALUES ('2049', '2041', '四会', '四会市', '中国,广东省,肇庆市,四会市', '3', 'sihui', '0758', '526200', 'S', '112.73416', '23.32686'); +INSERT INTO `yoshop_region` VALUES ('2050', '1964', '惠州', '惠州市', '中国,广东省,惠州市', '2', 'huizhou', '0752', '516000', 'H', '114.412599', '23.079404'); +INSERT INTO `yoshop_region` VALUES ('2051', '2050', '惠城', '惠城区', '中国,广东省,惠州市,惠城区', '3', 'huicheng', '0752', '516008', 'H', '114.3828', '23.08377'); +INSERT INTO `yoshop_region` VALUES ('2052', '2050', '惠阳', '惠阳区', '中国,广东省,惠州市,惠阳区', '3', 'huiyang', '0752', '516211', 'H', '114.45639', '22.78845'); +INSERT INTO `yoshop_region` VALUES ('2053', '2050', '博罗', '博罗县', '中国,广东省,惠州市,博罗县', '3', 'boluo', '0752', '516100', 'B', '114.28964', '23.17307'); +INSERT INTO `yoshop_region` VALUES ('2054', '2050', '惠东', '惠东县', '中国,广东省,惠州市,惠东县', '3', 'huidong', '0752', '516300', 'H', '114.72009', '22.98484'); +INSERT INTO `yoshop_region` VALUES ('2055', '2050', '龙门', '龙门县', '中国,广东省,惠州市,龙门县', '3', 'longmen', '0752', '516800', 'L', '114.25479', '23.72758'); +INSERT INTO `yoshop_region` VALUES ('2056', '1964', '梅州', '梅州市', '中国,广东省,梅州市', '2', 'meizhou', '0753', '514021', 'M', '116.117582', '24.299112'); +INSERT INTO `yoshop_region` VALUES ('2057', '2056', '梅江', '梅江区', '中国,广东省,梅州市,梅江区', '3', 'meijiang', '0753', '514000', 'M', '116.11663', '24.31062'); +INSERT INTO `yoshop_region` VALUES ('2058', '2056', '梅县', '梅县区', '中国,广东省,梅州市,梅县区', '3', 'meixian', '0753', '514787', 'M', '116.097753', '24.286739'); +INSERT INTO `yoshop_region` VALUES ('2059', '2056', '大埔', '大埔县', '中国,广东省,梅州市,大埔县', '3', 'dabu', '0753', '514200', 'D', '116.69662', '24.35325'); +INSERT INTO `yoshop_region` VALUES ('2060', '2056', '丰顺', '丰顺县', '中国,广东省,梅州市,丰顺县', '3', 'fengshun', '0753', '514300', 'F', '116.18219', '23.74094'); +INSERT INTO `yoshop_region` VALUES ('2061', '2056', '五华', '五华县', '中国,广东省,梅州市,五华县', '3', 'wuhua', '0753', '514400', 'W', '115.77893', '23.92417'); +INSERT INTO `yoshop_region` VALUES ('2062', '2056', '平远', '平远县', '中国,广东省,梅州市,平远县', '3', 'pingyuan', '0753', '514600', 'P', '115.89556', '24.57116'); +INSERT INTO `yoshop_region` VALUES ('2063', '2056', '蕉岭', '蕉岭县', '中国,广东省,梅州市,蕉岭县', '3', 'jiaoling', '0753', '514100', 'J', '116.17089', '24.65732'); +INSERT INTO `yoshop_region` VALUES ('2064', '2056', '兴宁', '兴宁市', '中国,广东省,梅州市,兴宁市', '3', 'xingning', '0753', '514500', 'X', '115.73141', '24.14001'); +INSERT INTO `yoshop_region` VALUES ('2065', '1964', '汕尾', '汕尾市', '中国,广东省,汕尾市', '2', 'shanwei', '0660', '516600', 'S', '115.364238', '22.774485'); +INSERT INTO `yoshop_region` VALUES ('2066', '2065', '城区', '城区', '中国,广东省,汕尾市,城区', '3', 'chengqu', '0660', '516600', 'C', '115.36503', '22.7789'); +INSERT INTO `yoshop_region` VALUES ('2067', '2065', '海丰', '海丰县', '中国,广东省,汕尾市,海丰县', '3', 'haifeng', '0660', '516400', 'H', '115.32336', '22.96653'); +INSERT INTO `yoshop_region` VALUES ('2068', '2065', '陆河', '陆河县', '中国,广东省,汕尾市,陆河县', '3', 'luhe', '0660', '516700', 'L', '115.65597', '23.30365'); +INSERT INTO `yoshop_region` VALUES ('2069', '2065', '陆丰', '陆丰市', '中国,广东省,汕尾市,陆丰市', '3', 'lufeng', '0660', '516500', 'L', '115.64813', '22.94335'); +INSERT INTO `yoshop_region` VALUES ('2070', '1964', '河源', '河源市', '中国,广东省,河源市', '2', 'heyuan', '0762', '517000', 'H', '114.697802', '23.746266'); +INSERT INTO `yoshop_region` VALUES ('2071', '2070', '源城', '源城区', '中国,广东省,河源市,源城区', '3', 'yuancheng', '0762', '517000', 'Y', '114.70242', '23.7341'); +INSERT INTO `yoshop_region` VALUES ('2072', '2070', '紫金', '紫金县', '中国,广东省,河源市,紫金县', '3', 'zijin', '0762', '517400', 'Z', '115.18365', '23.63867'); +INSERT INTO `yoshop_region` VALUES ('2073', '2070', '龙川', '龙川县', '中国,广东省,河源市,龙川县', '3', 'longchuan', '0762', '517300', 'L', '115.26025', '24.10142'); +INSERT INTO `yoshop_region` VALUES ('2074', '2070', '连平', '连平县', '中国,广东省,河源市,连平县', '3', 'lianping', '0762', '517100', 'L', '114.49026', '24.37156'); +INSERT INTO `yoshop_region` VALUES ('2075', '2070', '和平', '和平县', '中国,广东省,河源市,和平县', '3', 'heping', '0762', '517200', 'H', '114.93841', '24.44319'); +INSERT INTO `yoshop_region` VALUES ('2076', '2070', '东源', '东源县', '中国,广东省,河源市,东源县', '3', 'dongyuan', '0762', '517583', 'D', '114.74633', '23.78835'); +INSERT INTO `yoshop_region` VALUES ('2077', '1964', '阳江', '阳江市', '中国,广东省,阳江市', '2', 'yangjiang', '0662', '529500', 'Y', '111.975107', '21.859222'); +INSERT INTO `yoshop_region` VALUES ('2078', '2077', '江城', '江城区', '中国,广东省,阳江市,江城区', '3', 'jiangcheng', '0662', '529500', 'J', '111.95488', '21.86193'); +INSERT INTO `yoshop_region` VALUES ('2079', '2077', '阳东', '阳东区', '中国,广东省,阳江市,阳东区', '3', 'yangdong', '0662', '529900', 'Y', '112.01467', '21.87398'); +INSERT INTO `yoshop_region` VALUES ('2080', '2077', '阳西', '阳西县', '中国,广东省,阳江市,阳西县', '3', 'yangxi', '0662', '529800', 'Y', '111.61785', '21.75234'); +INSERT INTO `yoshop_region` VALUES ('2081', '2077', '阳春', '阳春市', '中国,广东省,阳江市,阳春市', '3', 'yangchun', '0662', '529600', 'Y', '111.78854', '22.17232'); +INSERT INTO `yoshop_region` VALUES ('2082', '1964', '清远', '清远市', '中国,广东省,清远市', '2', 'qingyuan', '0763', '511500', 'Q', '113.036779', '23.704188'); +INSERT INTO `yoshop_region` VALUES ('2083', '2082', '清城', '清城区', '中国,广东省,清远市,清城区', '3', 'qingcheng', '0763', '511515', 'Q', '113.06265', '23.69784'); +INSERT INTO `yoshop_region` VALUES ('2084', '2082', '清新', '清新区', '中国,广东省,清远市,清新区', '3', 'qingxin', '0763', '511810', 'Q', '113.015203', '23.736949'); +INSERT INTO `yoshop_region` VALUES ('2085', '2082', '佛冈', '佛冈县', '中国,广东省,清远市,佛冈县', '3', 'fogang', '0763', '511600', 'F', '113.53286', '23.87231'); +INSERT INTO `yoshop_region` VALUES ('2086', '2082', '阳山', '阳山县', '中国,广东省,清远市,阳山县', '3', 'yangshan', '0763', '513100', 'Y', '112.64129', '24.46516'); +INSERT INTO `yoshop_region` VALUES ('2087', '2082', '连山', '连山壮族瑶族自治县', '中国,广东省,清远市,连山壮族瑶族自治县', '3', 'lianshan', '0763', '513200', 'L', '112.0802', '24.56807'); +INSERT INTO `yoshop_region` VALUES ('2088', '2082', '连南', '连南瑶族自治县', '中国,广东省,清远市,连南瑶族自治县', '3', 'liannan', '0763', '513300', 'L', '112.28842', '24.71726'); +INSERT INTO `yoshop_region` VALUES ('2089', '2082', '英德', '英德市', '中国,广东省,清远市,英德市', '3', 'yingde', '0763', '513000', 'Y', '113.415', '24.18571'); +INSERT INTO `yoshop_region` VALUES ('2090', '2082', '连州', '连州市', '中国,广东省,清远市,连州市', '3', 'lianzhou', '0763', '513400', 'L', '112.38153', '24.77913'); +INSERT INTO `yoshop_region` VALUES ('2091', '1964', '东莞', '东莞市', '中国,广东省,东莞市', '2', 'dongguan', '0769', '523888', 'D', '113.760234', '23.048884'); +INSERT INTO `yoshop_region` VALUES ('2092', '2091', '莞城', '莞城区', '中国,广东省,东莞市,莞城区', '3', 'guancheng', '0769', '523128', null, '113.751043', '23.053412'); +INSERT INTO `yoshop_region` VALUES ('2093', '2091', '南城', '南城区', '中国,广东省,东莞市,南城区', '3', 'nancheng', '0769', '523617', 'N', '113.752125', '23.02018'); +INSERT INTO `yoshop_region` VALUES ('2094', '2091', '万江', '万江区', '中国,广东省,东莞市,万江区', '3', 'wanjiang', '0769', '523039', 'W', '113.739053', '23.043842'); +INSERT INTO `yoshop_region` VALUES ('2095', '2091', '石碣', '石碣镇', '中国,广东省,东莞市,石碣镇', '3', 'shijie', '0769', '523290', 'S', '113.80217', '23.09899'); +INSERT INTO `yoshop_region` VALUES ('2096', '2091', '石龙', '石龙镇', '中国,广东省,东莞市,石龙镇', '3', 'shilong', '0769', '523326', 'S', '113.876381', '23.107444'); +INSERT INTO `yoshop_region` VALUES ('2097', '2091', '茶山', '茶山镇', '中国,广东省,东莞市,茶山镇', '3', 'chashan', '0769', '523380', 'C', '113.883526', '23.062375'); +INSERT INTO `yoshop_region` VALUES ('2098', '2091', '石排', '石排镇', '中国,广东省,东莞市,石排镇', '3', 'shipai', '0769', '523346', 'S', '113.919859', '23.0863'); +INSERT INTO `yoshop_region` VALUES ('2099', '2091', '企石', '企石镇', '中国,广东省,东莞市,企石镇', '3', 'qishi', '0769', '523507', 'Q', '114.013233', '23.066044'); +INSERT INTO `yoshop_region` VALUES ('2100', '2091', '横沥', '横沥镇', '中国,广东省,东莞市,横沥镇', '3', 'hengli', '0769', '523471', 'H', '113.957436', '23.025732'); +INSERT INTO `yoshop_region` VALUES ('2101', '2091', '桥头', '桥头镇', '中国,广东省,东莞市,桥头镇', '3', 'qiaotou', '0769', '523520', 'Q', '114.01385', '22.939727'); +INSERT INTO `yoshop_region` VALUES ('2102', '2091', '谢岗', '谢岗镇', '中国,广东省,东莞市,谢岗镇', '3', 'xiegang', '0769', '523592', 'X', '114.141396', '22.959664'); +INSERT INTO `yoshop_region` VALUES ('2103', '2091', '东坑', '东坑镇', '中国,广东省,东莞市,东坑镇', '3', 'dongkeng', '0769', '523451', 'D', '113.939835', '22.992804'); +INSERT INTO `yoshop_region` VALUES ('2104', '2091', '常平', '常平镇', '中国,广东省,东莞市,常平镇', '3', 'changping', '0769', '523560', 'C', '114.029627', '23.016116'); +INSERT INTO `yoshop_region` VALUES ('2105', '2091', '寮步', '寮步镇', '中国,广东省,东莞市,寮步镇', '3', 'liaobu', '0769', '523411', null, '113.884745', '22.991738'); +INSERT INTO `yoshop_region` VALUES ('2106', '2091', '大朗', '大朗镇', '中国,广东省,东莞市,大朗镇', '3', 'dalang', '0769', '523770', 'D', '113.9271', '22.965748'); +INSERT INTO `yoshop_region` VALUES ('2107', '2091', '麻涌', '麻涌镇', '中国,广东省,东莞市,麻涌镇', '3', 'machong', '0769', '523143', 'M', '113.546177', '23.045315'); +INSERT INTO `yoshop_region` VALUES ('2108', '2091', '中堂', '中堂镇', '中国,广东省,东莞市,中堂镇', '3', 'zhongtang', '0769', '523233', 'Z', '113.654422', '23.090164'); +INSERT INTO `yoshop_region` VALUES ('2109', '2091', '高埗', '高埗镇', '中国,广东省,东莞市,高埗镇', '3', 'gaobu', '0769', '523282', null, '113.735917', '23.068415'); +INSERT INTO `yoshop_region` VALUES ('2110', '2091', '樟木头', '樟木头镇', '中国,广东省,东莞市,樟木头镇', '3', 'zhangmutou', '0769', '523619', 'Z', '114.066298', '22.956682'); +INSERT INTO `yoshop_region` VALUES ('2111', '2091', '大岭山', '大岭山镇', '中国,广东省,东莞市,大岭山镇', '3', 'dalingshan', '0769', '523835', 'D', '113.782955', '22.885366'); +INSERT INTO `yoshop_region` VALUES ('2112', '2091', '望牛墩', '望牛墩镇', '中国,广东省,东莞市,望牛墩镇', '3', 'wangniudun', '0769', '523203', 'W', '113.658847', '23.055018'); +INSERT INTO `yoshop_region` VALUES ('2113', '2091', '黄江', '黄江镇', '中国,广东省,东莞市,黄江镇', '3', 'huangjiang', '0769', '523755', 'H', '113.992635', '22.877536'); +INSERT INTO `yoshop_region` VALUES ('2114', '2091', '洪梅', '洪梅镇', '中国,广东省,东莞市,洪梅镇', '3', 'hongmei', '0769', '523163', 'H', '113.613081', '22.992675'); +INSERT INTO `yoshop_region` VALUES ('2115', '2091', '清溪', '清溪镇', '中国,广东省,东莞市,清溪镇', '3', 'qingxi', '0769', '523660', 'Q', '114.155796', '22.844456'); +INSERT INTO `yoshop_region` VALUES ('2116', '2091', '沙田', '沙田镇', '中国,广东省,东莞市,沙田镇', '3', 'shatian', '0769', '523988', 'S', '113.760234', '23.048884'); +INSERT INTO `yoshop_region` VALUES ('2117', '2091', '道滘', '道滘镇', '中国,广东省,东莞市,道滘镇', '3', 'daojiao', '0769', '523171', null, '113.760234', '23.048884'); +INSERT INTO `yoshop_region` VALUES ('2118', '2091', '塘厦', '塘厦镇', '中国,广东省,东莞市,塘厦镇', '3', 'tangxia', '0769', '523713', 'T', '114.10765', '22.822862'); +INSERT INTO `yoshop_region` VALUES ('2119', '2091', '虎门', '虎门镇', '中国,广东省,东莞市,虎门镇', '3', 'humen', '0769', '523932', 'H', '113.71118', '22.82615'); +INSERT INTO `yoshop_region` VALUES ('2120', '2091', '厚街', '厚街镇', '中国,广东省,东莞市,厚街镇', '3', 'houjie', '0769', '523960', 'H', '113.67301', '22.940815'); +INSERT INTO `yoshop_region` VALUES ('2121', '2091', '凤岗', '凤岗镇', '中国,广东省,东莞市,凤岗镇', '3', 'fenggang', '0769', '523690', 'F', '114.141194', '22.744598'); +INSERT INTO `yoshop_region` VALUES ('2122', '2091', '长安', '长安镇', '中国,广东省,东莞市,长安镇', '3', 'chang\'an', '0769', '523850', 'C', '113.803939', '22.816644'); +INSERT INTO `yoshop_region` VALUES ('2123', '1964', '中山', '中山市', '中国,广东省,中山市', '2', 'zhongshan', '0760', '528403', 'Z', '113.382391', '22.521113'); +INSERT INTO `yoshop_region` VALUES ('2124', '2123', '石岐', '石岐区', '中国,广东省,中山市,石岐区', '3', 'shiqi', '0760', '528400', 'S', '113.378835', '22.52522'); +INSERT INTO `yoshop_region` VALUES ('2125', '2123', '南区', '南区', '中国,广东省,中山市,南区', '3', 'nanqu', '0760', '528400', 'N', '113.355896', '22.486568'); +INSERT INTO `yoshop_region` VALUES ('2126', '2123', '五桂山', '五桂山区', '中国,广东省,中山市,五桂山区', '3', 'wuguishan', '0760', '528458', 'W', '113.41079', '22.51968'); +INSERT INTO `yoshop_region` VALUES ('2127', '2123', '火炬', '火炬开发区', '中国,广东省,中山市,火炬开发区', '3', 'huoju', '0760', '528437', 'H', '113.480523', '22.566082'); +INSERT INTO `yoshop_region` VALUES ('2128', '2123', '黄圃', '黄圃镇', '中国,广东省,中山市,黄圃镇', '3', 'huangpu', '0760', '528429', 'H', '113.342359', '22.715116'); +INSERT INTO `yoshop_region` VALUES ('2129', '2123', '南头', '南头镇', '中国,广东省,中山市,南头镇', '3', 'nantou', '0760', '528421', 'N', '113.296358', '22.713907'); +INSERT INTO `yoshop_region` VALUES ('2130', '2123', '东凤', '东凤镇', '中国,广东省,中山市,东凤镇', '3', 'dongfeng', '0760', '528425', 'D', '113.26114', '22.68775'); +INSERT INTO `yoshop_region` VALUES ('2131', '2123', '阜沙', '阜沙镇', '中国,广东省,中山市,阜沙镇', '3', 'fusha', '0760', '528434', 'F', '113.353024', '22.666364'); +INSERT INTO `yoshop_region` VALUES ('2132', '2123', '小榄', '小榄镇', '中国,广东省,中山市,小榄镇', '3', 'xiaolan', '0760', '528415', 'X', '113.244235', '22.666951'); +INSERT INTO `yoshop_region` VALUES ('2133', '2123', '东升', '东升镇', '中国,广东省,中山市,东升镇', '3', 'dongsheng', '0760', '528400', 'D', '113.296298', '22.614003'); +INSERT INTO `yoshop_region` VALUES ('2134', '2123', '古镇', '古镇镇', '中国,广东省,中山市,古镇镇', '3', 'guzhen', '0760', '528422', 'G', '113.179745', '22.611019'); +INSERT INTO `yoshop_region` VALUES ('2135', '2123', '横栏', '横栏镇', '中国,广东省,中山市,横栏镇', '3', 'henglan', '0760', '528478', 'H', '113.265845', '22.523202'); +INSERT INTO `yoshop_region` VALUES ('2136', '2123', '三角', '三角镇', '中国,广东省,中山市,三角镇', '3', 'sanjiao', '0760', '528422', 'S', '113.423624', '22.677033'); +INSERT INTO `yoshop_region` VALUES ('2137', '2123', '民众', '民众镇', '中国,广东省,中山市,民众镇', '3', 'minzhong', '0760', '528441', 'M', '113.486025', '22.623468'); +INSERT INTO `yoshop_region` VALUES ('2138', '2123', '南朗', '南朗镇', '中国,广东省,中山市,南朗镇', '3', 'nanlang', '0760', '528454', 'N', '113.533939', '22.492378'); +INSERT INTO `yoshop_region` VALUES ('2139', '2123', '港口', '港口镇', '中国,广东省,中山市,港口镇', '3', 'gangkou', '0760', '528447', 'G', '113.382391', '22.521113'); +INSERT INTO `yoshop_region` VALUES ('2140', '2123', '大涌', '大涌镇', '中国,广东省,中山市,大涌镇', '3', 'dayong', '0760', '528476', 'D', '113.291708', '22.467712'); +INSERT INTO `yoshop_region` VALUES ('2141', '2123', '沙溪', '沙溪镇', '中国,广东省,中山市,沙溪镇', '3', 'shaxi', '0760', '528471', 'S', '113.328369', '22.526325'); +INSERT INTO `yoshop_region` VALUES ('2142', '2123', '三乡', '三乡镇', '中国,广东省,中山市,三乡镇', '3', 'sanxiang', '0760', '528463', 'S', '113.4334', '22.352494'); +INSERT INTO `yoshop_region` VALUES ('2143', '2123', '板芙', '板芙镇', '中国,广东省,中山市,板芙镇', '3', 'banfu', '0760', '528459', 'B', '113.320346', '22.415674'); +INSERT INTO `yoshop_region` VALUES ('2144', '2123', '神湾', '神湾镇', '中国,广东省,中山市,神湾镇', '3', 'shenwan', '0760', '528462', 'S', '113.359387', '22.312476'); +INSERT INTO `yoshop_region` VALUES ('2145', '2123', '坦洲', '坦洲镇', '中国,广东省,中山市,坦洲镇', '3', 'tanzhou', '0760', '528467', 'T', '113.485677', '22.261269'); +INSERT INTO `yoshop_region` VALUES ('2146', '1964', '潮州', '潮州市', '中国,广东省,潮州市', '2', 'chaozhou', '0768', '521000', 'C', '116.632301', '23.661701'); +INSERT INTO `yoshop_region` VALUES ('2147', '2146', '湘桥', '湘桥区', '中国,广东省,潮州市,湘桥区', '3', 'xiangqiao', '0768', '521000', 'X', '116.62805', '23.67451'); +INSERT INTO `yoshop_region` VALUES ('2148', '2146', '潮安', '潮安区', '中国,广东省,潮州市,潮安区', '3', 'chao\'an', '0768', '515638', 'C', '116.592895', '23.643656'); +INSERT INTO `yoshop_region` VALUES ('2149', '2146', '饶平', '饶平县', '中国,广东省,潮州市,饶平县', '3', 'raoping', '0768', '515700', 'R', '117.00692', '23.66994'); +INSERT INTO `yoshop_region` VALUES ('2150', '1964', '揭阳', '揭阳市', '中国,广东省,揭阳市', '2', 'jieyang', '0633', '522000', 'J', '116.355733', '23.543778'); +INSERT INTO `yoshop_region` VALUES ('2151', '2150', '榕城', '榕城区', '中国,广东省,揭阳市,榕城区', '3', 'rongcheng', '0633', '522000', null, '116.3671', '23.52508'); +INSERT INTO `yoshop_region` VALUES ('2152', '2150', '揭东', '揭东区', '中国,广东省,揭阳市,揭东区', '3', 'jiedong', '0633', '515500', 'J', '116.412947', '23.569887'); +INSERT INTO `yoshop_region` VALUES ('2153', '2150', '揭西', '揭西县', '中国,广东省,揭阳市,揭西县', '3', 'jiexi', '0633', '515400', 'J', '115.83883', '23.42714'); +INSERT INTO `yoshop_region` VALUES ('2154', '2150', '惠来', '惠来县', '中国,广东省,揭阳市,惠来县', '3', 'huilai', '0633', '515200', 'H', '116.29599', '23.03289'); +INSERT INTO `yoshop_region` VALUES ('2155', '2150', '普宁', '普宁市', '中国,广东省,揭阳市,普宁市', '3', 'puning', '0633', '515300', 'P', '116.16564', '23.29732'); +INSERT INTO `yoshop_region` VALUES ('2156', '1964', '云浮', '云浮市', '中国,广东省,云浮市', '2', 'yunfu', '0766', '527300', 'Y', '112.044439', '22.929801'); +INSERT INTO `yoshop_region` VALUES ('2157', '2156', '云城', '云城区', '中国,广东省,云浮市,云城区', '3', 'yuncheng', '0766', '527300', 'Y', '112.03908', '22.92996'); +INSERT INTO `yoshop_region` VALUES ('2158', '2156', '云安', '云安区', '中国,广东省,云浮市,云安区', '3', 'yun\'an', '0766', '527500', 'Y', '112.00936', '23.07779'); +INSERT INTO `yoshop_region` VALUES ('2159', '2156', '新兴', '新兴县', '中国,广东省,云浮市,新兴县', '3', 'xinxing', '0766', '527400', 'X', '112.23019', '22.69734'); +INSERT INTO `yoshop_region` VALUES ('2160', '2156', '郁南', '郁南县', '中国,广东省,云浮市,郁南县', '3', 'yunan', '0766', '527100', 'Y', '111.53387', '23.23307'); +INSERT INTO `yoshop_region` VALUES ('2161', '2156', '罗定', '罗定市', '中国,广东省,云浮市,罗定市', '3', 'luoding', '0766', '527200', 'L', '111.56979', '22.76967'); +INSERT INTO `yoshop_region` VALUES ('2162', '0', '广西', '广西壮族自治区', '中国,广西壮族自治区', '1', 'guangxi', '', '', 'G', '108.320004', '22.82402'); +INSERT INTO `yoshop_region` VALUES ('2163', '2162', '南宁', '南宁市', '中国,广西壮族自治区,南宁市', '2', 'nanning', '0771', '530028', 'N', '108.320004', '22.82402'); +INSERT INTO `yoshop_region` VALUES ('2164', '2163', '兴宁', '兴宁区', '中国,广西壮族自治区,南宁市,兴宁区', '3', 'xingning', '0771', '530023', 'X', '108.36694', '22.85355'); +INSERT INTO `yoshop_region` VALUES ('2165', '2163', '青秀', '青秀区', '中国,广西壮族自治区,南宁市,青秀区', '3', 'qingxiu', '0771', '530213', 'Q', '108.49545', '22.78511'); +INSERT INTO `yoshop_region` VALUES ('2166', '2163', '江南', '江南区', '中国,广西壮族自治区,南宁市,江南区', '3', 'jiangnan', '0771', '530031', 'J', '108.27325', '22.78127'); +INSERT INTO `yoshop_region` VALUES ('2167', '2163', '西乡塘', '西乡塘区', '中国,广西壮族自治区,南宁市,西乡塘区', '3', 'xixiangtang', '0771', '530001', 'X', '108.31347', '22.83386'); +INSERT INTO `yoshop_region` VALUES ('2168', '2163', '良庆', '良庆区', '中国,广西壮族自治区,南宁市,良庆区', '3', 'liangqing', '0771', '530219', 'L', '108.41284', '22.74914'); +INSERT INTO `yoshop_region` VALUES ('2169', '2163', '邕宁', '邕宁区', '中国,广西壮族自治区,南宁市,邕宁区', '3', 'yongning', '0771', '530200', null, '108.48684', '22.75628'); +INSERT INTO `yoshop_region` VALUES ('2170', '2163', '武鸣', '武鸣县', '中国,广西壮族自治区,南宁市,武鸣县', '3', 'wuming', '0771', '530100', 'W', '108.27719', '23.15643'); +INSERT INTO `yoshop_region` VALUES ('2171', '2163', '隆安', '隆安县', '中国,广西壮族自治区,南宁市,隆安县', '3', 'long\'an', '0771', '532700', 'L', '107.69192', '23.17336'); +INSERT INTO `yoshop_region` VALUES ('2172', '2163', '马山', '马山县', '中国,广西壮族自治区,南宁市,马山县', '3', 'mashan', '0771', '530600', 'M', '108.17697', '23.70931'); +INSERT INTO `yoshop_region` VALUES ('2173', '2163', '上林', '上林县', '中国,广西壮族自治区,南宁市,上林县', '3', 'shanglin', '0771', '530500', 'S', '108.60522', '23.432'); +INSERT INTO `yoshop_region` VALUES ('2174', '2163', '宾阳', '宾阳县', '中国,广西壮族自治区,南宁市,宾阳县', '3', 'binyang', '0771', '530400', 'B', '108.81185', '23.2196'); +INSERT INTO `yoshop_region` VALUES ('2175', '2163', '横县', '横县', '中国,广西壮族自治区,南宁市,横县', '3', 'hengxian', '0771', '530300', 'H', '109.26608', '22.68448'); +INSERT INTO `yoshop_region` VALUES ('2176', '2163', '埌东', '埌东新区', '中国,广西壮族自治区,南宁市,埌东新区', '3', 'langdong', '0771', '530000', null, '108.419094', '22.812976'); +INSERT INTO `yoshop_region` VALUES ('2177', '2162', '柳州', '柳州市', '中国,广西壮族自治区,柳州市', '2', 'liuzhou', '0772', '545001', 'L', '109.411703', '24.314617'); +INSERT INTO `yoshop_region` VALUES ('2178', '2177', '城中', '城中区', '中国,广西壮族自治区,柳州市,城中区', '3', 'chengzhong', '0772', '545001', 'C', '109.41082', '24.31543'); +INSERT INTO `yoshop_region` VALUES ('2179', '2177', '鱼峰', '鱼峰区', '中国,广西壮族自治区,柳州市,鱼峰区', '3', 'yufeng', '0772', '545005', 'Y', '109.4533', '24.31868'); +INSERT INTO `yoshop_region` VALUES ('2180', '2177', '柳南', '柳南区', '中国,广西壮族自治区,柳州市,柳南区', '3', 'liunan', '0772', '545007', 'L', '109.38548', '24.33599'); +INSERT INTO `yoshop_region` VALUES ('2181', '2177', '柳北', '柳北区', '中国,广西壮族自治区,柳州市,柳北区', '3', 'liubei', '0772', '545002', 'L', '109.40202', '24.36267'); +INSERT INTO `yoshop_region` VALUES ('2182', '2177', '柳江', '柳江县', '中国,广西壮族自治区,柳州市,柳江县', '3', 'liujiang', '0772', '545100', 'L', '109.33273', '24.25596'); +INSERT INTO `yoshop_region` VALUES ('2183', '2177', '柳城', '柳城县', '中国,广西壮族自治区,柳州市,柳城县', '3', 'liucheng', '0772', '545200', 'L', '109.23877', '24.64951'); +INSERT INTO `yoshop_region` VALUES ('2184', '2177', '鹿寨', '鹿寨县', '中国,广西壮族自治区,柳州市,鹿寨县', '3', 'luzhai', '0772', '545600', 'L', '109.75177', '24.47306'); +INSERT INTO `yoshop_region` VALUES ('2185', '2177', '融安', '融安县', '中国,广西壮族自治区,柳州市,融安县', '3', 'rong\'an', '0772', '545400', 'R', '109.39761', '25.22465'); +INSERT INTO `yoshop_region` VALUES ('2186', '2177', '融水', '融水苗族自治县', '中国,广西壮族自治区,柳州市,融水苗族自治县', '3', 'rongshui', '0772', '545300', 'R', '109.25634', '25.06628'); +INSERT INTO `yoshop_region` VALUES ('2187', '2177', '三江', '三江侗族自治县', '中国,广西壮族自治区,柳州市,三江侗族自治县', '3', 'sanjiang', '0772', '545500', 'S', '109.60446', '25.78428'); +INSERT INTO `yoshop_region` VALUES ('2188', '2177', '柳东', '柳东新区', '中国,广西壮族自治区,柳州市,柳东新区', '3', 'liudong', '0772', '545000', 'L', '109.437053', '24.329204'); +INSERT INTO `yoshop_region` VALUES ('2189', '2162', '桂林', '桂林市', '中国,广西壮族自治区,桂林市', '2', 'guilin', '0773', '541100', 'G', '110.299121', '25.274215'); +INSERT INTO `yoshop_region` VALUES ('2190', '2189', '秀峰', '秀峰区', '中国,广西壮族自治区,桂林市,秀峰区', '3', 'xiufeng', '0773', '541001', 'X', '110.28915', '25.28249'); +INSERT INTO `yoshop_region` VALUES ('2191', '2189', '叠彩', '叠彩区', '中国,广西壮族自治区,桂林市,叠彩区', '3', 'diecai', '0773', '541001', 'D', '110.30195', '25.31381'); +INSERT INTO `yoshop_region` VALUES ('2192', '2189', '象山', '象山区', '中国,广西壮族自治区,桂林市,象山区', '3', 'xiangshan', '0773', '541002', 'X', '110.28108', '25.26168'); +INSERT INTO `yoshop_region` VALUES ('2193', '2189', '七星', '七星区', '中国,广西壮族自治区,桂林市,七星区', '3', 'qixing', '0773', '541004', 'Q', '110.31793', '25.2525'); +INSERT INTO `yoshop_region` VALUES ('2194', '2189', '雁山', '雁山区', '中国,广西壮族自治区,桂林市,雁山区', '3', 'yanshan', '0773', '541006', 'Y', '110.30911', '25.06038'); +INSERT INTO `yoshop_region` VALUES ('2195', '2189', '临桂', '临桂区', '中国,广西壮族自治区,桂林市,临桂区', '3', 'lingui', '0773', '541100', 'L', '110.205487', '25.246257'); +INSERT INTO `yoshop_region` VALUES ('2196', '2189', '阳朔', '阳朔县', '中国,广西壮族自治区,桂林市,阳朔县', '3', 'yangshuo', '0773', '541900', 'Y', '110.49475', '24.77579'); +INSERT INTO `yoshop_region` VALUES ('2197', '2189', '灵川', '灵川县', '中国,广西壮族自治区,桂林市,灵川县', '3', 'lingchuan', '0773', '541200', 'L', '110.32949', '25.41292'); +INSERT INTO `yoshop_region` VALUES ('2198', '2189', '全州', '全州县', '中国,广西壮族自治区,桂林市,全州县', '3', 'quanzhou', '0773', '541503', 'Q', '111.07211', '25.92799'); +INSERT INTO `yoshop_region` VALUES ('2199', '2189', '兴安', '兴安县', '中国,广西壮族自治区,桂林市,兴安县', '3', 'xing\'an', '0773', '541300', 'X', '110.67144', '25.61167'); +INSERT INTO `yoshop_region` VALUES ('2200', '2189', '永福', '永福县', '中国,广西壮族自治区,桂林市,永福县', '3', 'yongfu', '0773', '541800', 'Y', '109.98333', '24.98004'); +INSERT INTO `yoshop_region` VALUES ('2201', '2189', '灌阳', '灌阳县', '中国,广西壮族自治区,桂林市,灌阳县', '3', 'guanyang', '0773', '541600', 'G', '111.15954', '25.48803'); +INSERT INTO `yoshop_region` VALUES ('2202', '2189', '龙胜', '龙胜各族自治县', '中国,广西壮族自治区,桂林市,龙胜各族自治县', '3', 'longsheng', '0773', '541700', 'L', '110.01226', '25.79614'); +INSERT INTO `yoshop_region` VALUES ('2203', '2189', '资源', '资源县', '中国,广西壮族自治区,桂林市,资源县', '3', 'ziyuan', '0773', '541400', 'Z', '110.65255', '26.04237'); +INSERT INTO `yoshop_region` VALUES ('2204', '2189', '平乐', '平乐县', '中国,广西壮族自治区,桂林市,平乐县', '3', 'pingle', '0773', '542400', 'P', '110.64175', '24.63242'); +INSERT INTO `yoshop_region` VALUES ('2205', '2189', '荔浦', '荔浦县', '中国,广西壮族自治区,桂林市,荔浦县', '3', 'lipu', '0773', '546600', 'L', '110.3971', '24.49589'); +INSERT INTO `yoshop_region` VALUES ('2206', '2189', '恭城', '恭城瑶族自治县', '中国,广西壮族自治区,桂林市,恭城瑶族自治县', '3', 'gongcheng', '0773', '542500', 'G', '110.83035', '24.83286'); +INSERT INTO `yoshop_region` VALUES ('2207', '2162', '梧州', '梧州市', '中国,广西壮族自治区,梧州市', '2', 'wuzhou', '0774', '543002', 'W', '111.316229', '23.472309'); +INSERT INTO `yoshop_region` VALUES ('2208', '2207', '万秀', '万秀区', '中国,广西壮族自治区,梧州市,万秀区', '3', 'wanxiu', '0774', '543000', 'W', '111.32052', '23.47298'); +INSERT INTO `yoshop_region` VALUES ('2209', '2207', '长洲', '长洲区', '中国,广西壮族自治区,梧州市,长洲区', '3', 'changzhou', '0774', '543003', 'C', '111.27494', '23.48573'); +INSERT INTO `yoshop_region` VALUES ('2210', '2207', '龙圩', '龙圩区', '中国,广西壮族自治区,梧州市,龙圩区', '3', 'longxu', '0774', '543002', 'L', '111.316229', '23.472309'); +INSERT INTO `yoshop_region` VALUES ('2211', '2207', '苍梧', '苍梧县', '中国,广西壮族自治区,梧州市,苍梧县', '3', 'cangwu', '0774', '543100', 'C', '111.24533', '23.42049'); +INSERT INTO `yoshop_region` VALUES ('2212', '2207', '藤县', '藤县', '中国,广西壮族自治区,梧州市,藤县', '3', 'tengxian', '0774', '543300', 'T', '110.91418', '23.37605'); +INSERT INTO `yoshop_region` VALUES ('2213', '2207', '蒙山', '蒙山县', '中国,广西壮族自治区,梧州市,蒙山县', '3', 'mengshan', '0774', '546700', 'M', '110.52221', '24.20168'); +INSERT INTO `yoshop_region` VALUES ('2214', '2207', '岑溪', '岑溪市', '中国,广西壮族自治区,梧州市,岑溪市', '3', 'cenxi', '0774', '543200', null, '110.99594', '22.9191'); +INSERT INTO `yoshop_region` VALUES ('2215', '2162', '北海', '北海市', '中国,广西壮族自治区,北海市', '2', 'beihai', '0779', '536000', 'B', '109.119254', '21.473343'); +INSERT INTO `yoshop_region` VALUES ('2216', '2215', '海城', '海城区', '中国,广西壮族自治区,北海市,海城区', '3', 'haicheng', '0779', '536000', 'H', '109.11744', '21.47501'); +INSERT INTO `yoshop_region` VALUES ('2217', '2215', '银海', '银海区', '中国,广西壮族自治区,北海市,银海区', '3', 'yinhai', '0779', '536000', 'Y', '109.13029', '21.4783'); +INSERT INTO `yoshop_region` VALUES ('2218', '2215', '铁山港', '铁山港区', '中国,广西壮族自治区,北海市,铁山港区', '3', 'tieshangang', '0779', '536017', 'T', '109.45578', '21.59661'); +INSERT INTO `yoshop_region` VALUES ('2219', '2215', '合浦', '合浦县', '中国,广西壮族自治区,北海市,合浦县', '3', 'hepu', '0779', '536100', 'H', '109.20068', '21.66601'); +INSERT INTO `yoshop_region` VALUES ('2220', '2162', '防城港', '防城港市', '中国,广西壮族自治区,防城港市', '2', 'fangchenggang', '0770', '538001', 'F', '108.345478', '21.614631'); +INSERT INTO `yoshop_region` VALUES ('2221', '2220', '港口', '港口区', '中国,广西壮族自治区,防城港市,港口区', '3', 'gangkou', '0770', '538001', 'G', '108.38022', '21.64342'); +INSERT INTO `yoshop_region` VALUES ('2222', '2220', '防城', '防城区', '中国,广西壮族自治区,防城港市,防城区', '3', 'fangcheng', '0770', '538021', 'F', '108.35726', '21.76464'); +INSERT INTO `yoshop_region` VALUES ('2223', '2220', '上思', '上思县', '中国,广西壮族自治区,防城港市,上思县', '3', 'shangsi', '0770', '535500', 'S', '107.9823', '22.14957'); +INSERT INTO `yoshop_region` VALUES ('2224', '2220', '东兴', '东兴市', '中国,广西壮族自治区,防城港市,东兴市', '3', 'dongxing', '0770', '538100', 'D', '107.97204', '21.54713'); +INSERT INTO `yoshop_region` VALUES ('2225', '2162', '钦州', '钦州市', '中国,广西壮族自治区,钦州市', '2', 'qinzhou', '0777', '535099', 'Q', '108.624175', '21.967127'); +INSERT INTO `yoshop_region` VALUES ('2226', '2225', '钦南', '钦南区', '中国,广西壮族自治区,钦州市,钦南区', '3', 'qinnan', '0777', '535099', 'Q', '108.61775', '21.95137'); +INSERT INTO `yoshop_region` VALUES ('2227', '2225', '钦北', '钦北区', '中国,广西壮族自治区,钦州市,钦北区', '3', 'qinbei', '0777', '535099', 'Q', '108.63037', '21.95127'); +INSERT INTO `yoshop_region` VALUES ('2228', '2225', '灵山', '灵山县', '中国,广西壮族自治区,钦州市,灵山县', '3', 'lingshan', '0777', '535099', 'L', '109.29153', '22.4165'); +INSERT INTO `yoshop_region` VALUES ('2229', '2225', '浦北', '浦北县', '中国,广西壮族自治区,钦州市,浦北县', '3', 'pubei', '0777', '535099', 'P', '109.55572', '22.26888'); +INSERT INTO `yoshop_region` VALUES ('2230', '2162', '贵港', '贵港市', '中国,广西壮族自治区,贵港市', '2', 'guigang', '0775', '537100', 'G', '109.602146', '23.0936'); +INSERT INTO `yoshop_region` VALUES ('2231', '2230', '港北', '港北区', '中国,广西壮族自治区,贵港市,港北区', '3', 'gangbei', '0775', '537100', 'G', '109.57224', '23.11153'); +INSERT INTO `yoshop_region` VALUES ('2232', '2230', '港南', '港南区', '中国,广西壮族自治区,贵港市,港南区', '3', 'gangnan', '0775', '537100', 'G', '109.60617', '23.07226'); +INSERT INTO `yoshop_region` VALUES ('2233', '2230', '覃塘', '覃塘区', '中国,广西壮族自治区,贵港市,覃塘区', '3', 'qintang', '0775', '537121', null, '109.44293', '23.12677'); +INSERT INTO `yoshop_region` VALUES ('2234', '2230', '平南', '平南县', '中国,广西壮族自治区,贵港市,平南县', '3', 'pingnan', '0775', '537300', 'P', '110.39062', '23.54201'); +INSERT INTO `yoshop_region` VALUES ('2235', '2230', '桂平', '桂平市', '中国,广西壮族自治区,贵港市,桂平市', '3', 'guiping', '0775', '537200', 'G', '110.08105', '23.39339'); +INSERT INTO `yoshop_region` VALUES ('2236', '2162', '玉林', '玉林市', '中国,广西壮族自治区,玉林市', '2', 'yulin', '0775', '537000', 'Y', '110.154393', '22.63136'); +INSERT INTO `yoshop_region` VALUES ('2237', '2236', '玉州', '玉州区', '中国,广西壮族自治区,玉林市,玉州区', '3', 'yuzhou', '0775', '537000', 'Y', '110.15114', '22.6281'); +INSERT INTO `yoshop_region` VALUES ('2238', '2236', '福绵', '福绵区', '中国,广西壮族自治区,玉林市,福绵区', '3', 'fumian', '0775', '537023', 'F', '110.064816', '22.583057'); +INSERT INTO `yoshop_region` VALUES ('2239', '2236', '玉东', '玉东新区', '中国,广西壮族自治区,玉林市,玉东新区', '3', 'yudong', '0775', '537000', 'Y', '110.154393', '22.63136'); +INSERT INTO `yoshop_region` VALUES ('2240', '2236', '容县', '容县', '中国,广西壮族自治区,玉林市,容县', '3', 'rongxian', '0775', '537500', 'R', '110.55593', '22.85701'); +INSERT INTO `yoshop_region` VALUES ('2241', '2236', '陆川', '陆川县', '中国,广西壮族自治区,玉林市,陆川县', '3', 'luchuan', '0775', '537700', 'L', '110.26413', '22.32454'); +INSERT INTO `yoshop_region` VALUES ('2242', '2236', '博白', '博白县', '中国,广西壮族自治区,玉林市,博白县', '3', 'bobai', '0775', '537600', 'B', '109.97744', '22.27286'); +INSERT INTO `yoshop_region` VALUES ('2243', '2236', '兴业', '兴业县', '中国,广西壮族自治区,玉林市,兴业县', '3', 'xingye', '0775', '537800', 'X', '109.87612', '22.74237'); +INSERT INTO `yoshop_region` VALUES ('2244', '2236', '北流', '北流市', '中国,广西壮族自治区,玉林市,北流市', '3', 'beiliu', '0775', '537400', 'B', '110.35302', '22.70817'); +INSERT INTO `yoshop_region` VALUES ('2245', '2162', '百色', '百色市', '中国,广西壮族自治区,百色市', '2', 'baise', '0776', '533000', 'B', '106.616285', '23.897742'); +INSERT INTO `yoshop_region` VALUES ('2246', '2245', '右江', '右江区', '中国,广西壮族自治区,百色市,右江区', '3', 'youjiang', '0776', '533000', 'Y', '106.61764', '23.9009'); +INSERT INTO `yoshop_region` VALUES ('2247', '2245', '田阳', '田阳县', '中国,广西壮族自治区,百色市,田阳县', '3', 'tianyang', '0776', '533600', 'T', '106.91558', '23.73535'); +INSERT INTO `yoshop_region` VALUES ('2248', '2245', '田东', '田东县', '中国,广西壮族自治区,百色市,田东县', '3', 'tiandong', '0776', '531500', 'T', '107.12432', '23.60003'); +INSERT INTO `yoshop_region` VALUES ('2249', '2245', '平果', '平果县', '中国,广西壮族自治区,百色市,平果县', '3', 'pingguo', '0776', '531400', 'P', '107.59045', '23.32969'); +INSERT INTO `yoshop_region` VALUES ('2250', '2245', '德保', '德保县', '中国,广西壮族自治区,百色市,德保县', '3', 'debao', '0776', '533700', 'D', '106.61917', '23.32515'); +INSERT INTO `yoshop_region` VALUES ('2251', '2245', '靖西', '靖西县', '中国,广西壮族自治区,百色市,靖西县', '3', 'jingxi', '0776', '533800', 'J', '106.41766', '23.13425'); +INSERT INTO `yoshop_region` VALUES ('2252', '2245', '那坡', '那坡县', '中国,广西壮族自治区,百色市,那坡县', '3', 'napo', '0776', '533900', 'N', '105.84191', '23.40649'); +INSERT INTO `yoshop_region` VALUES ('2253', '2245', '凌云', '凌云县', '中国,广西壮族自治区,百色市,凌云县', '3', 'lingyun', '0776', '533100', 'L', '106.56155', '24.34747'); +INSERT INTO `yoshop_region` VALUES ('2254', '2245', '乐业', '乐业县', '中国,广西壮族自治区,百色市,乐业县', '3', 'leye', '0776', '533200', 'L', '106.56124', '24.78295'); +INSERT INTO `yoshop_region` VALUES ('2255', '2245', '田林', '田林县', '中国,广西壮族自治区,百色市,田林县', '3', 'tianlin', '0776', '533300', 'T', '106.22882', '24.29207'); +INSERT INTO `yoshop_region` VALUES ('2256', '2245', '西林', '西林县', '中国,广西壮族自治区,百色市,西林县', '3', 'xilin', '0776', '533500', 'X', '105.09722', '24.48966'); +INSERT INTO `yoshop_region` VALUES ('2257', '2245', '隆林', '隆林各族自治县', '中国,广西壮族自治区,百色市,隆林各族自治县', '3', 'longlin', '0776', '533400', 'L', '105.34295', '24.77036'); +INSERT INTO `yoshop_region` VALUES ('2258', '2162', '贺州', '贺州市', '中国,广西壮族自治区,贺州市', '2', 'hezhou', '0774', '542800', 'H', '111.552056', '24.414141'); +INSERT INTO `yoshop_region` VALUES ('2259', '2258', '八步', '八步区', '中国,广西壮族自治区,贺州市,八步区', '3', 'babu', '0774', '542800', 'B', '111.55225', '24.41179'); +INSERT INTO `yoshop_region` VALUES ('2260', '2258', '昭平', '昭平县', '中国,广西壮族自治区,贺州市,昭平县', '3', 'zhaoping', '0774', '546800', 'Z', '110.81082', '24.1701'); +INSERT INTO `yoshop_region` VALUES ('2261', '2258', '钟山', '钟山县', '中国,广西壮族自治区,贺州市,钟山县', '3', 'zhongshan', '0774', '542600', 'Z', '111.30459', '24.52482'); +INSERT INTO `yoshop_region` VALUES ('2262', '2258', '富川', '富川瑶族自治县', '中国,广西壮族自治区,贺州市,富川瑶族自治县', '3', 'fuchuan', '0774', '542700', 'F', '111.27767', '24.81431'); +INSERT INTO `yoshop_region` VALUES ('2263', '2258', '平桂', '平桂管理区', '中国,广西壮族自治区,贺州市,平桂管理区', '3', 'pingui', '0774', '542800', 'P', '111.485651', '24.458041'); +INSERT INTO `yoshop_region` VALUES ('2264', '2162', '河池', '河池市', '中国,广西壮族自治区,河池市', '2', 'hechi', '0778', '547000', 'H', '108.062105', '24.695899'); +INSERT INTO `yoshop_region` VALUES ('2265', '2264', '金城江', '金城江区', '中国,广西壮族自治区,河池市,金城江区', '3', 'jinchengjiang', '0779', '547000', 'J', '108.03727', '24.6897'); +INSERT INTO `yoshop_region` VALUES ('2266', '2264', '南丹', '南丹县', '中国,广西壮族自治区,河池市,南丹县', '3', 'nandan', '0781', '547200', 'N', '107.54562', '24.9776'); +INSERT INTO `yoshop_region` VALUES ('2267', '2264', '天峨', '天峨县', '中国,广西壮族自治区,河池市,天峨县', '3', 'tiane', '0782', '547300', 'T', '107.17205', '24.99593'); +INSERT INTO `yoshop_region` VALUES ('2268', '2264', '凤山', '凤山县', '中国,广西壮族自治区,河池市,凤山县', '3', 'fengshan', '0783', '547600', 'F', '107.04892', '24.54215'); +INSERT INTO `yoshop_region` VALUES ('2269', '2264', '东兰', '东兰县', '中国,广西壮族自治区,河池市,东兰县', '3', 'donglan', '0784', '547400', 'D', '107.37527', '24.51053'); +INSERT INTO `yoshop_region` VALUES ('2270', '2264', '罗城', '罗城仫佬族自治县', '中国,广西壮族自治区,河池市,罗城仫佬族自治县', '3', 'luocheng', '0785', '546400', 'L', '108.90777', '24.77923'); +INSERT INTO `yoshop_region` VALUES ('2271', '2264', '环江', '环江毛南族自治县', '中国,广西壮族自治区,河池市,环江毛南族自治县', '3', 'huanjiang', '0786', '547100', 'H', '108.26055', '24.82916'); +INSERT INTO `yoshop_region` VALUES ('2272', '2264', '巴马', '巴马瑶族自治县', '中国,广西壮族自治区,河池市,巴马瑶族自治县', '3', 'bama', '0787', '547500', 'B', '107.25308', '24.14135'); +INSERT INTO `yoshop_region` VALUES ('2273', '2264', '都安', '都安瑶族自治县', '中国,广西壮族自治区,河池市,都安瑶族自治县', '3', 'du\'an', '0788', '530700', 'D', '108.10116', '23.93245'); +INSERT INTO `yoshop_region` VALUES ('2274', '2264', '大化', '大化瑶族自治县', '中国,广西壮族自治区,河池市,大化瑶族自治县', '3', 'dahua', '0789', '530800', 'D', '107.9985', '23.74487'); +INSERT INTO `yoshop_region` VALUES ('2275', '2264', '宜州', '宜州市', '中国,广西壮族自治区,河池市,宜州市', '3', 'yizhou', '0780', '546300', 'Y', '108.65304', '24.49391'); +INSERT INTO `yoshop_region` VALUES ('2276', '2162', '来宾', '来宾市', '中国,广西壮族自治区,来宾市', '2', 'laibin', '0772', '546100', 'L', '109.229772', '23.733766'); +INSERT INTO `yoshop_region` VALUES ('2277', '2276', '兴宾', '兴宾区', '中国,广西壮族自治区,来宾市,兴宾区', '3', 'xingbin', '0772', '546100', 'X', '109.23471', '23.72731'); +INSERT INTO `yoshop_region` VALUES ('2278', '2276', '忻城', '忻城县', '中国,广西壮族自治区,来宾市,忻城县', '3', 'xincheng', '0772', '546200', 'X', '108.66357', '24.06862'); +INSERT INTO `yoshop_region` VALUES ('2279', '2276', '象州', '象州县', '中国,广西壮族自治区,来宾市,象州县', '3', 'xiangzhou', '0772', '545800', 'X', '109.6994', '23.97355'); +INSERT INTO `yoshop_region` VALUES ('2280', '2276', '武宣', '武宣县', '中国,广西壮族自治区,来宾市,武宣县', '3', 'wuxuan', '0772', '545900', 'W', '109.66284', '23.59474'); +INSERT INTO `yoshop_region` VALUES ('2281', '2276', '金秀', '金秀瑶族自治县', '中国,广西壮族自治区,来宾市,金秀瑶族自治县', '3', 'jinxiu', '0772', '545799', 'J', '110.19079', '24.12929'); +INSERT INTO `yoshop_region` VALUES ('2282', '2276', '合山', '合山市', '中国,广西壮族自治区,来宾市,合山市', '3', 'heshan', '0772', '546500', 'H', '108.88586', '23.80619'); +INSERT INTO `yoshop_region` VALUES ('2283', '2162', '崇左', '崇左市', '中国,广西壮族自治区,崇左市', '2', 'chongzuo', '0771', '532299', 'C', '107.353926', '22.404108'); +INSERT INTO `yoshop_region` VALUES ('2284', '2283', '江州', '江州区', '中国,广西壮族自治区,崇左市,江州区', '3', 'jiangzhou', '0771', '532299', 'J', '107.34747', '22.41135'); +INSERT INTO `yoshop_region` VALUES ('2285', '2283', '扶绥', '扶绥县', '中国,广西壮族自治区,崇左市,扶绥县', '3', 'fusui', '0771', '532199', 'F', '107.90405', '22.63413'); +INSERT INTO `yoshop_region` VALUES ('2286', '2283', '宁明', '宁明县', '中国,广西壮族自治区,崇左市,宁明县', '3', 'ningming', '0771', '532599', 'N', '107.07299', '22.13655'); +INSERT INTO `yoshop_region` VALUES ('2287', '2283', '龙州', '龙州县', '中国,广西壮族自治区,崇左市,龙州县', '3', 'longzhou', '0771', '532499', 'L', '106.85415', '22.33937'); +INSERT INTO `yoshop_region` VALUES ('2288', '2283', '大新', '大新县', '中国,广西壮族自治区,崇左市,大新县', '3', 'daxin', '0771', '532399', 'D', '107.19821', '22.83412'); +INSERT INTO `yoshop_region` VALUES ('2289', '2283', '天等', '天等县', '中国,广西壮族自治区,崇左市,天等县', '3', 'tiandeng', '0771', '532899', 'T', '107.13998', '23.077'); +INSERT INTO `yoshop_region` VALUES ('2290', '2283', '凭祥', '凭祥市', '中国,广西壮族自治区,崇左市,凭祥市', '3', 'pingxiang', '0771', '532699', 'P', '106.75534', '22.10573'); +INSERT INTO `yoshop_region` VALUES ('2291', '0', '海南', '海南省', '中国,海南省', '1', 'hainan', '', '', 'H', '110.33119', '20.031971'); +INSERT INTO `yoshop_region` VALUES ('2292', '2291', '海口', '海口市', '中国,海南省,海口市', '2', 'haikou', '0898', '570000', 'H', '110.33119', '20.031971'); +INSERT INTO `yoshop_region` VALUES ('2293', '2292', '秀英', '秀英区', '中国,海南省,海口市,秀英区', '3', 'xiuying', '0898', '570311', 'X', '110.29345', '20.00752'); +INSERT INTO `yoshop_region` VALUES ('2294', '2292', '龙华', '龙华区', '中国,海南省,海口市,龙华区', '3', 'longhua', '0898', '570145', 'L', '110.30194', '20.02866'); +INSERT INTO `yoshop_region` VALUES ('2295', '2292', '琼山', '琼山区', '中国,海南省,海口市,琼山区', '3', 'qiongshan', '0898', '571100', 'Q', '110.35418', '20.00321'); +INSERT INTO `yoshop_region` VALUES ('2296', '2292', '美兰', '美兰区', '中国,海南省,海口市,美兰区', '3', 'meilan', '0898', '570203', 'M', '110.36908', '20.02864'); +INSERT INTO `yoshop_region` VALUES ('2297', '2291', '三亚', '三亚市', '中国,海南省,三亚市', '2', 'sanya', '0898', '572000', 'S', '109.508268', '18.247872'); +INSERT INTO `yoshop_region` VALUES ('2298', '2297', '海棠', '海棠区', '中国,海南省,三亚市,海棠区', '3', 'haitang', '0898', '572000', 'H', '109.508268', '18.247872'); +INSERT INTO `yoshop_region` VALUES ('2299', '2297', '吉阳', '吉阳区', '中国,海南省,三亚市,吉阳区', '3', 'jiyang', '0898', '572000', 'J', '109.508268', '18.247872'); +INSERT INTO `yoshop_region` VALUES ('2300', '2297', '天涯', '天涯区', '中国,海南省,三亚市,天涯区', '3', 'tianya', '0898', '572000', 'T', '109.508268', '18.247872'); +INSERT INTO `yoshop_region` VALUES ('2301', '2297', '崖州', '崖州区', '中国,海南省,三亚市,崖州区', '3', 'yazhou', '0898', '572000', 'Y', '109.508268', '18.247872'); +INSERT INTO `yoshop_region` VALUES ('2302', '2291', '三沙', '三沙市', '中国,海南省,三沙市', '2', 'sansha', '0898', '573199', 'S', '112.34882', '16.831039'); +INSERT INTO `yoshop_region` VALUES ('2303', '2302', '西沙', '西沙群岛', '中国,海南省,三沙市,西沙群岛', '3', 'xishaislands', '0898', '572000', 'X', '112.025528', '16.331342'); +INSERT INTO `yoshop_region` VALUES ('2304', '2302', '南沙', '南沙群岛', '中国,海南省,三沙市,南沙群岛', '3', 'nanshaislands', '0898', '573100', 'N', '116.749998', '11.471888'); +INSERT INTO `yoshop_region` VALUES ('2305', '2302', '中沙', '中沙群岛', '中国,海南省,三沙市,中沙群岛', '3', 'zhongshaislands', '0898', '573100', 'Z', '117.740071', '15.112856'); +INSERT INTO `yoshop_region` VALUES ('2306', '2291', ' ', '直辖县级', '中国,海南省,直辖县级', '2', '', '', '', 'Z', '109.503479', '18.739906'); +INSERT INTO `yoshop_region` VALUES ('2307', '2306', '五指山', '五指山市', '中国,海南省,直辖县级,五指山市', '3', 'wuzhishan', '0898', '572200', 'W', '109.516662', '18.776921'); +INSERT INTO `yoshop_region` VALUES ('2308', '2306', '琼海', '琼海市', '中国,海南省,直辖县级,琼海市', '3', 'qionghai', '0898', '571400', 'Q', '110.466785', '19.246011'); +INSERT INTO `yoshop_region` VALUES ('2309', '2306', '儋州', '儋州市', '中国,海南省,直辖县级,儋州市', '3', 'danzhou', '0898', '571700', null, '109.576782', '19.517486'); +INSERT INTO `yoshop_region` VALUES ('2310', '2306', '文昌', '文昌市', '中国,海南省,直辖县级,文昌市', '3', 'wenchang', '0898', '571339', 'W', '110.753975', '19.612986'); +INSERT INTO `yoshop_region` VALUES ('2311', '2306', '万宁', '万宁市', '中国,海南省,直辖县级,万宁市', '3', 'wanning', '0898', '571500', 'W', '110.388793', '18.796216'); +INSERT INTO `yoshop_region` VALUES ('2312', '2306', '东方', '东方市', '中国,海南省,直辖县级,东方市', '3', 'dongfang', '0898', '572600', 'D', '108.653789', '19.10198'); +INSERT INTO `yoshop_region` VALUES ('2313', '2306', '定安', '定安县', '中国,海南省,直辖县级,定安县', '3', 'ding\'an', '0898', '571200', 'D', '110.323959', '19.699211'); +INSERT INTO `yoshop_region` VALUES ('2314', '2306', '屯昌', '屯昌县', '中国,海南省,直辖县级,屯昌县', '3', 'tunchang', '0898', '571600', 'T', '110.102773', '19.362916'); +INSERT INTO `yoshop_region` VALUES ('2315', '2306', '澄迈', '澄迈县', '中国,海南省,直辖县级,澄迈县', '3', 'chengmai', '0898', '571900', 'C', '110.007147', '19.737095'); +INSERT INTO `yoshop_region` VALUES ('2316', '2306', '临高', '临高县', '中国,海南省,直辖县级,临高县', '3', 'lingao', '0898', '571800', 'L', '109.687697', '19.908293'); +INSERT INTO `yoshop_region` VALUES ('2317', '2306', '白沙', '白沙黎族自治县', '中国,海南省,直辖县级,白沙黎族自治县', '3', 'baisha', '0898', '572800', 'B', '109.452606', '19.224584'); +INSERT INTO `yoshop_region` VALUES ('2318', '2306', '昌江', '昌江黎族自治县', '中国,海南省,直辖县级,昌江黎族自治县', '3', 'changjiang', '0898', '572700', 'C', '109.053351', '19.260968'); +INSERT INTO `yoshop_region` VALUES ('2319', '2306', '乐东', '乐东黎族自治县', '中国,海南省,直辖县级,乐东黎族自治县', '3', 'ledong', '0898', '572500', 'L', '109.175444', '18.74758'); +INSERT INTO `yoshop_region` VALUES ('2320', '2306', '陵水', '陵水黎族自治县', '中国,海南省,直辖县级,陵水黎族自治县', '3', 'lingshui', '0898', '572400', 'L', '110.037218', '18.505006'); +INSERT INTO `yoshop_region` VALUES ('2321', '2306', '保亭', '保亭黎族苗族自治县', '中国,海南省,直辖县级,保亭黎族苗族自治县', '3', 'baoting', '0898', '572300', 'B', '109.70245', '18.636371'); +INSERT INTO `yoshop_region` VALUES ('2322', '2306', '琼中', '琼中黎族苗族自治县', '中国,海南省,直辖县级,琼中黎族苗族自治县', '3', 'qiongzhong', '0898', '572900', 'Q', '109.839996', '19.03557'); +INSERT INTO `yoshop_region` VALUES ('2323', '0', '重庆', '重庆市', '中国,重庆', '1', 'chongqing', '', '', 'Z', '106.504962', '29.533155'); +INSERT INTO `yoshop_region` VALUES ('2324', '2323', '重庆', '重庆市', '中国,重庆,重庆市', '2', 'chongqing', '023', '400000', 'Z', '106.504962', '29.533155'); +INSERT INTO `yoshop_region` VALUES ('2325', '2324', '万州', '万州区', '中国,重庆,重庆市,万州区', '3', 'wanzhou', '023', '404000', 'W', '108.40869', '30.80788'); +INSERT INTO `yoshop_region` VALUES ('2326', '2324', '涪陵', '涪陵区', '中国,重庆,重庆市,涪陵区', '3', 'fuling', '023', '408000', 'F', '107.39007', '29.70292'); +INSERT INTO `yoshop_region` VALUES ('2327', '2324', '渝中', '渝中区', '中国,重庆,重庆市,渝中区', '3', 'yuzhong', '023', '400010', 'Y', '106.56901', '29.55279'); +INSERT INTO `yoshop_region` VALUES ('2328', '2324', '大渡口', '大渡口区', '中国,重庆,重庆市,大渡口区', '3', 'dadukou', '023', '400080', 'D', '106.48262', '29.48447'); +INSERT INTO `yoshop_region` VALUES ('2329', '2324', '江北', '江北区', '中国,重庆,重庆市,江北区', '3', 'jiangbei', '023', '400020', 'J', '106.57434', '29.60658'); +INSERT INTO `yoshop_region` VALUES ('2330', '2324', '沙坪坝', '沙坪坝区', '中国,重庆,重庆市,沙坪坝区', '3', 'shapingba', '023', '400030', 'S', '106.45752', '29.54113'); +INSERT INTO `yoshop_region` VALUES ('2331', '2324', '九龙坡', '九龙坡区', '中国,重庆,重庆市,九龙坡区', '3', 'jiulongpo', '023', '400050', 'J', '106.51107', '29.50197'); +INSERT INTO `yoshop_region` VALUES ('2332', '2324', '南岸', '南岸区', '中国,重庆,重庆市,南岸区', '3', 'nan\'an', '023', '400064', 'N', '106.56347', '29.52311'); +INSERT INTO `yoshop_region` VALUES ('2333', '2324', '北碚', '北碚区', '中国,重庆,重庆市,北碚区', '3', 'beibei', '023', '400700', 'B', '106.39614', '29.80574'); +INSERT INTO `yoshop_region` VALUES ('2334', '2324', '綦江', '綦江区', '中国,重庆,重庆市,綦江区', '3', 'qijiang', '023', '400800', null, '106.926779', '28.960656'); +INSERT INTO `yoshop_region` VALUES ('2335', '2324', '大足', '大足区', '中国,重庆,重庆市,大足区', '3', 'dazu', '023', '400900', 'D', '105.768121', '29.484025'); +INSERT INTO `yoshop_region` VALUES ('2336', '2324', '渝北', '渝北区', '中国,重庆,重庆市,渝北区', '3', 'yubei', '023', '401120', 'Y', '106.6307', '29.7182'); +INSERT INTO `yoshop_region` VALUES ('2337', '2324', '巴南', '巴南区', '中国,重庆,重庆市,巴南区', '3', 'banan', '023', '401320', 'B', '106.52365', '29.38311'); +INSERT INTO `yoshop_region` VALUES ('2338', '2324', '黔江', '黔江区', '中国,重庆,重庆市,黔江区', '3', 'qianjiang', '023', '409700', 'Q', '108.7709', '29.5332'); +INSERT INTO `yoshop_region` VALUES ('2339', '2324', '长寿', '长寿区', '中国,重庆,重庆市,长寿区', '3', 'changshou', '023', '401220', 'C', '107.08166', '29.85359'); +INSERT INTO `yoshop_region` VALUES ('2340', '2324', '江津', '江津区', '中国,重庆,重庆市,江津区', '3', 'jiangjin', '023', '402260', 'J', '106.25912', '29.29008'); +INSERT INTO `yoshop_region` VALUES ('2341', '2324', '合川', '合川区', '中国,重庆,重庆市,合川区', '3', 'hechuan', '023', '401520', 'H', '106.27633', '29.97227'); +INSERT INTO `yoshop_region` VALUES ('2342', '2324', '永川', '永川区', '中国,重庆,重庆市,永川区', '3', 'yongchuan', '023', '402160', 'Y', '105.927', '29.35593'); +INSERT INTO `yoshop_region` VALUES ('2343', '2324', '南川', '南川区', '中国,重庆,重庆市,南川区', '3', 'nanchuan', '023', '408400', 'N', '107.09936', '29.15751'); +INSERT INTO `yoshop_region` VALUES ('2344', '2324', '璧山', '璧山区', '中国,重庆,重庆市,璧山区', '3', 'bishan', '023', '402760', null, '106.231126', '29.593581'); +INSERT INTO `yoshop_region` VALUES ('2345', '2324', '铜梁', '铜梁区', '中国,重庆,重庆市,铜梁区', '3', 'tongliang', '023', '402560', 'T', '106.054948', '29.839944'); +INSERT INTO `yoshop_region` VALUES ('2346', '2324', '潼南', '潼南区', '中国,重庆,重庆市,潼南区', '3', 'tongnan', '023', '402660', null, '105.84005', '30.1912'); +INSERT INTO `yoshop_region` VALUES ('2347', '2324', '荣昌', '荣昌区', '中国,重庆,重庆市,荣昌区', '3', 'rongchang', '023', '402460', 'R', '105.59442', '29.40488'); +INSERT INTO `yoshop_region` VALUES ('2348', '2324', '梁平', '梁平区', '中国,重庆,重庆市,梁平区', '3', 'liangping', '023', '405200', 'L', '107.79998', '30.67545'); +INSERT INTO `yoshop_region` VALUES ('2349', '2363', '城口', '城口县', '中国,重庆,重庆市,城口县', '3', 'chengkou', '023', '405900', 'C', '108.66513', '31.94801'); +INSERT INTO `yoshop_region` VALUES ('2350', '2363', '丰都', '丰都县', '中国,重庆,重庆市,丰都县', '3', 'fengdu', '023', '408200', 'F', '107.73098', '29.86348'); +INSERT INTO `yoshop_region` VALUES ('2351', '2363', '垫江', '垫江县', '中国,重庆,重庆市,垫江县', '3', 'dianjiang', '023', '408300', 'D', '107.35446', '30.33359'); +INSERT INTO `yoshop_region` VALUES ('2352', '2363', '武隆', '武隆县', '中国,重庆,重庆市,武隆县', '3', 'wulong', '023', '408500', 'W', '107.7601', '29.32548'); +INSERT INTO `yoshop_region` VALUES ('2353', '2363', '忠县', '忠县', '中国,重庆,重庆市,忠县', '3', 'zhongxian', '023', '404300', 'Z', '108.03689', '30.28898'); +INSERT INTO `yoshop_region` VALUES ('2354', '2363', '开县', '开县', '中国,重庆,重庆市,开县', '3', 'kaixian', '023', '405400', 'K', '108.39306', '31.16095'); +INSERT INTO `yoshop_region` VALUES ('2355', '2363', '云阳', '云阳县', '中国,重庆,重庆市,云阳县', '3', 'yunyang', '023', '404500', 'Y', '108.69726', '30.93062'); +INSERT INTO `yoshop_region` VALUES ('2356', '2363', '奉节', '奉节县', '中国,重庆,重庆市,奉节县', '3', 'fengjie', '023', '404600', 'F', '109.46478', '31.01825'); +INSERT INTO `yoshop_region` VALUES ('2357', '2363', '巫山', '巫山县', '中国,重庆,重庆市,巫山县', '3', 'wushan', '023', '404700', 'W', '109.87814', '31.07458'); +INSERT INTO `yoshop_region` VALUES ('2358', '2363', '巫溪', '巫溪县', '中国,重庆,重庆市,巫溪县', '3', 'wuxi', '023', '405800', 'W', '109.63128', '31.39756'); +INSERT INTO `yoshop_region` VALUES ('2359', '2363', '石柱', '石柱土家族自治县', '中国,重庆,重庆市,石柱土家族自治县', '3', 'shizhu', '023', '409100', 'S', '108.11389', '30.00054'); +INSERT INTO `yoshop_region` VALUES ('2360', '2363', '秀山', '秀山土家族苗族自治县', '中国,重庆,重庆市,秀山土家族苗族自治县', '3', 'xiushan', '023', '409900', 'X', '108.98861', '28.45062'); +INSERT INTO `yoshop_region` VALUES ('2361', '2363', '酉阳', '酉阳土家族苗族自治县', '中国,重庆,重庆市,酉阳土家族苗族自治县', '3', 'youyang', '023', '409800', 'Y', '108.77212', '28.8446'); +INSERT INTO `yoshop_region` VALUES ('2362', '2363', '彭水', '彭水苗族土家族自治县', '中国,重庆,重庆市,彭水苗族土家族自治县', '3', 'pengshui', '023', '409600', 'P', '108.16638', '29.29516'); +INSERT INTO `yoshop_region` VALUES ('2363', '2323', '县', '县', '中国,重庆,县', '2', 'xian', '023', '400000', 'L', '106.463344', '29.729153'); +INSERT INTO `yoshop_region` VALUES ('2367', '0', '四川', '四川省', '中国,四川省', '1', 'sichuan', '', '', 'S', '104.065735', '30.659462'); +INSERT INTO `yoshop_region` VALUES ('2368', '2367', '成都', '成都市', '中国,四川省,成都市', '2', 'chengdu', '028', '610015', 'C', '104.065735', '30.659462'); +INSERT INTO `yoshop_region` VALUES ('2369', '2368', '锦江', '锦江区', '中国,四川省,成都市,锦江区', '3', 'jinjiang', '028', '610021', 'J', '104.08347', '30.65614'); +INSERT INTO `yoshop_region` VALUES ('2370', '2368', '青羊', '青羊区', '中国,四川省,成都市,青羊区', '3', 'qingyang', '028', '610031', 'Q', '104.06151', '30.67387'); +INSERT INTO `yoshop_region` VALUES ('2371', '2368', '金牛', '金牛区', '中国,四川省,成都市,金牛区', '3', 'jinniu', '028', '610036', 'J', '104.05114', '30.69126'); +INSERT INTO `yoshop_region` VALUES ('2372', '2368', '武侯', '武侯区', '中国,四川省,成都市,武侯区', '3', 'wuhou', '028', '610041', 'W', '104.04303', '30.64235'); +INSERT INTO `yoshop_region` VALUES ('2373', '2368', '成华', '成华区', '中国,四川省,成都市,成华区', '3', 'chenghua', '028', '610066', 'C', '104.10193', '30.65993'); +INSERT INTO `yoshop_region` VALUES ('2374', '2368', '龙泉驿', '龙泉驿区', '中国,四川省,成都市,龙泉驿区', '3', 'longquanyi', '028', '610100', 'L', '104.27462', '30.55658'); +INSERT INTO `yoshop_region` VALUES ('2375', '2368', '青白江', '青白江区', '中国,四川省,成都市,青白江区', '3', 'qingbaijiang', '028', '610300', 'Q', '104.251', '30.87841'); +INSERT INTO `yoshop_region` VALUES ('2376', '2368', '新都', '新都区', '中国,四川省,成都市,新都区', '3', 'xindu', '028', '610500', 'X', '104.15921', '30.82314'); +INSERT INTO `yoshop_region` VALUES ('2377', '2368', '温江', '温江区', '中国,四川省,成都市,温江区', '3', 'wenjiang', '028', '611130', 'W', '103.84881', '30.68444'); +INSERT INTO `yoshop_region` VALUES ('2378', '2368', '金堂', '金堂县', '中国,四川省,成都市,金堂县', '3', 'jintang', '028', '610400', 'J', '104.41195', '30.86195'); +INSERT INTO `yoshop_region` VALUES ('2379', '2368', '双流', '双流县', '中国,四川省,成都市,双流县', '3', 'shuangliu', '028', '610200', 'S', '103.92373', '30.57444'); +INSERT INTO `yoshop_region` VALUES ('2380', '2368', '郫县', '郫县', '中国,四川省,成都市,郫县', '3', 'pixian', '028', '611730', null, '103.88717', '30.81054'); +INSERT INTO `yoshop_region` VALUES ('2381', '2368', '大邑', '大邑县', '中国,四川省,成都市,大邑县', '3', 'dayi', '028', '611330', 'D', '103.52075', '30.58738'); +INSERT INTO `yoshop_region` VALUES ('2382', '2368', '蒲江', '蒲江县', '中国,四川省,成都市,蒲江县', '3', 'pujiang', '028', '611630', 'P', '103.50616', '30.19667'); +INSERT INTO `yoshop_region` VALUES ('2383', '2368', '新津', '新津县', '中国,四川省,成都市,新津县', '3', 'xinjin', '028', '611430', 'X', '103.8114', '30.40983'); +INSERT INTO `yoshop_region` VALUES ('2384', '2368', '都江堰', '都江堰市', '中国,四川省,成都市,都江堰市', '3', 'dujiangyan', '028', '611830', 'D', '103.61941', '30.99825'); +INSERT INTO `yoshop_region` VALUES ('2385', '2368', '彭州', '彭州市', '中国,四川省,成都市,彭州市', '3', 'pengzhou', '028', '611930', 'P', '103.958', '30.99011'); +INSERT INTO `yoshop_region` VALUES ('2386', '2368', '邛崃', '邛崃市', '中国,四川省,成都市,邛崃市', '3', 'qionglai', '028', '611530', null, '103.46283', '30.41489'); +INSERT INTO `yoshop_region` VALUES ('2387', '2368', '崇州', '崇州市', '中国,四川省,成都市,崇州市', '3', 'chongzhou', '028', '611230', 'C', '103.67285', '30.63014'); +INSERT INTO `yoshop_region` VALUES ('2388', '2367', '自贡', '自贡市', '中国,四川省,自贡市', '2', 'zigong', '0813', '643000', 'Z', '104.773447', '29.352765'); +INSERT INTO `yoshop_region` VALUES ('2389', '2388', '自流井', '自流井区', '中国,四川省,自贡市,自流井区', '3', 'ziliujing', '0813', '643000', 'Z', '104.77719', '29.33745'); +INSERT INTO `yoshop_region` VALUES ('2390', '2388', '贡井', '贡井区', '中国,四川省,自贡市,贡井区', '3', 'gongjing', '0813', '643020', 'G', '104.71536', '29.34576'); +INSERT INTO `yoshop_region` VALUES ('2391', '2388', '大安', '大安区', '中国,四川省,自贡市,大安区', '3', 'da\'an', '0813', '643010', 'D', '104.77383', '29.36364'); +INSERT INTO `yoshop_region` VALUES ('2392', '2388', '沿滩', '沿滩区', '中国,四川省,自贡市,沿滩区', '3', 'yantan', '0813', '643030', 'Y', '104.88012', '29.26611'); +INSERT INTO `yoshop_region` VALUES ('2393', '2388', '荣县', '荣县', '中国,四川省,自贡市,荣县', '3', 'rongxian', '0813', '643100', 'R', '104.4176', '29.44445'); +INSERT INTO `yoshop_region` VALUES ('2394', '2388', '富顺', '富顺县', '中国,四川省,自贡市,富顺县', '3', 'fushun', '0813', '643200', 'F', '104.97491', '29.18123'); +INSERT INTO `yoshop_region` VALUES ('2395', '2367', '攀枝花', '攀枝花市', '中国,四川省,攀枝花市', '2', 'panzhihua', '0812', '617000', 'P', '101.716007', '26.580446'); +INSERT INTO `yoshop_region` VALUES ('2396', '2395', '东区', '东区', '中国,四川省,攀枝花市,东区', '3', 'dongqu', '0812', '617067', 'D', '101.7052', '26.54677'); +INSERT INTO `yoshop_region` VALUES ('2397', '2395', '西区', '西区', '中国,四川省,攀枝花市,西区', '3', 'xiqu', '0812', '617068', 'X', '101.63058', '26.59753'); +INSERT INTO `yoshop_region` VALUES ('2398', '2395', '仁和', '仁和区', '中国,四川省,攀枝花市,仁和区', '3', 'renhe', '0812', '617061', 'R', '101.73812', '26.49841'); +INSERT INTO `yoshop_region` VALUES ('2399', '2395', '米易', '米易县', '中国,四川省,攀枝花市,米易县', '3', 'miyi', '0812', '617200', 'M', '102.11132', '26.88718'); +INSERT INTO `yoshop_region` VALUES ('2400', '2395', '盐边', '盐边县', '中国,四川省,攀枝花市,盐边县', '3', 'yanbian', '0812', '617100', 'Y', '101.85446', '26.68847'); +INSERT INTO `yoshop_region` VALUES ('2401', '2367', '泸州', '泸州市', '中国,四川省,泸州市', '2', 'luzhou', '0830', '646000', null, '105.443348', '28.889138'); +INSERT INTO `yoshop_region` VALUES ('2402', '2401', '江阳', '江阳区', '中国,四川省,泸州市,江阳区', '3', 'jiangyang', '0830', '646000', 'J', '105.45336', '28.88934'); +INSERT INTO `yoshop_region` VALUES ('2403', '2401', '纳溪', '纳溪区', '中国,四川省,泸州市,纳溪区', '3', 'naxi', '0830', '646300', 'N', '105.37255', '28.77343'); +INSERT INTO `yoshop_region` VALUES ('2404', '2401', '龙马潭', '龙马潭区', '中国,四川省,泸州市,龙马潭区', '3', 'longmatan', '0830', '646000', 'L', '105.43774', '28.91308'); +INSERT INTO `yoshop_region` VALUES ('2405', '2401', '泸县', '泸县', '中国,四川省,泸州市,泸县', '3', 'luxian', '0830', '646106', null, '105.38192', '29.15041'); +INSERT INTO `yoshop_region` VALUES ('2406', '2401', '合江', '合江县', '中国,四川省,泸州市,合江县', '3', 'hejiang', '0830', '646200', 'H', '105.8352', '28.81005'); +INSERT INTO `yoshop_region` VALUES ('2407', '2401', '叙永', '叙永县', '中国,四川省,泸州市,叙永县', '3', 'xuyong', '0830', '646400', 'X', '105.44473', '28.15586'); +INSERT INTO `yoshop_region` VALUES ('2408', '2401', '古蔺', '古蔺县', '中国,四川省,泸州市,古蔺县', '3', 'gulin', '0830', '646500', 'G', '105.81347', '28.03867'); +INSERT INTO `yoshop_region` VALUES ('2409', '2367', '德阳', '德阳市', '中国,四川省,德阳市', '2', 'deyang', '0838', '618000', 'D', '104.398651', '31.127991'); +INSERT INTO `yoshop_region` VALUES ('2410', '2409', '旌阳', '旌阳区', '中国,四川省,德阳市,旌阳区', '3', 'jingyang', '0838', '618000', null, '104.39367', '31.13906'); +INSERT INTO `yoshop_region` VALUES ('2411', '2409', '中江', '中江县', '中国,四川省,德阳市,中江县', '3', 'zhongjiang', '0838', '618100', 'Z', '104.67861', '31.03297'); +INSERT INTO `yoshop_region` VALUES ('2412', '2409', '罗江', '罗江县', '中国,四川省,德阳市,罗江县', '3', 'luojiang', '0838', '618500', 'L', '104.51025', '31.31665'); +INSERT INTO `yoshop_region` VALUES ('2413', '2409', '广汉', '广汉市', '中国,四川省,德阳市,广汉市', '3', 'guanghan', '0838', '618300', 'G', '104.28234', '30.97686'); +INSERT INTO `yoshop_region` VALUES ('2414', '2409', '什邡', '什邡市', '中国,四川省,德阳市,什邡市', '3', 'shifang', '0838', '618400', 'S', '104.16754', '31.1264'); +INSERT INTO `yoshop_region` VALUES ('2415', '2409', '绵竹', '绵竹市', '中国,四川省,德阳市,绵竹市', '3', 'mianzhu', '0838', '618200', 'M', '104.22076', '31.33772'); +INSERT INTO `yoshop_region` VALUES ('2416', '2367', '绵阳', '绵阳市', '中国,四川省,绵阳市', '2', 'mianyang', '0816', '621000', 'M', '104.741722', '31.46402'); +INSERT INTO `yoshop_region` VALUES ('2417', '2416', '涪城', '涪城区', '中国,四川省,绵阳市,涪城区', '3', 'fucheng', '0816', '621000', 'F', '104.75719', '31.45522'); +INSERT INTO `yoshop_region` VALUES ('2418', '2416', '游仙', '游仙区', '中国,四川省,绵阳市,游仙区', '3', 'youxian', '0816', '621022', 'Y', '104.77092', '31.46574'); +INSERT INTO `yoshop_region` VALUES ('2419', '2416', '三台', '三台县', '中国,四川省,绵阳市,三台县', '3', 'santai', '0816', '621100', 'S', '105.09079', '31.09179'); +INSERT INTO `yoshop_region` VALUES ('2420', '2416', '盐亭', '盐亭县', '中国,四川省,绵阳市,盐亭县', '3', 'yanting', '0816', '621600', 'Y', '105.3898', '31.22176'); +INSERT INTO `yoshop_region` VALUES ('2421', '2416', '安县', '安县', '中国,四川省,绵阳市,安县', '3', 'anxian', '0816', '622650', 'A', '104.56738', '31.53487'); +INSERT INTO `yoshop_region` VALUES ('2422', '2416', '梓潼', '梓潼县', '中国,四川省,绵阳市,梓潼县', '3', 'zitong', '0816', '622150', null, '105.16183', '31.6359'); +INSERT INTO `yoshop_region` VALUES ('2423', '2416', '北川', '北川羌族自治县', '中国,四川省,绵阳市,北川羌族自治县', '3', 'beichuan', '0816', '622750', 'B', '104.46408', '31.83286'); +INSERT INTO `yoshop_region` VALUES ('2424', '2416', '平武', '平武县', '中国,四川省,绵阳市,平武县', '3', 'pingwu', '0816', '622550', 'P', '104.52862', '32.40791'); +INSERT INTO `yoshop_region` VALUES ('2425', '2416', '江油', '江油市', '中国,四川省,绵阳市,江油市', '3', 'jiangyou', '0816', '621700', 'J', '104.74539', '31.77775'); +INSERT INTO `yoshop_region` VALUES ('2426', '2367', '广元', '广元市', '中国,四川省,广元市', '2', 'guangyuan', '0839', '628000', 'G', '105.829757', '32.433668'); +INSERT INTO `yoshop_region` VALUES ('2427', '2426', '利州', '利州区', '中国,四川省,广元市,利州区', '3', 'lizhou', '0839', '628017', 'L', '105.826194', '32.432276'); +INSERT INTO `yoshop_region` VALUES ('2428', '2426', '昭化', '昭化区', '中国,四川省,广元市,昭化区', '3', 'zhaohua', '0839', '628017', 'Z', '105.640491', '32.386518'); +INSERT INTO `yoshop_region` VALUES ('2429', '2426', '朝天', '朝天区', '中国,四川省,广元市,朝天区', '3', 'chaotian', '0839', '628017', 'C', '105.89273', '32.64398'); +INSERT INTO `yoshop_region` VALUES ('2430', '2426', '旺苍', '旺苍县', '中国,四川省,广元市,旺苍县', '3', 'wangcang', '0839', '628200', 'W', '106.29022', '32.22845'); +INSERT INTO `yoshop_region` VALUES ('2431', '2426', '青川', '青川县', '中国,四川省,广元市,青川县', '3', 'qingchuan', '0839', '628100', 'Q', '105.2391', '32.58563'); +INSERT INTO `yoshop_region` VALUES ('2432', '2426', '剑阁', '剑阁县', '中国,四川省,广元市,剑阁县', '3', 'jiange', '0839', '628300', 'J', '105.5252', '32.28845'); +INSERT INTO `yoshop_region` VALUES ('2433', '2426', '苍溪', '苍溪县', '中国,四川省,广元市,苍溪县', '3', 'cangxi', '0839', '628400', 'C', '105.936', '31.73209'); +INSERT INTO `yoshop_region` VALUES ('2434', '2367', '遂宁', '遂宁市', '中国,四川省,遂宁市', '2', 'suining', '0825', '629000', 'S', '105.571331', '30.513311'); +INSERT INTO `yoshop_region` VALUES ('2435', '2434', '船山', '船山区', '中国,四川省,遂宁市,船山区', '3', 'chuanshan', '0825', '629000', 'C', '105.5809', '30.49991'); +INSERT INTO `yoshop_region` VALUES ('2436', '2434', '安居', '安居区', '中国,四川省,遂宁市,安居区', '3', 'anju', '0825', '629000', 'A', '105.46402', '30.35778'); +INSERT INTO `yoshop_region` VALUES ('2437', '2434', '蓬溪', '蓬溪县', '中国,四川省,遂宁市,蓬溪县', '3', 'pengxi', '0825', '629100', 'P', '105.70752', '30.75775'); +INSERT INTO `yoshop_region` VALUES ('2438', '2434', '射洪', '射洪县', '中国,四川省,遂宁市,射洪县', '3', 'shehong', '0825', '629200', 'S', '105.38922', '30.87203'); +INSERT INTO `yoshop_region` VALUES ('2439', '2434', '大英', '大英县', '中国,四川省,遂宁市,大英县', '3', 'daying', '0825', '629300', 'D', '105.24346', '30.59434'); +INSERT INTO `yoshop_region` VALUES ('2440', '2367', '内江', '内江市', '中国,四川省,内江市', '2', 'neijiang', '0832', '641000', 'N', '105.066138', '29.58708'); +INSERT INTO `yoshop_region` VALUES ('2441', '2440', '市中区', '市中区', '中国,四川省,内江市,市中区', '3', 'shizhongqu', '0832', '641000', 'S', '105.0679', '29.58709'); +INSERT INTO `yoshop_region` VALUES ('2442', '2440', '东兴', '东兴区', '中国,四川省,内江市,东兴区', '3', 'dongxing', '0832', '641100', 'D', '105.07554', '29.59278'); +INSERT INTO `yoshop_region` VALUES ('2443', '2440', '威远', '威远县', '中国,四川省,内江市,威远县', '3', 'weiyuan', '0832', '642450', 'W', '104.66955', '29.52823'); +INSERT INTO `yoshop_region` VALUES ('2444', '2440', '资中', '资中县', '中国,四川省,内江市,资中县', '3', 'zizhong', '0832', '641200', 'Z', '104.85205', '29.76409'); +INSERT INTO `yoshop_region` VALUES ('2445', '2440', '隆昌', '隆昌县', '中国,四川省,内江市,隆昌县', '3', 'longchang', '0832', '642150', 'L', '105.28738', '29.33937'); +INSERT INTO `yoshop_region` VALUES ('2446', '2367', '乐山', '乐山市', '中国,四川省,乐山市', '2', 'leshan', '0833', '614000', 'L', '103.761263', '29.582024'); +INSERT INTO `yoshop_region` VALUES ('2447', '2446', '市中区', '市中区', '中国,四川省,乐山市,市中区', '3', 'shizhongqu', '0833', '614000', 'S', '103.76159', '29.55543'); +INSERT INTO `yoshop_region` VALUES ('2448', '2446', '沙湾', '沙湾区', '中国,四川省,乐山市,沙湾区', '3', 'shawan', '0833', '614900', 'S', '103.54873', '29.41194'); +INSERT INTO `yoshop_region` VALUES ('2449', '2446', '五通桥', '五通桥区', '中国,四川省,乐山市,五通桥区', '3', 'wutongqiao', '0833', '614800', 'W', '103.82345', '29.40344'); +INSERT INTO `yoshop_region` VALUES ('2450', '2446', '金口河', '金口河区', '中国,四川省,乐山市,金口河区', '3', 'jinkouhe', '0833', '614700', 'J', '103.07858', '29.24578'); +INSERT INTO `yoshop_region` VALUES ('2451', '2446', '犍为', '犍为县', '中国,四川省,乐山市,犍为县', '3', 'qianwei', '0833', '614400', null, '103.94989', '29.20973'); +INSERT INTO `yoshop_region` VALUES ('2452', '2446', '井研', '井研县', '中国,四川省,乐山市,井研县', '3', 'jingyan', '0833', '613100', 'J', '104.07019', '29.65228'); +INSERT INTO `yoshop_region` VALUES ('2453', '2446', '夹江', '夹江县', '中国,四川省,乐山市,夹江县', '3', 'jiajiang', '0833', '614100', 'J', '103.57199', '29.73868'); +INSERT INTO `yoshop_region` VALUES ('2454', '2446', '沐川', '沐川县', '中国,四川省,乐山市,沐川县', '3', 'muchuan', '0833', '614500', null, '103.90353', '28.95646'); +INSERT INTO `yoshop_region` VALUES ('2455', '2446', '峨边', '峨边彝族自治县', '中国,四川省,乐山市,峨边彝族自治县', '3', 'ebian', '0833', '614300', 'E', '103.26339', '29.23004'); +INSERT INTO `yoshop_region` VALUES ('2456', '2446', '马边', '马边彝族自治县', '中国,四川省,乐山市,马边彝族自治县', '3', 'mabian', '0833', '614600', 'M', '103.54617', '28.83593'); +INSERT INTO `yoshop_region` VALUES ('2457', '2446', '峨眉山', '峨眉山市', '中国,四川省,乐山市,峨眉山市', '3', 'emeishan', '0833', '614200', 'E', '103.4844', '29.60117'); +INSERT INTO `yoshop_region` VALUES ('2458', '2367', '南充', '南充市', '中国,四川省,南充市', '2', 'nanchong', '0817', '637000', 'N', '106.082974', '30.795281'); +INSERT INTO `yoshop_region` VALUES ('2459', '2458', '顺庆', '顺庆区', '中国,四川省,南充市,顺庆区', '3', 'shunqing', '0817', '637000', 'S', '106.09216', '30.79642'); +INSERT INTO `yoshop_region` VALUES ('2460', '2458', '高坪', '高坪区', '中国,四川省,南充市,高坪区', '3', 'gaoping', '0817', '637100', 'G', '106.11894', '30.78162'); +INSERT INTO `yoshop_region` VALUES ('2461', '2458', '嘉陵', '嘉陵区', '中国,四川省,南充市,嘉陵区', '3', 'jialing', '0817', '637100', 'J', '106.07141', '30.75848'); +INSERT INTO `yoshop_region` VALUES ('2462', '2458', '南部', '南部县', '中国,四川省,南充市,南部县', '3', 'nanbu', '0817', '637300', 'N', '106.06738', '31.35451'); +INSERT INTO `yoshop_region` VALUES ('2463', '2458', '营山', '营山县', '中国,四川省,南充市,营山县', '3', 'yingshan', '0817', '637700', 'Y', '106.56637', '31.07747'); +INSERT INTO `yoshop_region` VALUES ('2464', '2458', '蓬安', '蓬安县', '中国,四川省,南充市,蓬安县', '3', 'peng\'an', '0817', '637800', 'P', '106.41262', '31.02964'); +INSERT INTO `yoshop_region` VALUES ('2465', '2458', '仪陇', '仪陇县', '中国,四川省,南充市,仪陇县', '3', 'yilong', '0817', '637600', 'Y', '106.29974', '31.27628'); +INSERT INTO `yoshop_region` VALUES ('2466', '2458', '西充', '西充县', '中国,四川省,南充市,西充县', '3', 'xichong', '0817', '637200', 'X', '105.89996', '30.9969'); +INSERT INTO `yoshop_region` VALUES ('2467', '2458', '阆中', '阆中市', '中国,四川省,南充市,阆中市', '3', 'langzhong', '0817', '637400', null, '106.00494', '31.55832'); +INSERT INTO `yoshop_region` VALUES ('2468', '2367', '眉山', '眉山市', '中国,四川省,眉山市', '2', 'meishan', '028', '620020', 'M', '103.831788', '30.048318'); +INSERT INTO `yoshop_region` VALUES ('2469', '2468', '东坡', '东坡区', '中国,四川省,眉山市,东坡区', '3', 'dongpo', '028', '620010', 'D', '103.832', '30.04219'); +INSERT INTO `yoshop_region` VALUES ('2470', '2468', '彭山', '彭山区', '中国,四川省,眉山市,彭山区', '3', 'pengshan', '028', '620860', 'P', '103.87268', '30.19283'); +INSERT INTO `yoshop_region` VALUES ('2471', '2468', '仁寿', '仁寿县', '中国,四川省,眉山市,仁寿县', '3', 'renshou', '028', '620500', 'R', '104.13412', '29.99599'); +INSERT INTO `yoshop_region` VALUES ('2472', '2468', '洪雅', '洪雅县', '中国,四川省,眉山市,洪雅县', '3', 'hongya', '028', '620360', 'H', '103.37313', '29.90661'); +INSERT INTO `yoshop_region` VALUES ('2473', '2468', '丹棱', '丹棱县', '中国,四川省,眉山市,丹棱县', '3', 'danling', '028', '620200', 'D', '103.51339', '30.01562'); +INSERT INTO `yoshop_region` VALUES ('2474', '2468', '青神', '青神县', '中国,四川省,眉山市,青神县', '3', 'qingshen', '028', '620460', 'Q', '103.84771', '29.83235'); +INSERT INTO `yoshop_region` VALUES ('2475', '2367', '宜宾', '宜宾市', '中国,四川省,宜宾市', '2', 'yibin', '0831', '644000', 'Y', '104.630825', '28.760189'); +INSERT INTO `yoshop_region` VALUES ('2476', '2475', '翠屏', '翠屏区', '中国,四川省,宜宾市,翠屏区', '3', 'cuiping', '0831', '644000', 'C', '104.61978', '28.76566'); +INSERT INTO `yoshop_region` VALUES ('2477', '2475', '南溪', '南溪区', '中国,四川省,宜宾市,南溪区', '3', 'nanxi', '0831', '644100', 'N', '104.981133', '28.839806'); +INSERT INTO `yoshop_region` VALUES ('2478', '2475', '宜宾', '宜宾县', '中国,四川省,宜宾市,宜宾县', '3', 'yibin', '0831', '644600', 'Y', '104.53314', '28.68996'); +INSERT INTO `yoshop_region` VALUES ('2479', '2475', '江安', '江安县', '中国,四川省,宜宾市,江安县', '3', 'jiang\'an', '0831', '644200', 'J', '105.06683', '28.72385'); +INSERT INTO `yoshop_region` VALUES ('2480', '2475', '长宁', '长宁县', '中国,四川省,宜宾市,长宁县', '3', 'changning', '0831', '644300', 'C', '104.9252', '28.57777'); +INSERT INTO `yoshop_region` VALUES ('2481', '2475', '高县', '高县', '中国,四川省,宜宾市,高县', '3', 'gaoxian', '0831', '645150', 'G', '104.51754', '28.43619'); +INSERT INTO `yoshop_region` VALUES ('2482', '2475', '珙县', '珙县', '中国,四川省,宜宾市,珙县', '3', 'gongxian', '0831', '644500', null, '104.71398', '28.44512'); +INSERT INTO `yoshop_region` VALUES ('2483', '2475', '筠连', '筠连县', '中国,四川省,宜宾市,筠连县', '3', 'junlian', '0831', '645250', null, '104.51217', '28.16495'); +INSERT INTO `yoshop_region` VALUES ('2484', '2475', '兴文', '兴文县', '中国,四川省,宜宾市,兴文县', '3', 'xingwen', '0831', '644400', 'X', '105.23675', '28.3044'); +INSERT INTO `yoshop_region` VALUES ('2485', '2475', '屏山', '屏山县', '中国,四川省,宜宾市,屏山县', '3', 'pingshan', '0831', '645350', 'P', '104.16293', '28.64369'); +INSERT INTO `yoshop_region` VALUES ('2486', '2367', '广安', '广安市', '中国,四川省,广安市', '2', 'guang\'an', '0826', '638000', 'G', '106.633369', '30.456398'); +INSERT INTO `yoshop_region` VALUES ('2487', '2486', '广安', '广安区', '中国,四川省,广安市,广安区', '3', 'guang\'an', '0826', '638000', 'G', '106.64163', '30.47389'); +INSERT INTO `yoshop_region` VALUES ('2488', '2486', '前锋', '前锋区', '中国,四川省,广安市,前锋区', '3', 'qianfeng', '0826', '638019', 'Q', '106.893537', '30.494572'); +INSERT INTO `yoshop_region` VALUES ('2489', '2486', '岳池', '岳池县', '中国,四川省,广安市,岳池县', '3', 'yuechi', '0826', '638300', 'Y', '106.44079', '30.53918'); +INSERT INTO `yoshop_region` VALUES ('2490', '2486', '武胜', '武胜县', '中国,四川省,广安市,武胜县', '3', 'wusheng', '0826', '638400', 'W', '106.29592', '30.34932'); +INSERT INTO `yoshop_region` VALUES ('2491', '2486', '邻水', '邻水县', '中国,四川省,广安市,邻水县', '3', 'linshui', '0826', '638500', 'L', '106.92968', '30.33449'); +INSERT INTO `yoshop_region` VALUES ('2492', '2486', '华蓥', '华蓥市', '中国,四川省,广安市,华蓥市', '3', 'huaying', '0826', '638600', 'H', '106.78466', '30.39007'); +INSERT INTO `yoshop_region` VALUES ('2493', '2367', '达州', '达州市', '中国,四川省,达州市', '2', 'dazhou', '0818', '635000', 'D', '107.502262', '31.209484'); +INSERT INTO `yoshop_region` VALUES ('2494', '2493', '通川', '通川区', '中国,四川省,达州市,通川区', '3', 'tongchuan', '0818', '635000', 'T', '107.50456', '31.21469'); +INSERT INTO `yoshop_region` VALUES ('2495', '2493', '达川', '达川区', '中国,四川省,达州市,达川区', '3', 'dachuan', '0818', '635000', 'D', '107.502262', '31.209484'); +INSERT INTO `yoshop_region` VALUES ('2496', '2493', '宣汉', '宣汉县', '中国,四川省,达州市,宣汉县', '3', 'xuanhan', '0818', '636150', 'X', '107.72775', '31.35516'); +INSERT INTO `yoshop_region` VALUES ('2497', '2493', '开江', '开江县', '中国,四川省,达州市,开江县', '3', 'kaijiang', '0818', '636250', 'K', '107.86889', '31.0841'); +INSERT INTO `yoshop_region` VALUES ('2498', '2493', '大竹', '大竹县', '中国,四川省,达州市,大竹县', '3', 'dazhu', '0818', '635100', 'D', '107.20855', '30.74147'); +INSERT INTO `yoshop_region` VALUES ('2499', '2493', '渠县', '渠县', '中国,四川省,达州市,渠县', '3', 'quxian', '0818', '635200', 'Q', '106.97381', '30.8376'); +INSERT INTO `yoshop_region` VALUES ('2500', '2493', '万源', '万源市', '中国,四川省,达州市,万源市', '3', 'wanyuan', '0818', '636350', 'W', '108.03598', '32.08091'); +INSERT INTO `yoshop_region` VALUES ('2501', '2367', '雅安', '雅安市', '中国,四川省,雅安市', '2', 'ya\'an', '0835', '625000', 'Y', '103.001033', '29.987722'); +INSERT INTO `yoshop_region` VALUES ('2502', '2501', '雨城', '雨城区', '中国,四川省,雅安市,雨城区', '3', 'yucheng', '0835', '625000', 'Y', '103.03305', '30.00531'); +INSERT INTO `yoshop_region` VALUES ('2503', '2501', '名山', '名山区', '中国,四川省,雅安市,名山区', '3', 'mingshan', '0835', '625100', 'M', '103.112214', '30.084718'); +INSERT INTO `yoshop_region` VALUES ('2504', '2501', '荥经', '荥经县', '中国,四川省,雅安市,荥经县', '3', 'yingjing', '0835', '625200', null, '102.84652', '29.79402'); +INSERT INTO `yoshop_region` VALUES ('2505', '2501', '汉源', '汉源县', '中国,四川省,雅安市,汉源县', '3', 'hanyuan', '0835', '625300', 'H', '102.6784', '29.35168'); +INSERT INTO `yoshop_region` VALUES ('2506', '2501', '石棉', '石棉县', '中国,四川省,雅安市,石棉县', '3', 'shimian', '0835', '625400', 'S', '102.35943', '29.22796'); +INSERT INTO `yoshop_region` VALUES ('2507', '2501', '天全', '天全县', '中国,四川省,雅安市,天全县', '3', 'tianquan', '0835', '625500', 'T', '102.75906', '30.06754'); +INSERT INTO `yoshop_region` VALUES ('2508', '2501', '芦山', '芦山县', '中国,四川省,雅安市,芦山县', '3', 'lushan', '0835', '625600', 'L', '102.92791', '30.14369'); +INSERT INTO `yoshop_region` VALUES ('2509', '2501', '宝兴', '宝兴县', '中国,四川省,雅安市,宝兴县', '3', 'baoxing', '0835', '625700', 'B', '102.81555', '30.36836'); +INSERT INTO `yoshop_region` VALUES ('2510', '2367', '巴中', '巴中市', '中国,四川省,巴中市', '2', 'bazhong', '0827', '636000', 'B', '106.753669', '31.858809'); +INSERT INTO `yoshop_region` VALUES ('2511', '2510', '巴州', '巴州区', '中国,四川省,巴中市,巴州区', '3', 'bazhou', '0827', '636001', 'B', '106.76889', '31.85125'); +INSERT INTO `yoshop_region` VALUES ('2512', '2510', '恩阳', '恩阳区', '中国,四川省,巴中市,恩阳区', '3', 'enyang', '0827', '636064', 'E', '106.753669', '31.858809'); +INSERT INTO `yoshop_region` VALUES ('2513', '2510', '通江', '通江县', '中国,四川省,巴中市,通江县', '3', 'tongjiang', '0827', '636700', 'T', '107.24398', '31.91294'); +INSERT INTO `yoshop_region` VALUES ('2514', '2510', '南江', '南江县', '中国,四川省,巴中市,南江县', '3', 'nanjiang', '0827', '636600', 'N', '106.84164', '32.35335'); +INSERT INTO `yoshop_region` VALUES ('2515', '2510', '平昌', '平昌县', '中国,四川省,巴中市,平昌县', '3', 'pingchang', '0827', '636400', 'P', '107.10424', '31.5594'); +INSERT INTO `yoshop_region` VALUES ('2516', '2367', '资阳', '资阳市', '中国,四川省,资阳市', '2', 'ziyang', '028', '641300', 'Z', '104.641917', '30.122211'); +INSERT INTO `yoshop_region` VALUES ('2517', '2516', '雁江', '雁江区', '中国,四川省,资阳市,雁江区', '3', 'yanjiang', '028', '641300', 'Y', '104.65216', '30.11525'); +INSERT INTO `yoshop_region` VALUES ('2518', '2516', '安岳', '安岳县', '中国,四川省,资阳市,安岳县', '3', 'anyue', '028', '642350', 'A', '105.3363', '30.09786'); +INSERT INTO `yoshop_region` VALUES ('2519', '2516', '乐至', '乐至县', '中国,四川省,资阳市,乐至县', '3', 'lezhi', '028', '641500', 'L', '105.03207', '30.27227'); +INSERT INTO `yoshop_region` VALUES ('2520', '2516', '简阳', '简阳市', '中国,四川省,资阳市,简阳市', '3', 'jianyang', '028', '641400', 'J', '104.54864', '30.3904'); +INSERT INTO `yoshop_region` VALUES ('2521', '2367', '阿坝', '阿坝藏族羌族自治州', '中国,四川省,阿坝藏族羌族自治州', '2', 'aba', '0837', '624000', 'A', '102.221374', '31.899792'); +INSERT INTO `yoshop_region` VALUES ('2522', '2521', '汶川', '汶川县', '中国,四川省,阿坝藏族羌族自治州,汶川县', '3', 'wenchuan', '0837', '623000', null, '103.59079', '31.47326'); +INSERT INTO `yoshop_region` VALUES ('2523', '2521', '理县', '理县', '中国,四川省,阿坝藏族羌族自治州,理县', '3', 'lixian', '0837', '623100', 'L', '103.17175', '31.43603'); +INSERT INTO `yoshop_region` VALUES ('2524', '2521', '茂县', '茂县', '中国,四川省,阿坝藏族羌族自治州,茂县', '3', 'maoxian', '0837', '623200', 'M', '103.85372', '31.682'); +INSERT INTO `yoshop_region` VALUES ('2525', '2521', '松潘', '松潘县', '中国,四川省,阿坝藏族羌族自治州,松潘县', '3', 'songpan', '0837', '623300', 'S', '103.59924', '32.63871'); +INSERT INTO `yoshop_region` VALUES ('2526', '2521', '九寨沟', '九寨沟县', '中国,四川省,阿坝藏族羌族自治州,九寨沟县', '3', 'jiuzhaigou', '0837', '623400', 'J', '104.23672', '33.26318'); +INSERT INTO `yoshop_region` VALUES ('2527', '2521', '金川', '金川县', '中国,四川省,阿坝藏族羌族自治州,金川县', '3', 'jinchuan', '0837', '624100', 'J', '102.06555', '31.47623'); +INSERT INTO `yoshop_region` VALUES ('2528', '2521', '小金', '小金县', '中国,四川省,阿坝藏族羌族自治州,小金县', '3', 'xiaojin', '0837', '624200', 'X', '102.36499', '30.99934'); +INSERT INTO `yoshop_region` VALUES ('2529', '2521', '黑水', '黑水县', '中国,四川省,阿坝藏族羌族自治州,黑水县', '3', 'heishui', '0837', '623500', 'H', '102.99176', '32.06184'); +INSERT INTO `yoshop_region` VALUES ('2530', '2521', '马尔康', '马尔康县', '中国,四川省,阿坝藏族羌族自治州,马尔康县', '3', 'maerkang', '0837', '624000', 'M', '102.20625', '31.90584'); +INSERT INTO `yoshop_region` VALUES ('2531', '2521', '壤塘', '壤塘县', '中国,四川省,阿坝藏族羌族自治州,壤塘县', '3', 'rangtang', '0837', '624300', 'R', '100.9783', '32.26578'); +INSERT INTO `yoshop_region` VALUES ('2532', '2521', '阿坝', '阿坝县', '中国,四川省,阿坝藏族羌族自治州,阿坝县', '3', 'aba', '0837', '624600', 'A', '101.70632', '32.90301'); +INSERT INTO `yoshop_region` VALUES ('2533', '2521', '若尔盖', '若尔盖县', '中国,四川省,阿坝藏族羌族自治州,若尔盖县', '3', 'ruoergai', '0837', '624500', 'R', '102.9598', '33.57432'); +INSERT INTO `yoshop_region` VALUES ('2534', '2521', '红原', '红原县', '中国,四川省,阿坝藏族羌族自治州,红原县', '3', 'hongyuan', '0837', '624400', 'H', '102.54525', '32.78989'); +INSERT INTO `yoshop_region` VALUES ('2535', '2367', '甘孜', '甘孜藏族自治州', '中国,四川省,甘孜藏族自治州', '2', 'garze', '0836', '626000', 'G', '101.963815', '30.050663'); +INSERT INTO `yoshop_region` VALUES ('2536', '2535', '康定', '康定县', '中国,四川省,甘孜藏族自治州,康定县', '3', 'kangding', '0836', '626000', 'K', '101.96487', '30.05532'); +INSERT INTO `yoshop_region` VALUES ('2537', '2535', '泸定', '泸定县', '中国,四川省,甘孜藏族自治州,泸定县', '3', 'luding', '0836', '626100', null, '102.23507', '29.91475'); +INSERT INTO `yoshop_region` VALUES ('2538', '2535', '丹巴', '丹巴县', '中国,四川省,甘孜藏族自治州,丹巴县', '3', 'danba', '0836', '626300', 'D', '101.88424', '30.87656'); +INSERT INTO `yoshop_region` VALUES ('2539', '2535', '九龙', '九龙县', '中国,四川省,甘孜藏族自治州,九龙县', '3', 'jiulong', '0836', '626200', 'J', '101.50848', '29.00091'); +INSERT INTO `yoshop_region` VALUES ('2540', '2535', '雅江', '雅江县', '中国,四川省,甘孜藏族自治州,雅江县', '3', 'yajiang', '0836', '627450', 'Y', '101.01492', '30.03281'); +INSERT INTO `yoshop_region` VALUES ('2541', '2535', '道孚', '道孚县', '中国,四川省,甘孜藏族自治州,道孚县', '3', 'daofu', '0836', '626400', 'D', '101.12554', '30.98046'); +INSERT INTO `yoshop_region` VALUES ('2542', '2535', '炉霍', '炉霍县', '中国,四川省,甘孜藏族自治州,炉霍县', '3', 'luhuo', '0836', '626500', 'L', '100.67681', '31.3917'); +INSERT INTO `yoshop_region` VALUES ('2543', '2535', '甘孜', '甘孜县', '中国,四川省,甘孜藏族自治州,甘孜县', '3', 'ganzi', '0836', '626700', 'G', '99.99307', '31.62672'); +INSERT INTO `yoshop_region` VALUES ('2544', '2535', '新龙', '新龙县', '中国,四川省,甘孜藏族自治州,新龙县', '3', 'xinlong', '0836', '626800', 'X', '100.3125', '30.94067'); +INSERT INTO `yoshop_region` VALUES ('2545', '2535', '德格', '德格县', '中国,四川省,甘孜藏族自治州,德格县', '3', 'dege', '0836', '627250', 'D', '98.58078', '31.80615'); +INSERT INTO `yoshop_region` VALUES ('2546', '2535', '白玉', '白玉县', '中国,四川省,甘孜藏族自治州,白玉县', '3', 'baiyu', '0836', '627150', 'B', '98.82568', '31.20902'); +INSERT INTO `yoshop_region` VALUES ('2547', '2535', '石渠', '石渠县', '中国,四川省,甘孜藏族自治州,石渠县', '3', 'shiqu', '0836', '627350', 'S', '98.10156', '32.97884'); +INSERT INTO `yoshop_region` VALUES ('2548', '2535', '色达', '色达县', '中国,四川省,甘孜藏族自治州,色达县', '3', 'seda', '0836', '626600', 'S', '100.33224', '32.26839'); +INSERT INTO `yoshop_region` VALUES ('2549', '2535', '理塘', '理塘县', '中国,四川省,甘孜藏族自治州,理塘县', '3', 'litang', '0836', '627550', 'L', '100.27005', '29.99674'); +INSERT INTO `yoshop_region` VALUES ('2550', '2535', '巴塘', '巴塘县', '中国,四川省,甘孜藏族自治州,巴塘县', '3', 'batang', '0836', '627650', 'B', '99.10409', '30.00423'); +INSERT INTO `yoshop_region` VALUES ('2551', '2535', '乡城', '乡城县', '中国,四川省,甘孜藏族自治州,乡城县', '3', 'xiangcheng', '0836', '627850', 'X', '99.79943', '28.93554'); +INSERT INTO `yoshop_region` VALUES ('2552', '2535', '稻城', '稻城县', '中国,四川省,甘孜藏族自治州,稻城县', '3', 'daocheng', '0836', '627750', 'D', '100.29809', '29.0379'); +INSERT INTO `yoshop_region` VALUES ('2553', '2535', '得荣', '得荣县', '中国,四川省,甘孜藏族自治州,得荣县', '3', 'derong', '0836', '627950', 'D', '99.28628', '28.71297'); +INSERT INTO `yoshop_region` VALUES ('2554', '2367', '凉山', '凉山彝族自治州', '中国,四川省,凉山彝族自治州', '2', 'liangshan', '0834', '615000', 'L', '102.258746', '27.886762'); +INSERT INTO `yoshop_region` VALUES ('2555', '2554', '西昌', '西昌市', '中国,四川省,凉山彝族自治州,西昌市', '3', 'xichang', '0835', '615000', 'X', '102.26413', '27.89524'); +INSERT INTO `yoshop_region` VALUES ('2556', '2554', '木里', '木里藏族自治县', '中国,四川省,凉山彝族自治州,木里藏族自治县', '3', 'muli', '0851', '615800', 'M', '101.2796', '27.92875'); +INSERT INTO `yoshop_region` VALUES ('2557', '2554', '盐源', '盐源县', '中国,四川省,凉山彝族自治州,盐源县', '3', 'yanyuan', '0836', '615700', 'Y', '101.5097', '27.42177'); +INSERT INTO `yoshop_region` VALUES ('2558', '2554', '德昌', '德昌县', '中国,四川省,凉山彝族自治州,德昌县', '3', 'dechang', '0837', '615500', 'D', '102.18017', '27.40482'); +INSERT INTO `yoshop_region` VALUES ('2559', '2554', '会理', '会理县', '中国,四川省,凉山彝族自治州,会理县', '3', 'huili', '0838', '615100', 'H', '102.24539', '26.65627'); +INSERT INTO `yoshop_region` VALUES ('2560', '2554', '会东', '会东县', '中国,四川省,凉山彝族自治州,会东县', '3', 'huidong', '0839', '615200', 'H', '102.57815', '26.63429'); +INSERT INTO `yoshop_region` VALUES ('2561', '2554', '宁南', '宁南县', '中国,四川省,凉山彝族自治州,宁南县', '3', 'ningnan', '0840', '615400', 'N', '102.76116', '27.06567'); +INSERT INTO `yoshop_region` VALUES ('2562', '2554', '普格', '普格县', '中国,四川省,凉山彝族自治州,普格县', '3', 'puge', '0841', '615300', 'P', '102.54055', '27.37485'); +INSERT INTO `yoshop_region` VALUES ('2563', '2554', '布拖', '布拖县', '中国,四川省,凉山彝族自治州,布拖县', '3', 'butuo', '0842', '616350', 'B', '102.81234', '27.7079'); +INSERT INTO `yoshop_region` VALUES ('2564', '2554', '金阳', '金阳县', '中国,四川省,凉山彝族自治州,金阳县', '3', 'jinyang', '0843', '616250', 'J', '103.24774', '27.69698'); +INSERT INTO `yoshop_region` VALUES ('2565', '2554', '昭觉', '昭觉县', '中国,四川省,凉山彝族自治州,昭觉县', '3', 'zhaojue', '0844', '616150', 'Z', '102.84661', '28.01155'); +INSERT INTO `yoshop_region` VALUES ('2566', '2554', '喜德', '喜德县', '中国,四川省,凉山彝族自治州,喜德县', '3', 'xide', '0845', '616750', 'X', '102.41336', '28.30739'); +INSERT INTO `yoshop_region` VALUES ('2567', '2554', '冕宁', '冕宁县', '中国,四川省,凉山彝族自治州,冕宁县', '3', 'mianning', '0846', '615600', 'M', '102.17108', '28.55161'); +INSERT INTO `yoshop_region` VALUES ('2568', '2554', '越西', '越西县', '中国,四川省,凉山彝族自治州,越西县', '3', 'yuexi', '0847', '616650', 'Y', '102.5079', '28.64133'); +INSERT INTO `yoshop_region` VALUES ('2569', '2554', '甘洛', '甘洛县', '中国,四川省,凉山彝族自治州,甘洛县', '3', 'ganluo', '0848', '616850', 'G', '102.77154', '28.96624'); +INSERT INTO `yoshop_region` VALUES ('2570', '2554', '美姑', '美姑县', '中国,四川省,凉山彝族自治州,美姑县', '3', 'meigu', '0849', '616450', 'M', '103.13116', '28.32596'); +INSERT INTO `yoshop_region` VALUES ('2571', '2554', '雷波', '雷波县', '中国,四川省,凉山彝族自治州,雷波县', '3', 'leibo', '0850', '616550', 'L', '103.57287', '28.26407'); +INSERT INTO `yoshop_region` VALUES ('2572', '0', '贵州', '贵州省', '中国,贵州省', '1', 'guizhou', '', '', 'G', '106.713478', '26.578343'); +INSERT INTO `yoshop_region` VALUES ('2573', '2572', '贵阳', '贵阳市', '中国,贵州省,贵阳市', '2', 'guiyang', '0851', '550001', 'G', '106.713478', '26.578343'); +INSERT INTO `yoshop_region` VALUES ('2574', '2573', '南明', '南明区', '中国,贵州省,贵阳市,南明区', '3', 'nanming', '0851', '550001', 'N', '106.7145', '26.56819'); +INSERT INTO `yoshop_region` VALUES ('2575', '2573', '云岩', '云岩区', '中国,贵州省,贵阳市,云岩区', '3', 'yunyan', '0851', '550001', 'Y', '106.72485', '26.60484'); +INSERT INTO `yoshop_region` VALUES ('2576', '2573', '花溪', '花溪区', '中国,贵州省,贵阳市,花溪区', '3', 'huaxi', '0851', '550025', 'H', '106.67688', '26.43343'); +INSERT INTO `yoshop_region` VALUES ('2577', '2573', '乌当', '乌当区', '中国,贵州省,贵阳市,乌当区', '3', 'wudang', '0851', '550018', 'W', '106.7521', '26.6302'); +INSERT INTO `yoshop_region` VALUES ('2578', '2573', '白云', '白云区', '中国,贵州省,贵阳市,白云区', '3', 'baiyun', '0851', '550014', 'B', '106.63088', '26.68284'); +INSERT INTO `yoshop_region` VALUES ('2579', '2573', '观山湖', '观山湖区', '中国,贵州省,贵阳市,观山湖区', '3', 'guanshanhu', '0851', '550009', 'G', '106.625442', '26.618209'); +INSERT INTO `yoshop_region` VALUES ('2580', '2573', '开阳', '开阳县', '中国,贵州省,贵阳市,开阳县', '3', 'kaiyang', '0851', '550300', 'K', '106.9692', '27.05533'); +INSERT INTO `yoshop_region` VALUES ('2581', '2573', '息烽', '息烽县', '中国,贵州省,贵阳市,息烽县', '3', 'xifeng', '0851', '551100', 'X', '106.738', '27.09346'); +INSERT INTO `yoshop_region` VALUES ('2582', '2573', '修文', '修文县', '中国,贵州省,贵阳市,修文县', '3', 'xiuwen', '0851', '550200', 'X', '106.59487', '26.83783'); +INSERT INTO `yoshop_region` VALUES ('2583', '2573', '清镇', '清镇市', '中国,贵州省,贵阳市,清镇市', '3', 'qingzhen', '0851', '551400', 'Q', '106.46862', '26.55261'); +INSERT INTO `yoshop_region` VALUES ('2584', '2572', '六盘水', '六盘水市', '中国,贵州省,六盘水市', '2', 'liupanshui', '0858', '553400', 'L', '104.846743', '26.584643'); +INSERT INTO `yoshop_region` VALUES ('2585', '2584', '钟山', '钟山区', '中国,贵州省,六盘水市,钟山区', '3', 'zhongshan', '0858', '553000', 'Z', '104.87848', '26.57699'); +INSERT INTO `yoshop_region` VALUES ('2586', '2584', '六枝', '六枝特区', '中国,贵州省,六盘水市,六枝特区', '3', 'liuzhi', '0858', '553400', 'L', '105.48062', '26.20117'); +INSERT INTO `yoshop_region` VALUES ('2587', '2584', '水城', '水城县', '中国,贵州省,六盘水市,水城县', '3', 'shuicheng', '0858', '553000', 'S', '104.95764', '26.54785'); +INSERT INTO `yoshop_region` VALUES ('2588', '2584', '盘县', '盘县', '中国,贵州省,六盘水市,盘县', '3', 'panxian', '0858', '561601', 'P', '104.47061', '25.7136'); +INSERT INTO `yoshop_region` VALUES ('2589', '2572', '遵义', '遵义市', '中国,贵州省,遵义市', '2', 'zunyi', '0852', '563000', 'Z', '106.937265', '27.706626'); +INSERT INTO `yoshop_region` VALUES ('2590', '2589', '红花岗', '红花岗区', '中国,贵州省,遵义市,红花岗区', '3', 'honghuagang', '0852', '563000', 'H', '106.89404', '27.64471'); +INSERT INTO `yoshop_region` VALUES ('2591', '2589', '汇川', '汇川区', '中国,贵州省,遵义市,汇川区', '3', 'huichuan', '0852', '563000', 'H', '106.9393', '27.70625'); +INSERT INTO `yoshop_region` VALUES ('2592', '2589', '遵义', '遵义县', '中国,贵州省,遵义市,遵义县', '3', 'zunyi', '0852', '563100', 'Z', '106.83331', '27.53772'); +INSERT INTO `yoshop_region` VALUES ('2593', '2589', '桐梓', '桐梓县', '中国,贵州省,遵义市,桐梓县', '3', 'tongzi', '0852', '563200', 'T', '106.82568', '28.13806'); +INSERT INTO `yoshop_region` VALUES ('2594', '2589', '绥阳', '绥阳县', '中国,贵州省,遵义市,绥阳县', '3', 'suiyang', '0852', '563300', 'S', '107.19064', '27.94702'); +INSERT INTO `yoshop_region` VALUES ('2595', '2589', '正安', '正安县', '中国,贵州省,遵义市,正安县', '3', 'zheng\'an', '0852', '563400', 'Z', '107.44357', '28.5512'); +INSERT INTO `yoshop_region` VALUES ('2596', '2589', '道真', '道真仡佬族苗族自治县', '中国,贵州省,遵义市,道真仡佬族苗族自治县', '3', 'daozhen', '0852', '563500', 'D', '107.61152', '28.864'); +INSERT INTO `yoshop_region` VALUES ('2597', '2589', '务川', '务川仡佬族苗族自治县', '中国,贵州省,遵义市,务川仡佬族苗族自治县', '3', 'wuchuan', '0852', '564300', 'W', '107.88935', '28.52227'); +INSERT INTO `yoshop_region` VALUES ('2598', '2589', '凤冈', '凤冈县', '中国,贵州省,遵义市,凤冈县', '3', 'fenggang', '0852', '564200', 'F', '107.71682', '27.95461'); +INSERT INTO `yoshop_region` VALUES ('2599', '2589', '湄潭', '湄潭县', '中国,贵州省,遵义市,湄潭县', '3', 'meitan', '0852', '564100', null, '107.48779', '27.76676'); +INSERT INTO `yoshop_region` VALUES ('2600', '2589', '余庆', '余庆县', '中国,贵州省,遵义市,余庆县', '3', 'yuqing', '0852', '564400', 'Y', '107.88821', '27.22532'); +INSERT INTO `yoshop_region` VALUES ('2601', '2589', '习水', '习水县', '中国,贵州省,遵义市,习水县', '3', 'xishui', '0852', '564600', 'X', '106.21267', '28.31976'); +INSERT INTO `yoshop_region` VALUES ('2602', '2589', '赤水', '赤水市', '中国,贵州省,遵义市,赤水市', '3', 'chishui', '0852', '564700', 'C', '105.69845', '28.58921'); +INSERT INTO `yoshop_region` VALUES ('2603', '2589', '仁怀', '仁怀市', '中国,贵州省,遵义市,仁怀市', '3', 'renhuai', '0852', '564500', 'R', '106.40152', '27.79231'); +INSERT INTO `yoshop_region` VALUES ('2604', '2572', '安顺', '安顺市', '中国,贵州省,安顺市', '2', 'anshun', '0853', '561000', 'A', '105.932188', '26.245544'); +INSERT INTO `yoshop_region` VALUES ('2605', '2604', '西秀', '西秀区', '中国,贵州省,安顺市,西秀区', '3', 'xixiu', '0853', '561000', 'X', '105.96585', '26.24491'); +INSERT INTO `yoshop_region` VALUES ('2606', '2604', '平坝', '平坝区', '中国,贵州省,安顺市,平坝区', '3', 'pingba', '0853', '561100', 'P', '106.25683', '26.40543'); +INSERT INTO `yoshop_region` VALUES ('2607', '2604', '普定', '普定县', '中国,贵州省,安顺市,普定县', '3', 'puding', '0853', '562100', 'P', '105.74285', '26.30141'); +INSERT INTO `yoshop_region` VALUES ('2608', '2604', '镇宁', '镇宁布依族苗族自治县', '中国,贵州省,安顺市,镇宁布依族苗族自治县', '3', 'zhenning', '0853', '561200', 'Z', '105.76513', '26.05533'); +INSERT INTO `yoshop_region` VALUES ('2609', '2604', '关岭', '关岭布依族苗族自治县', '中国,贵州省,安顺市,关岭布依族苗族自治县', '3', 'guanling', '0853', '561300', 'G', '105.61883', '25.94248'); +INSERT INTO `yoshop_region` VALUES ('2610', '2604', '紫云', '紫云苗族布依族自治县', '中国,贵州省,安顺市,紫云苗族布依族自治县', '3', 'ziyun', '0853', '550800', 'Z', '106.08364', '25.75258'); +INSERT INTO `yoshop_region` VALUES ('2611', '2572', '毕节', '毕节市', '中国,贵州省,毕节市', '2', 'bijie', '0857', '551700', 'B', '105.28501', '27.301693'); +INSERT INTO `yoshop_region` VALUES ('2612', '2611', '七星关', '七星关区', '中国,贵州省,毕节市,七星关区', '3', 'qixingguan', '0857', '551700', 'Q', '104.9497', '27.153556'); +INSERT INTO `yoshop_region` VALUES ('2613', '2611', '大方', '大方县', '中国,贵州省,毕节市,大方县', '3', 'dafang', '0857', '551600', 'D', '105.609254', '27.143521'); +INSERT INTO `yoshop_region` VALUES ('2614', '2611', '黔西', '黔西县', '中国,贵州省,毕节市,黔西县', '3', 'qianxi', '0857', '551500', 'Q', '106.038299', '27.024923'); +INSERT INTO `yoshop_region` VALUES ('2615', '2611', '金沙', '金沙县', '中国,贵州省,毕节市,金沙县', '3', 'jinsha', '0857', '551800', 'J', '106.222103', '27.459693'); +INSERT INTO `yoshop_region` VALUES ('2616', '2611', '织金', '织金县', '中国,贵州省,毕节市,织金县', '3', 'zhijin', '0857', '552100', 'Z', '105.768997', '26.668497'); +INSERT INTO `yoshop_region` VALUES ('2617', '2611', '纳雍', '纳雍县', '中国,贵州省,毕节市,纳雍县', '3', 'nayong', '0857', '553300', 'N', '105.375322', '26.769875'); +INSERT INTO `yoshop_region` VALUES ('2618', '2611', '威宁', '威宁彝族回族苗族自治县', '中国,贵州省,毕节市,威宁彝族回族苗族自治县', '3', 'weining', '0857', '553100', 'W', '104.286523', '26.859099'); +INSERT INTO `yoshop_region` VALUES ('2619', '2611', '赫章', '赫章县', '中国,贵州省,毕节市,赫章县', '3', 'hezhang', '0857', '553200', 'H', '104.726438', '27.119243'); +INSERT INTO `yoshop_region` VALUES ('2620', '2572', '铜仁', '铜仁市', '中国,贵州省,铜仁市', '2', 'tongren', '0856', '554300', 'T', '109.191555', '27.718346'); +INSERT INTO `yoshop_region` VALUES ('2621', '2620', '碧江', '碧江区', '中国,贵州省,铜仁市,碧江区', '3', 'bijiang', '0856', '554300', 'B', '109.191555', '27.718346'); +INSERT INTO `yoshop_region` VALUES ('2622', '2620', '万山', '万山区', '中国,贵州省,铜仁市,万山区', '3', 'wanshan', '0856', '554200', 'W', '109.21199', '27.51903'); +INSERT INTO `yoshop_region` VALUES ('2623', '2620', '江口', '江口县', '中国,贵州省,铜仁市,江口县', '3', 'jiangkou', '0856', '554400', 'J', '108.848427', '27.691904'); +INSERT INTO `yoshop_region` VALUES ('2624', '2620', '玉屏', '玉屏侗族自治县', '中国,贵州省,铜仁市,玉屏侗族自治县', '3', 'yuping', '0856', '554004', 'Y', '108.917882', '27.238024'); +INSERT INTO `yoshop_region` VALUES ('2625', '2620', '石阡', '石阡县', '中国,贵州省,铜仁市,石阡县', '3', 'shiqian', '0856', '555100', 'S', '108.229854', '27.519386'); +INSERT INTO `yoshop_region` VALUES ('2626', '2620', '思南', '思南县', '中国,贵州省,铜仁市,思南县', '3', 'sinan', '0856', '565100', 'S', '108.255827', '27.941331'); +INSERT INTO `yoshop_region` VALUES ('2627', '2620', '印江', '印江土家族苗族自治县', '中国,贵州省,铜仁市,印江土家族苗族自治县', '3', 'yinjiang', '0856', '555200', 'Y', '108.405517', '27.997976'); +INSERT INTO `yoshop_region` VALUES ('2628', '2620', '德江', '德江县', '中国,贵州省,铜仁市,德江县', '3', 'dejiang', '0856', '565200', 'D', '108.117317', '28.26094'); +INSERT INTO `yoshop_region` VALUES ('2629', '2620', '沿河', '沿河土家族自治县', '中国,贵州省,铜仁市,沿河土家族自治县', '3', 'yuanhe', '0856', '565300', 'Y', '108.495746', '28.560487'); +INSERT INTO `yoshop_region` VALUES ('2630', '2620', '松桃', '松桃苗族自治县', '中国,贵州省,铜仁市,松桃苗族自治县', '3', 'songtao', '0856', '554100', 'S', '109.202627', '28.165419'); +INSERT INTO `yoshop_region` VALUES ('2631', '2572', '黔西南', '黔西南布依族苗族自治州', '中国,贵州省,黔西南布依族苗族自治州', '2', 'qianxinan', '0859', '562400', 'Q', '104.897971', '25.08812'); +INSERT INTO `yoshop_region` VALUES ('2632', '2631', '兴义', '兴义市 ', '中国,贵州省,黔西南布依族苗族自治州,兴义市 ', '3', 'xingyi', '0859', '562400', 'X', '104.89548', '25.09205'); +INSERT INTO `yoshop_region` VALUES ('2633', '2631', '兴仁', '兴仁县', '中国,贵州省,黔西南布依族苗族自治州,兴仁县', '3', 'xingren', '0859', '562300', 'X', '105.18652', '25.43282'); +INSERT INTO `yoshop_region` VALUES ('2634', '2631', '普安', '普安县', '中国,贵州省,黔西南布依族苗族自治州,普安县', '3', 'pu\'an', '0859', '561500', 'P', '104.95529', '25.78603'); +INSERT INTO `yoshop_region` VALUES ('2635', '2631', '晴隆', '晴隆县', '中国,贵州省,黔西南布依族苗族自治州,晴隆县', '3', 'qinglong', '0859', '561400', 'Q', '105.2192', '25.83522'); +INSERT INTO `yoshop_region` VALUES ('2636', '2631', '贞丰', '贞丰县', '中国,贵州省,黔西南布依族苗族自治州,贞丰县', '3', 'zhenfeng', '0859', '562200', 'Z', '105.65454', '25.38464'); +INSERT INTO `yoshop_region` VALUES ('2637', '2631', '望谟', '望谟县', '中国,贵州省,黔西南布依族苗族自治州,望谟县', '3', 'wangmo', '0859', '552300', 'W', '106.09957', '25.17822'); +INSERT INTO `yoshop_region` VALUES ('2638', '2631', '册亨', '册亨县', '中国,贵州省,黔西南布依族苗族自治州,册亨县', '3', 'ceheng', '0859', '552200', 'C', '105.8124', '24.98376'); +INSERT INTO `yoshop_region` VALUES ('2639', '2631', '安龙', '安龙县', '中国,贵州省,黔西南布依族苗族自治州,安龙县', '3', 'anlong', '0859', '552400', 'A', '105.44268', '25.09818'); +INSERT INTO `yoshop_region` VALUES ('2640', '2572', '黔东南', '黔东南苗族侗族自治州', '中国,贵州省,黔东南苗族侗族自治州', '2', 'qiandongnan', '0855', '556000', 'Q', '107.977488', '26.583352'); +INSERT INTO `yoshop_region` VALUES ('2641', '2640', '凯里', '凯里市', '中国,贵州省,黔东南苗族侗族自治州,凯里市', '3', 'kaili', '0855', '556000', 'K', '107.98132', '26.56689'); +INSERT INTO `yoshop_region` VALUES ('2642', '2640', '黄平', '黄平县', '中国,贵州省,黔东南苗族侗族自治州,黄平县', '3', 'huangping', '0855', '556100', 'H', '107.90179', '26.89573'); +INSERT INTO `yoshop_region` VALUES ('2643', '2640', '施秉', '施秉县', '中国,贵州省,黔东南苗族侗族自治州,施秉县', '3', 'shibing', '0855', '556200', 'S', '108.12597', '27.03495'); +INSERT INTO `yoshop_region` VALUES ('2644', '2640', '三穗', '三穗县', '中国,贵州省,黔东南苗族侗族自治州,三穗县', '3', 'sansui', '0855', '556500', 'S', '108.67132', '26.94765'); +INSERT INTO `yoshop_region` VALUES ('2645', '2640', '镇远', '镇远县', '中国,贵州省,黔东南苗族侗族自治州,镇远县', '3', 'zhenyuan', '0855', '557700', 'Z', '108.42721', '27.04933'); +INSERT INTO `yoshop_region` VALUES ('2646', '2640', '岑巩', '岑巩县', '中国,贵州省,黔东南苗族侗族自治州,岑巩县', '3', 'cengong', '0855', '557800', null, '108.81884', '27.17539'); +INSERT INTO `yoshop_region` VALUES ('2647', '2640', '天柱', '天柱县', '中国,贵州省,黔东南苗族侗族自治州,天柱县', '3', 'tianzhu', '0855', '556600', 'T', '109.20718', '26.90781'); +INSERT INTO `yoshop_region` VALUES ('2648', '2640', '锦屏', '锦屏县', '中国,贵州省,黔东南苗族侗族自治州,锦屏县', '3', 'jinping', '0855', '556700', 'J', '109.19982', '26.67635'); +INSERT INTO `yoshop_region` VALUES ('2649', '2640', '剑河', '剑河县', '中国,贵州省,黔东南苗族侗族自治州,剑河县', '3', 'jianhe', '0855', '556400', 'J', '108.5913', '26.6525'); +INSERT INTO `yoshop_region` VALUES ('2650', '2640', '台江', '台江县', '中国,贵州省,黔东南苗族侗族自治州,台江县', '3', 'taijiang', '0855', '556300', 'T', '108.31814', '26.66916'); +INSERT INTO `yoshop_region` VALUES ('2651', '2640', '黎平', '黎平县', '中国,贵州省,黔东南苗族侗族自治州,黎平县', '3', 'liping', '0855', '557300', 'L', '109.13607', '26.23114'); +INSERT INTO `yoshop_region` VALUES ('2652', '2640', '榕江', '榕江县', '中国,贵州省,黔东南苗族侗族自治州,榕江县', '3', 'rongjiang', '0855', '557200', null, '108.52072', '25.92421'); +INSERT INTO `yoshop_region` VALUES ('2653', '2640', '从江', '从江县', '中国,贵州省,黔东南苗族侗族自治州,从江县', '3', 'congjiang', '0855', '557400', 'C', '108.90527', '25.75415'); +INSERT INTO `yoshop_region` VALUES ('2654', '2640', '雷山', '雷山县', '中国,贵州省,黔东南苗族侗族自治州,雷山县', '3', 'leishan', '0855', '557100', 'L', '108.07745', '26.38385'); +INSERT INTO `yoshop_region` VALUES ('2655', '2640', '麻江', '麻江县', '中国,贵州省,黔东南苗族侗族自治州,麻江县', '3', 'majiang', '0855', '557600', 'M', '107.59155', '26.49235'); +INSERT INTO `yoshop_region` VALUES ('2656', '2640', '丹寨', '丹寨县', '中国,贵州省,黔东南苗族侗族自治州,丹寨县', '3', 'danzhai', '0855', '557500', 'D', '107.79718', '26.19816'); +INSERT INTO `yoshop_region` VALUES ('2657', '2572', '黔南', '黔南布依族苗族自治州', '中国,贵州省,黔南布依族苗族自治州', '2', 'qiannan', '0854', '558000', 'Q', '107.517156', '26.258219'); +INSERT INTO `yoshop_region` VALUES ('2658', '2657', '都匀', '都匀市', '中国,贵州省,黔南布依族苗族自治州,都匀市', '3', 'duyun', '0854', '558000', 'D', '107.51872', '26.2594'); +INSERT INTO `yoshop_region` VALUES ('2659', '2657', '福泉', '福泉市', '中国,贵州省,黔南布依族苗族自治州,福泉市', '3', 'fuquan', '0854', '550500', 'F', '107.51715', '26.67989'); +INSERT INTO `yoshop_region` VALUES ('2660', '2657', '荔波', '荔波县', '中国,贵州省,黔南布依族苗族自治州,荔波县', '3', 'libo', '0854', '558400', 'L', '107.88592', '25.4139'); +INSERT INTO `yoshop_region` VALUES ('2661', '2657', '贵定', '贵定县', '中国,贵州省,黔南布依族苗族自治州,贵定县', '3', 'guiding', '0854', '551300', 'G', '107.23654', '26.57812'); +INSERT INTO `yoshop_region` VALUES ('2662', '2657', '瓮安', '瓮安县', '中国,贵州省,黔南布依族苗族自治州,瓮安县', '3', 'weng\'an', '0854', '550400', 'W', '107.4757', '27.06813'); +INSERT INTO `yoshop_region` VALUES ('2663', '2657', '独山', '独山县', '中国,贵州省,黔南布依族苗族自治州,独山县', '3', 'dushan', '0854', '558200', 'D', '107.54101', '25.8245'); +INSERT INTO `yoshop_region` VALUES ('2664', '2657', '平塘', '平塘县', '中国,贵州省,黔南布依族苗族自治州,平塘县', '3', 'pingtang', '0854', '558300', 'P', '107.32428', '25.83294'); +INSERT INTO `yoshop_region` VALUES ('2665', '2657', '罗甸', '罗甸县', '中国,贵州省,黔南布依族苗族自治州,罗甸县', '3', 'luodian', '0854', '550100', 'L', '106.75186', '25.42586'); +INSERT INTO `yoshop_region` VALUES ('2666', '2657', '长顺', '长顺县', '中国,贵州省,黔南布依族苗族自治州,长顺县', '3', 'changshun', '0854', '550700', 'C', '106.45217', '26.02299'); +INSERT INTO `yoshop_region` VALUES ('2667', '2657', '龙里', '龙里县', '中国,贵州省,黔南布依族苗族自治州,龙里县', '3', 'longli', '0854', '551200', 'L', '106.97662', '26.45076'); +INSERT INTO `yoshop_region` VALUES ('2668', '2657', '惠水', '惠水县', '中国,贵州省,黔南布依族苗族自治州,惠水县', '3', 'huishui', '0854', '550600', 'H', '106.65911', '26.13389'); +INSERT INTO `yoshop_region` VALUES ('2669', '2657', '三都', '三都水族自治县', '中国,贵州省,黔南布依族苗族自治州,三都水族自治县', '3', 'sandu', '0854', '558100', 'S', '107.87464', '25.98562'); +INSERT INTO `yoshop_region` VALUES ('2670', '0', '云南', '云南省', '中国,云南省', '1', 'yunnan', '', '', 'Y', '102.712251', '25.040609'); +INSERT INTO `yoshop_region` VALUES ('2671', '2670', '昆明', '昆明市', '中国,云南省,昆明市', '2', 'kunming', '0871', '650500', 'K', '102.712251', '25.040609'); +INSERT INTO `yoshop_region` VALUES ('2672', '2671', '五华', '五华区', '中国,云南省,昆明市,五华区', '3', 'wuhua', '0871', '650021', 'W', '102.70786', '25.03521'); +INSERT INTO `yoshop_region` VALUES ('2673', '2671', '盘龙', '盘龙区', '中国,云南省,昆明市,盘龙区', '3', 'panlong', '0871', '650051', 'P', '102.71994', '25.04053'); +INSERT INTO `yoshop_region` VALUES ('2674', '2671', '官渡', '官渡区', '中国,云南省,昆明市,官渡区', '3', 'guandu', '0871', '650200', 'G', '102.74362', '25.01497'); +INSERT INTO `yoshop_region` VALUES ('2675', '2671', '西山', '西山区', '中国,云南省,昆明市,西山区', '3', 'xishan', '0871', '650118', 'X', '102.66464', '25.03796'); +INSERT INTO `yoshop_region` VALUES ('2676', '2671', '东川', '东川区', '中国,云南省,昆明市,东川区', '3', 'dongchuan', '0871', '654100', 'D', '103.18832', '26.083'); +INSERT INTO `yoshop_region` VALUES ('2677', '2671', '呈贡', '呈贡区', '中国,云南省,昆明市,呈贡区', '3', 'chenggong', '0871', '650500', 'C', '102.801382', '24.889275'); +INSERT INTO `yoshop_region` VALUES ('2678', '2671', '晋宁', '晋宁县', '中国,云南省,昆明市,晋宁县', '3', 'jinning', '0871', '650600', 'J', '102.59393', '24.6665'); +INSERT INTO `yoshop_region` VALUES ('2679', '2671', '富民', '富民县', '中国,云南省,昆明市,富民县', '3', 'fumin', '0871', '650400', 'F', '102.4985', '25.22119'); +INSERT INTO `yoshop_region` VALUES ('2680', '2671', '宜良', '宜良县', '中国,云南省,昆明市,宜良县', '3', 'yiliang', '0871', '652100', 'Y', '103.14117', '24.91705'); +INSERT INTO `yoshop_region` VALUES ('2681', '2671', '石林', '石林彝族自治县', '中国,云南省,昆明市,石林彝族自治县', '3', 'shilin', '0871', '652200', 'S', '103.27148', '24.75897'); +INSERT INTO `yoshop_region` VALUES ('2682', '2671', '嵩明', '嵩明县', '中国,云南省,昆明市,嵩明县', '3', 'songming', '0871', '651700', null, '103.03729', '25.33986'); +INSERT INTO `yoshop_region` VALUES ('2683', '2671', '禄劝', '禄劝彝族苗族自治县', '中国,云南省,昆明市,禄劝彝族苗族自治县', '3', 'luquan', '0871', '651500', 'L', '102.4671', '25.55387'); +INSERT INTO `yoshop_region` VALUES ('2684', '2671', '寻甸', '寻甸回族彝族自治县 ', '中国,云南省,昆明市,寻甸回族彝族自治县 ', '3', 'xundian', '0871', '655200', 'X', '103.2557', '25.55961'); +INSERT INTO `yoshop_region` VALUES ('2685', '2671', '安宁', '安宁市', '中国,云南省,昆明市,安宁市', '3', 'anning', '0871', '650300', 'A', '102.46972', '24.91652'); +INSERT INTO `yoshop_region` VALUES ('2686', '2670', '曲靖', '曲靖市', '中国,云南省,曲靖市', '2', 'qujing', '0874', '655000', 'Q', '103.797851', '25.501557'); +INSERT INTO `yoshop_region` VALUES ('2687', '2686', '麒麟', '麒麟区', '中国,云南省,曲靖市,麒麟区', '3', 'qilin', '0874', '655000', null, '103.80504', '25.49515'); +INSERT INTO `yoshop_region` VALUES ('2688', '2686', '马龙', '马龙县', '中国,云南省,曲靖市,马龙县', '3', 'malong', '0874', '655100', 'M', '103.57873', '25.42521'); +INSERT INTO `yoshop_region` VALUES ('2689', '2686', '陆良', '陆良县', '中国,云南省,曲靖市,陆良县', '3', 'luliang', '0874', '655600', 'L', '103.6665', '25.02335'); +INSERT INTO `yoshop_region` VALUES ('2690', '2686', '师宗', '师宗县', '中国,云南省,曲靖市,师宗县', '3', 'shizong', '0874', '655700', 'S', '103.99084', '24.82822'); +INSERT INTO `yoshop_region` VALUES ('2691', '2686', '罗平', '罗平县', '中国,云南省,曲靖市,罗平县', '3', 'luoping', '0874', '655800', 'L', '104.30859', '24.88444'); +INSERT INTO `yoshop_region` VALUES ('2692', '2686', '富源', '富源县', '中国,云南省,曲靖市,富源县', '3', 'fuyuan', '0874', '655500', 'F', '104.25387', '25.66587'); +INSERT INTO `yoshop_region` VALUES ('2693', '2686', '会泽', '会泽县', '中国,云南省,曲靖市,会泽县', '3', 'huize', '0874', '654200', 'H', '103.30017', '26.41076'); +INSERT INTO `yoshop_region` VALUES ('2694', '2686', '沾益', '沾益县', '中国,云南省,曲靖市,沾益县', '3', 'zhanyi', '0874', '655331', 'Z', '103.82135', '25.60715'); +INSERT INTO `yoshop_region` VALUES ('2695', '2686', '宣威', '宣威市', '中国,云南省,曲靖市,宣威市', '3', 'xuanwei', '0874', '655400', 'X', '104.10409', '26.2173'); +INSERT INTO `yoshop_region` VALUES ('2696', '2670', '玉溪', '玉溪市', '中国,云南省,玉溪市', '2', 'yuxi', '0877', '653100', 'Y', '102.543907', '24.350461'); +INSERT INTO `yoshop_region` VALUES ('2697', '2696', '红塔', '红塔区', '中国,云南省,玉溪市,红塔区', '3', 'hongta', '0877', '653100', 'H', '102.5449', '24.35411'); +INSERT INTO `yoshop_region` VALUES ('2698', '2696', '江川', '江川县', '中国,云南省,玉溪市,江川县', '3', 'jiangchuan', '0877', '652600', 'J', '102.75412', '24.28863'); +INSERT INTO `yoshop_region` VALUES ('2699', '2696', '澄江', '澄江县', '中国,云南省,玉溪市,澄江县', '3', 'chengjiang', '0877', '652500', 'C', '102.90817', '24.67376'); +INSERT INTO `yoshop_region` VALUES ('2700', '2696', '通海', '通海县', '中国,云南省,玉溪市,通海县', '3', 'tonghai', '0877', '652700', 'T', '102.76641', '24.11362'); +INSERT INTO `yoshop_region` VALUES ('2701', '2696', '华宁', '华宁县', '中国,云南省,玉溪市,华宁县', '3', 'huaning', '0877', '652800', 'H', '102.92831', '24.1926'); +INSERT INTO `yoshop_region` VALUES ('2702', '2696', '易门', '易门县', '中国,云南省,玉溪市,易门县', '3', 'yimen', '0877', '651100', 'Y', '102.16354', '24.67122'); +INSERT INTO `yoshop_region` VALUES ('2703', '2696', '峨山', '峨山彝族自治县', '中国,云南省,玉溪市,峨山彝族自治县', '3', 'eshan', '0877', '653200', 'E', '102.40576', '24.16904'); +INSERT INTO `yoshop_region` VALUES ('2704', '2696', '新平', '新平彝族傣族自治县', '中国,云南省,玉溪市,新平彝族傣族自治县', '3', 'xinping', '0877', '653400', 'X', '101.98895', '24.06886'); +INSERT INTO `yoshop_region` VALUES ('2705', '2696', '元江', '元江哈尼族彝族傣族自治县', '中国,云南省,玉溪市,元江哈尼族彝族傣族自治县', '3', 'yuanjiang', '0877', '653300', 'Y', '101.99812', '23.59655'); +INSERT INTO `yoshop_region` VALUES ('2706', '2670', '保山', '保山市', '中国,云南省,保山市', '2', 'baoshan', '0875', '678000', 'B', '99.167133', '25.111802'); +INSERT INTO `yoshop_region` VALUES ('2707', '2706', '隆阳', '隆阳区', '中国,云南省,保山市,隆阳区', '3', 'longyang', '0875', '678000', 'L', '99.16334', '25.11163'); +INSERT INTO `yoshop_region` VALUES ('2708', '2706', '施甸', '施甸县', '中国,云南省,保山市,施甸县', '3', 'shidian', '0875', '678200', 'S', '99.18768', '24.72418'); +INSERT INTO `yoshop_region` VALUES ('2709', '2706', '腾冲', '腾冲县', '中国,云南省,保山市,腾冲县', '3', 'tengchong', '0875', '679100', 'T', '98.49414', '25.02539'); +INSERT INTO `yoshop_region` VALUES ('2710', '2706', '龙陵', '龙陵县', '中国,云南省,保山市,龙陵县', '3', 'longling', '0875', '678300', 'L', '98.69024', '24.58746'); +INSERT INTO `yoshop_region` VALUES ('2711', '2706', '昌宁', '昌宁县', '中国,云南省,保山市,昌宁县', '3', 'changning', '0875', '678100', 'C', '99.6036', '24.82763'); +INSERT INTO `yoshop_region` VALUES ('2712', '2670', '昭通', '昭通市', '中国,云南省,昭通市', '2', 'zhaotong', '0870', '657000', 'Z', '103.717216', '27.336999'); +INSERT INTO `yoshop_region` VALUES ('2713', '2712', '昭阳', '昭阳区', '中国,云南省,昭通市,昭阳区', '3', 'zhaoyang', '0870', '657000', 'Z', '103.70654', '27.31998'); +INSERT INTO `yoshop_region` VALUES ('2714', '2712', '鲁甸', '鲁甸县', '中国,云南省,昭通市,鲁甸县', '3', 'ludian', '0870', '657100', 'L', '103.54721', '27.19238'); +INSERT INTO `yoshop_region` VALUES ('2715', '2712', '巧家', '巧家县', '中国,云南省,昭通市,巧家县', '3', 'qiaojia', '0870', '654600', 'Q', '102.92416', '26.91237'); +INSERT INTO `yoshop_region` VALUES ('2716', '2712', '盐津', '盐津县', '中国,云南省,昭通市,盐津县', '3', 'yanjin', '0870', '657500', 'Y', '104.23461', '28.10856'); +INSERT INTO `yoshop_region` VALUES ('2717', '2712', '大关', '大关县', '中国,云南省,昭通市,大关县', '3', 'daguan', '0870', '657400', 'D', '103.89254', '27.7488'); +INSERT INTO `yoshop_region` VALUES ('2718', '2712', '永善', '永善县', '中国,云南省,昭通市,永善县', '3', 'yongshan', '0870', '657300', 'Y', '103.63504', '28.2279'); +INSERT INTO `yoshop_region` VALUES ('2719', '2712', '绥江', '绥江县', '中国,云南省,昭通市,绥江县', '3', 'suijiang', '0870', '657700', 'S', '103.94937', '28.59661'); +INSERT INTO `yoshop_region` VALUES ('2720', '2712', '镇雄', '镇雄县', '中国,云南省,昭通市,镇雄县', '3', 'zhenxiong', '0870', '657200', 'Z', '104.87258', '27.43981'); +INSERT INTO `yoshop_region` VALUES ('2721', '2712', '彝良', '彝良县', '中国,云南省,昭通市,彝良县', '3', 'yiliang', '0870', '657600', 'Y', '104.04983', '27.62809'); +INSERT INTO `yoshop_region` VALUES ('2722', '2712', '威信', '威信县', '中国,云南省,昭通市,威信县', '3', 'weixin', '0870', '657900', 'W', '105.04754', '27.84065'); +INSERT INTO `yoshop_region` VALUES ('2723', '2712', '水富', '水富县', '中国,云南省,昭通市,水富县', '3', 'shuifu', '0870', '657800', 'S', '104.4158', '28.62986'); +INSERT INTO `yoshop_region` VALUES ('2724', '2670', '丽江', '丽江市', '中国,云南省,丽江市', '2', 'lijiang', '0888', '674100', 'L', '100.233026', '26.872108'); +INSERT INTO `yoshop_region` VALUES ('2725', '2724', '古城', '古城区', '中国,云南省,丽江市,古城区', '3', 'gucheng', '0888', '674100', 'G', '100.2257', '26.87697'); +INSERT INTO `yoshop_region` VALUES ('2726', '2724', '玉龙', '玉龙纳西族自治县', '中国,云南省,丽江市,玉龙纳西族自治县', '3', 'yulong', '0888', '674100', 'Y', '100.2369', '26.82149'); +INSERT INTO `yoshop_region` VALUES ('2727', '2724', '永胜', '永胜县', '中国,云南省,丽江市,永胜县', '3', 'yongsheng', '0888', '674200', 'Y', '100.74667', '26.68591'); +INSERT INTO `yoshop_region` VALUES ('2728', '2724', '华坪', '华坪县', '中国,云南省,丽江市,华坪县', '3', 'huaping', '0888', '674800', 'H', '101.26562', '26.62967'); +INSERT INTO `yoshop_region` VALUES ('2729', '2724', '宁蒗', '宁蒗彝族自治县', '中国,云南省,丽江市,宁蒗彝族自治县', '3', 'ninglang', '0888', '674300', 'N', '100.8507', '27.28179'); +INSERT INTO `yoshop_region` VALUES ('2730', '2670', '普洱', '普洱市', '中国,云南省,普洱市', '2', 'pu\'er', '0879', '665000', 'P', '100.972344', '22.777321'); +INSERT INTO `yoshop_region` VALUES ('2731', '2730', '思茅', '思茅区', '中国,云南省,普洱市,思茅区', '3', 'simao', '0879', '665000', 'S', '100.97716', '22.78691'); +INSERT INTO `yoshop_region` VALUES ('2732', '2730', '宁洱', '宁洱哈尼族彝族自治县', '中国,云南省,普洱市,宁洱哈尼族彝族自治县', '3', 'ninger', '0879', '665100', 'N', '101.04653', '23.06341'); +INSERT INTO `yoshop_region` VALUES ('2733', '2730', '墨江', '墨江哈尼族自治县', '中国,云南省,普洱市,墨江哈尼族自治县', '3', 'mojiang', '0879', '654800', 'M', '101.69171', '23.43214'); +INSERT INTO `yoshop_region` VALUES ('2734', '2730', '景东', '景东彝族自治县', '中国,云南省,普洱市,景东彝族自治县', '3', 'jingdong', '0879', '676200', 'J', '100.83599', '24.44791'); +INSERT INTO `yoshop_region` VALUES ('2735', '2730', '景谷', '景谷傣族彝族自治县', '中国,云南省,普洱市,景谷傣族彝族自治县', '3', 'jinggu', '0879', '666400', 'J', '100.70251', '23.49705'); +INSERT INTO `yoshop_region` VALUES ('2736', '2730', '镇沅', '镇沅彝族哈尼族拉祜族自治县', '中国,云南省,普洱市,镇沅彝族哈尼族拉祜族自治县', '3', 'zhenyuan', '0879', '666500', 'Z', '101.10675', '24.00557'); +INSERT INTO `yoshop_region` VALUES ('2737', '2730', '江城', '江城哈尼族彝族自治县', '中国,云南省,普洱市,江城哈尼族彝族自治县', '3', 'jiangcheng', '0879', '665900', 'J', '101.85788', '22.58424'); +INSERT INTO `yoshop_region` VALUES ('2738', '2730', '孟连', '孟连傣族拉祜族佤族自治县', '中国,云南省,普洱市,孟连傣族拉祜族佤族自治县', '3', 'menglian', '0879', '665800', 'M', '99.58424', '22.32922'); +INSERT INTO `yoshop_region` VALUES ('2739', '2730', '澜沧', '澜沧拉祜族自治县', '中国,云南省,普洱市,澜沧拉祜族自治县', '3', 'lancang', '0879', '665600', 'L', '99.93591', '22.55474'); +INSERT INTO `yoshop_region` VALUES ('2740', '2730', '西盟', '西盟佤族自治县', '中国,云南省,普洱市,西盟佤族自治县', '3', 'ximeng', '0879', '665700', 'X', '99.59869', '22.64774'); +INSERT INTO `yoshop_region` VALUES ('2741', '2670', '临沧', '临沧市', '中国,云南省,临沧市', '2', 'lincang', '0883', '677000', 'L', '100.08697', '23.886567'); +INSERT INTO `yoshop_region` VALUES ('2742', '2741', '临翔', '临翔区', '中国,云南省,临沧市,临翔区', '3', 'linxiang', '0883', '677000', 'L', '100.08242', '23.89497'); +INSERT INTO `yoshop_region` VALUES ('2743', '2741', '凤庆', '凤庆县', '中国,云南省,临沧市,凤庆县', '3', 'fengqing', '0883', '675900', 'F', '99.92837', '24.58034'); +INSERT INTO `yoshop_region` VALUES ('2744', '2741', '云县', '云县', '中国,云南省,临沧市,云县', '3', 'yunxian', '0883', '675800', 'Y', '100.12808', '24.44675'); +INSERT INTO `yoshop_region` VALUES ('2745', '2741', '永德', '永德县', '中国,云南省,临沧市,永德县', '3', 'yongde', '0883', '677600', 'Y', '99.25326', '24.0276'); +INSERT INTO `yoshop_region` VALUES ('2746', '2741', '镇康', '镇康县', '中国,云南省,临沧市,镇康县', '3', 'zhenkang', '0883', '677704', 'Z', '98.8255', '23.76241'); +INSERT INTO `yoshop_region` VALUES ('2747', '2741', '双江', '双江拉祜族佤族布朗族傣族自治县', '中国,云南省,临沧市,双江拉祜族佤族布朗族傣族自治县', '3', 'shuangjiang', '0883', '677300', 'S', '99.82769', '23.47349'); +INSERT INTO `yoshop_region` VALUES ('2748', '2741', '耿马', '耿马傣族佤族自治县', '中国,云南省,临沧市,耿马傣族佤族自治县', '3', 'gengma', '0883', '677500', 'G', '99.39785', '23.53776'); +INSERT INTO `yoshop_region` VALUES ('2749', '2741', '沧源', '沧源佤族自治县', '中国,云南省,临沧市,沧源佤族自治县', '3', 'cangyuan', '0883', '677400', 'C', '99.24545', '23.14821'); +INSERT INTO `yoshop_region` VALUES ('2750', '2670', '楚雄', '楚雄彝族自治州', '中国,云南省,楚雄彝族自治州', '2', 'chuxiong', '0878', '675000', 'C', '101.546046', '25.041988'); +INSERT INTO `yoshop_region` VALUES ('2751', '2750', '楚雄', '楚雄市', '中国,云南省,楚雄彝族自治州,楚雄市', '3', 'chuxiong', '0878', '675000', 'C', '101.54615', '25.0329'); +INSERT INTO `yoshop_region` VALUES ('2752', '2750', '双柏', '双柏县', '中国,云南省,楚雄彝族自治州,双柏县', '3', 'shuangbai', '0878', '675100', 'S', '101.64205', '24.68882'); +INSERT INTO `yoshop_region` VALUES ('2753', '2750', '牟定', '牟定县', '中国,云南省,楚雄彝族自治州,牟定县', '3', 'mouding', '0878', '675500', 'M', '101.54', '25.31551'); +INSERT INTO `yoshop_region` VALUES ('2754', '2750', '南华', '南华县', '中国,云南省,楚雄彝族自治州,南华县', '3', 'nanhua', '0878', '675200', 'N', '101.27313', '25.19293'); +INSERT INTO `yoshop_region` VALUES ('2755', '2750', '姚安', '姚安县', '中国,云南省,楚雄彝族自治州,姚安县', '3', 'yao\'an', '0878', '675300', 'Y', '101.24279', '25.50467'); +INSERT INTO `yoshop_region` VALUES ('2756', '2750', '大姚', '大姚县', '中国,云南省,楚雄彝族自治州,大姚县', '3', 'dayao', '0878', '675400', 'D', '101.32397', '25.72218'); +INSERT INTO `yoshop_region` VALUES ('2757', '2750', '永仁', '永仁县', '中国,云南省,楚雄彝族自治州,永仁县', '3', 'yongren', '0878', '651400', 'Y', '101.6716', '26.05794'); +INSERT INTO `yoshop_region` VALUES ('2758', '2750', '元谋', '元谋县', '中国,云南省,楚雄彝族自治州,元谋县', '3', 'yuanmou', '0878', '651300', 'Y', '101.87728', '25.70438'); +INSERT INTO `yoshop_region` VALUES ('2759', '2750', '武定', '武定县', '中国,云南省,楚雄彝族自治州,武定县', '3', 'wuding', '0878', '651600', 'W', '102.4038', '25.5295'); +INSERT INTO `yoshop_region` VALUES ('2760', '2750', '禄丰', '禄丰县', '中国,云南省,楚雄彝族自治州,禄丰县', '3', 'lufeng', '0878', '651200', 'L', '102.07797', '25.14815'); +INSERT INTO `yoshop_region` VALUES ('2761', '2670', '红河', '红河哈尼族彝族自治州', '中国,云南省,红河哈尼族彝族自治州', '2', 'honghe', '0873', '661400', 'H', '103.384182', '23.366775'); +INSERT INTO `yoshop_region` VALUES ('2762', '2761', '个旧', '个旧市', '中国,云南省,红河哈尼族彝族自治州,个旧市', '3', 'gejiu', '0873', '661000', 'G', '103.15966', '23.35894'); +INSERT INTO `yoshop_region` VALUES ('2763', '2761', '开远', '开远市', '中国,云南省,红河哈尼族彝族自治州,开远市', '3', 'kaiyuan', '0873', '661600', 'K', '103.26986', '23.71012'); +INSERT INTO `yoshop_region` VALUES ('2764', '2761', '蒙自', '蒙自市', '中国,云南省,红河哈尼族彝族自治州,蒙自市', '3', 'mengzi', '0873', '661101', 'M', '103.385005', '23.366843'); +INSERT INTO `yoshop_region` VALUES ('2765', '2761', '弥勒', '弥勒市', '中国,云南省,红河哈尼族彝族自治州,弥勒市', '3', 'mile', '0873', '652300', 'M', '103.436988', '24.40837'); +INSERT INTO `yoshop_region` VALUES ('2766', '2761', '屏边', '屏边苗族自治县', '中国,云南省,红河哈尼族彝族自治州,屏边苗族自治县', '3', 'pingbian', '0873', '661200', 'P', '103.68554', '22.98425'); +INSERT INTO `yoshop_region` VALUES ('2767', '2761', '建水', '建水县', '中国,云南省,红河哈尼族彝族自治州,建水县', '3', 'jianshui', '0873', '654300', 'J', '102.82656', '23.63472'); +INSERT INTO `yoshop_region` VALUES ('2768', '2761', '石屏', '石屏县', '中国,云南省,红河哈尼族彝族自治州,石屏县', '3', 'shiping', '0873', '662200', 'S', '102.49408', '23.71441'); +INSERT INTO `yoshop_region` VALUES ('2769', '2761', '泸西', '泸西县', '中国,云南省,红河哈尼族彝族自治州,泸西县', '3', 'luxi', '0873', '652400', null, '103.76373', '24.52854'); +INSERT INTO `yoshop_region` VALUES ('2770', '2761', '元阳', '元阳县', '中国,云南省,红河哈尼族彝族自治州,元阳县', '3', 'yuanyang', '0873', '662400', 'Y', '102.83261', '23.22281'); +INSERT INTO `yoshop_region` VALUES ('2771', '2761', '红河县', '红河县', '中国,云南省,红河哈尼族彝族自治州,红河县', '3', 'honghexian', '0873', '654400', 'H', '102.42059', '23.36767'); +INSERT INTO `yoshop_region` VALUES ('2772', '2761', '金平', '金平苗族瑶族傣族自治县', '中国,云南省,红河哈尼族彝族自治州,金平苗族瑶族傣族自治县', '3', 'jinping', '0873', '661500', 'J', '103.22651', '22.77959'); +INSERT INTO `yoshop_region` VALUES ('2773', '2761', '绿春', '绿春县', '中国,云南省,红河哈尼族彝族自治州,绿春县', '3', 'lvchun', '0873', '662500', 'L', '102.39672', '22.99371'); +INSERT INTO `yoshop_region` VALUES ('2774', '2761', '河口', '河口瑶族自治县', '中国,云南省,红河哈尼族彝族自治州,河口瑶族自治县', '3', 'hekou', '0873', '661300', 'H', '103.93936', '22.52929'); +INSERT INTO `yoshop_region` VALUES ('2775', '2670', '文山', '文山壮族苗族自治州', '中国,云南省,文山壮族苗族自治州', '2', 'wenshan', '0876', '663000', 'W', '104.24401', '23.36951'); +INSERT INTO `yoshop_region` VALUES ('2776', '2775', '文山', '文山市', '中国,云南省,文山壮族苗族自治州,文山市', '3', 'wenshan', '0876', '663000', 'W', '104.244277', '23.369216'); +INSERT INTO `yoshop_region` VALUES ('2777', '2775', '砚山', '砚山县', '中国,云南省,文山壮族苗族自治州,砚山县', '3', 'yanshan', '0876', '663100', 'Y', '104.33306', '23.60723'); +INSERT INTO `yoshop_region` VALUES ('2778', '2775', '西畴', '西畴县', '中国,云南省,文山壮族苗族自治州,西畴县', '3', 'xichou', '0876', '663500', 'X', '104.67419', '23.43941'); +INSERT INTO `yoshop_region` VALUES ('2779', '2775', '麻栗坡', '麻栗坡县', '中国,云南省,文山壮族苗族自治州,麻栗坡县', '3', 'malipo', '0876', '663600', 'M', '104.70132', '23.12028'); +INSERT INTO `yoshop_region` VALUES ('2780', '2775', '马关', '马关县', '中国,云南省,文山壮族苗族自治州,马关县', '3', 'maguan', '0876', '663700', 'M', '104.39514', '23.01293'); +INSERT INTO `yoshop_region` VALUES ('2781', '2775', '丘北', '丘北县', '中国,云南省,文山壮族苗族自治州,丘北县', '3', 'qiubei', '0876', '663200', 'Q', '104.19256', '24.03957'); +INSERT INTO `yoshop_region` VALUES ('2782', '2775', '广南', '广南县', '中国,云南省,文山壮族苗族自治州,广南县', '3', 'guangnan', '0876', '663300', 'G', '105.05511', '24.0464'); +INSERT INTO `yoshop_region` VALUES ('2783', '2775', '富宁', '富宁县', '中国,云南省,文山壮族苗族自治州,富宁县', '3', 'funing', '0876', '663400', 'F', '105.63085', '23.62536'); +INSERT INTO `yoshop_region` VALUES ('2784', '2670', '西双版纳', '西双版纳傣族自治州', '中国,云南省,西双版纳傣族自治州', '2', 'xishuangbanna', '0691', '666100', 'X', '100.797941', '22.001724'); +INSERT INTO `yoshop_region` VALUES ('2785', '2784', '景洪', '景洪市', '中国,云南省,西双版纳傣族自治州,景洪市', '3', 'jinghong', '0691', '666100', 'J', '100.79977', '22.01071'); +INSERT INTO `yoshop_region` VALUES ('2786', '2784', '勐海', '勐海县', '中国,云南省,西双版纳傣族自治州,勐海县', '3', 'menghai', '0691', '666200', null, '100.44931', '21.96175'); +INSERT INTO `yoshop_region` VALUES ('2787', '2784', '勐腊', '勐腊县', '中国,云南省,西双版纳傣族自治州,勐腊县', '3', 'mengla', '0691', '666300', null, '101.56488', '21.48162'); +INSERT INTO `yoshop_region` VALUES ('2788', '2670', '大理', '大理白族自治州', '中国,云南省,大理白族自治州', '2', 'dali', '0872', '671000', 'D', '100.240037', '25.592765'); +INSERT INTO `yoshop_region` VALUES ('2789', '2788', '大理', '大理市', '中国,云南省,大理白族自治州,大理市', '3', 'dali', '0872', '671000', 'D', '100.22998', '25.59157'); +INSERT INTO `yoshop_region` VALUES ('2790', '2788', '漾濞', '漾濞彝族自治县', '中国,云南省,大理白族自治州,漾濞彝族自治县', '3', 'yangbi', '0872', '672500', 'Y', '99.95474', '25.6652'); +INSERT INTO `yoshop_region` VALUES ('2791', '2788', '祥云', '祥云县', '中国,云南省,大理白族自治州,祥云县', '3', 'xiangyun', '0872', '672100', 'X', '100.55761', '25.47342'); +INSERT INTO `yoshop_region` VALUES ('2792', '2788', '宾川', '宾川县', '中国,云南省,大理白族自治州,宾川县', '3', 'binchuan', '0872', '671600', 'B', '100.57666', '25.83144'); +INSERT INTO `yoshop_region` VALUES ('2793', '2788', '弥渡', '弥渡县', '中国,云南省,大理白族自治州,弥渡县', '3', 'midu', '0872', '675600', 'M', '100.49075', '25.34179'); +INSERT INTO `yoshop_region` VALUES ('2794', '2788', '南涧', '南涧彝族自治县', '中国,云南省,大理白族自治州,南涧彝族自治县', '3', 'nanjian', '0872', '675700', 'N', '100.50964', '25.04349'); +INSERT INTO `yoshop_region` VALUES ('2795', '2788', '巍山', '巍山彝族回族自治县', '中国,云南省,大理白族自治州,巍山彝族回族自治县', '3', 'weishan', '0872', '672400', 'W', '100.30612', '25.23197'); +INSERT INTO `yoshop_region` VALUES ('2796', '2788', '永平', '永平县', '中国,云南省,大理白族自治州,永平县', '3', 'yongping', '0872', '672600', 'Y', '99.54095', '25.46451'); +INSERT INTO `yoshop_region` VALUES ('2797', '2788', '云龙', '云龙县', '中国,云南省,大理白族自治州,云龙县', '3', 'yunlong', '0872', '672700', 'Y', '99.37255', '25.88505'); +INSERT INTO `yoshop_region` VALUES ('2798', '2788', '洱源', '洱源县', '中国,云南省,大理白族自治州,洱源县', '3', 'eryuan', '0872', '671200', 'E', '99.94903', '26.10829'); +INSERT INTO `yoshop_region` VALUES ('2799', '2788', '剑川', '剑川县', '中国,云南省,大理白族自治州,剑川县', '3', 'jianchuan', '0872', '671300', 'J', '99.90545', '26.53688'); +INSERT INTO `yoshop_region` VALUES ('2800', '2788', '鹤庆', '鹤庆县', '中国,云南省,大理白族自治州,鹤庆县', '3', 'heqing', '0872', '671500', 'H', '100.17697', '26.55798'); +INSERT INTO `yoshop_region` VALUES ('2801', '2670', '德宏', '德宏傣族景颇族自治州', '中国,云南省,德宏傣族景颇族自治州', '2', 'dehong', '0692', '678400', 'D', '98.578363', '24.436694'); +INSERT INTO `yoshop_region` VALUES ('2802', '2801', '瑞丽', '瑞丽市', '中国,云南省,德宏傣族景颇族自治州,瑞丽市', '3', 'ruili', '0692', '678600', 'R', '97.85183', '24.01277'); +INSERT INTO `yoshop_region` VALUES ('2803', '2801', '芒市', '芒市', '中国,云南省,德宏傣族景颇族自治州,芒市', '3', 'mangshi', '0692', '678400', 'M', '98.588641', '24.433735'); +INSERT INTO `yoshop_region` VALUES ('2804', '2801', '梁河', '梁河县', '中国,云南省,德宏傣族景颇族自治州,梁河县', '3', 'lianghe', '0692', '679200', 'L', '98.29705', '24.80658'); +INSERT INTO `yoshop_region` VALUES ('2805', '2801', '盈江', '盈江县', '中国,云南省,德宏傣族景颇族自治州,盈江县', '3', 'yingjiang', '0692', '679300', 'Y', '97.93179', '24.70579'); +INSERT INTO `yoshop_region` VALUES ('2806', '2801', '陇川', '陇川县', '中国,云南省,德宏傣族景颇族自治州,陇川县', '3', 'longchuan', '0692', '678700', 'L', '97.79199', '24.18302'); +INSERT INTO `yoshop_region` VALUES ('2807', '2670', '怒江', '怒江傈僳族自治州', '中国,云南省,怒江傈僳族自治州', '2', 'nujiang', '0886', '673100', 'N', '98.854304', '25.850949'); +INSERT INTO `yoshop_region` VALUES ('2808', '2807', '泸水', '泸水县', '中国,云南省,怒江傈僳族自治州,泸水县', '3', 'lushui', '0886', '673100', null, '98.85534', '25.83772'); +INSERT INTO `yoshop_region` VALUES ('2809', '2807', '福贡', '福贡县', '中国,云南省,怒江傈僳族自治州,福贡县', '3', 'fugong', '0886', '673400', 'F', '98.86969', '26.90366'); +INSERT INTO `yoshop_region` VALUES ('2810', '2807', '贡山', '贡山独龙族怒族自治县', '中国,云南省,怒江傈僳族自治州,贡山独龙族怒族自治县', '3', 'gongshan', '0886', '673500', 'G', '98.66583', '27.74088'); +INSERT INTO `yoshop_region` VALUES ('2811', '2807', '兰坪', '兰坪白族普米族自治县', '中国,云南省,怒江傈僳族自治州,兰坪白族普米族自治县', '3', 'lanping', '0886', '671400', 'L', '99.41891', '26.45251'); +INSERT INTO `yoshop_region` VALUES ('2812', '2670', '迪庆', '迪庆藏族自治州', '中国,云南省,迪庆藏族自治州', '2', 'deqen', '0887', '674400', 'D', '99.706463', '27.826853'); +INSERT INTO `yoshop_region` VALUES ('2813', '2812', '香格里拉', '香格里拉市', '中国,云南省,迪庆藏族自治州,香格里拉市', '3', 'xianggelila', '0887', '674400', 'X', '99.70601', '27.82308'); +INSERT INTO `yoshop_region` VALUES ('2814', '2812', '德钦', '德钦县', '中国,云南省,迪庆藏族自治州,德钦县', '3', 'deqin', '0887', '674500', 'D', '98.91082', '28.4863'); +INSERT INTO `yoshop_region` VALUES ('2815', '2812', '维西', '维西傈僳族自治县', '中国,云南省,迪庆藏族自治州,维西傈僳族自治县', '3', 'weixi', '0887', '674600', 'W', '99.28402', '27.1793'); +INSERT INTO `yoshop_region` VALUES ('2816', '0', '西藏', '西藏自治区', '中国,西藏自治区', '1', 'tibet', '', '', 'X', '91.132212', '29.660361'); +INSERT INTO `yoshop_region` VALUES ('2817', '2816', '拉萨', '拉萨市', '中国,西藏自治区,拉萨市', '2', 'lhasa', '0891', '850000', 'L', '91.132212', '29.660361'); +INSERT INTO `yoshop_region` VALUES ('2818', '2817', '城关', '城关区', '中国,西藏自治区,拉萨市,城关区', '3', 'chengguan', '0891', '850000', 'C', '91.13859', '29.65312'); +INSERT INTO `yoshop_region` VALUES ('2819', '2817', '林周', '林周县', '中国,西藏自治区,拉萨市,林周县', '3', 'linzhou', '0891', '851600', 'L', '91.2586', '29.89445'); +INSERT INTO `yoshop_region` VALUES ('2820', '2817', '当雄', '当雄县', '中国,西藏自治区,拉萨市,当雄县', '3', 'dangxiong', '0891', '851500', 'D', '91.10076', '30.48309'); +INSERT INTO `yoshop_region` VALUES ('2821', '2817', '尼木', '尼木县', '中国,西藏自治区,拉萨市,尼木县', '3', 'nimu', '0891', '851300', 'N', '90.16378', '29.43353'); +INSERT INTO `yoshop_region` VALUES ('2822', '2817', '曲水', '曲水县', '中国,西藏自治区,拉萨市,曲水县', '3', 'qushui', '0891', '850600', 'Q', '90.73187', '29.35636'); +INSERT INTO `yoshop_region` VALUES ('2823', '2817', '堆龙德庆', '堆龙德庆县', '中国,西藏自治区,拉萨市,堆龙德庆县', '3', 'duilongdeqing', '0891', '851400', 'D', '91.00033', '29.65002'); +INSERT INTO `yoshop_region` VALUES ('2824', '2817', '达孜', '达孜县', '中国,西藏自治区,拉萨市,达孜县', '3', 'dazi', '0891', '850100', 'D', '91.35757', '29.6722'); +INSERT INTO `yoshop_region` VALUES ('2825', '2817', '墨竹工卡', '墨竹工卡县', '中国,西藏自治区,拉萨市,墨竹工卡县', '3', 'mozhugongka', '0891', '850200', 'M', '91.72814', '29.83614'); +INSERT INTO `yoshop_region` VALUES ('2826', '2816', '日喀则', '日喀则市', '中国,西藏自治区,日喀则市', '2', 'rikaze', '0892', '857000', 'R', '88.884874', '29.263792'); +INSERT INTO `yoshop_region` VALUES ('2827', '2826', '桑珠孜', '桑珠孜区', '中国,西藏自治区,日喀则市,桑珠孜区', '3', 'sangzhuzi', '0892', '857000', 'S', '88.880367', '29.269565'); +INSERT INTO `yoshop_region` VALUES ('2828', '2826', '南木林', '南木林县', '中国,西藏自治区,日喀则市,南木林县', '3', 'nanmulin', '0892', '857100', 'N', '89.09686', '29.68206'); +INSERT INTO `yoshop_region` VALUES ('2829', '2826', '江孜', '江孜县', '中国,西藏自治区,日喀则市,江孜县', '3', 'jiangzi', '0892', '857400', 'J', '89.60263', '28.91744'); +INSERT INTO `yoshop_region` VALUES ('2830', '2826', '定日', '定日县', '中国,西藏自治区,日喀则市,定日县', '3', 'dingri', '0892', '858200', 'D', '87.12176', '28.66129'); +INSERT INTO `yoshop_region` VALUES ('2831', '2826', '萨迦', '萨迦县', '中国,西藏自治区,日喀则市,萨迦县', '3', 'sajia', '0892', '857800', 'S', '88.02191', '28.90299'); +INSERT INTO `yoshop_region` VALUES ('2832', '2826', '拉孜', '拉孜县', '中国,西藏自治区,日喀则市,拉孜县', '3', 'lazi', '0892', '858100', 'L', '87.63412', '29.085'); +INSERT INTO `yoshop_region` VALUES ('2833', '2826', '昂仁', '昂仁县', '中国,西藏自治区,日喀则市,昂仁县', '3', 'angren', '0892', '858500', 'A', '87.23858', '29.29496'); +INSERT INTO `yoshop_region` VALUES ('2834', '2826', '谢通门', '谢通门县', '中国,西藏自治区,日喀则市,谢通门县', '3', 'xietongmen', '0892', '858900', 'X', '88.26242', '29.43337'); +INSERT INTO `yoshop_region` VALUES ('2835', '2826', '白朗', '白朗县', '中国,西藏自治区,日喀则市,白朗县', '3', 'bailang', '0892', '857300', 'B', '89.26205', '29.10553'); +INSERT INTO `yoshop_region` VALUES ('2836', '2826', '仁布', '仁布县', '中国,西藏自治区,日喀则市,仁布县', '3', 'renbu', '0892', '857200', 'R', '89.84228', '29.2301'); +INSERT INTO `yoshop_region` VALUES ('2837', '2826', '康马', '康马县', '中国,西藏自治区,日喀则市,康马县', '3', 'kangma', '0892', '857500', 'K', '89.68527', '28.5567'); +INSERT INTO `yoshop_region` VALUES ('2838', '2826', '定结', '定结县', '中国,西藏自治区,日喀则市,定结县', '3', 'dingjie', '0892', '857900', 'D', '87.77255', '28.36403'); +INSERT INTO `yoshop_region` VALUES ('2839', '2826', '仲巴', '仲巴县', '中国,西藏自治区,日喀则市,仲巴县', '3', 'zhongba', '0892', '858800', 'Z', '84.02951', '29.76595'); +INSERT INTO `yoshop_region` VALUES ('2840', '2826', '亚东', '亚东县', '中国,西藏自治区,日喀则市,亚东县', '3', 'yadong', '0892', '857600', 'Y', '88.90802', '27.4839'); +INSERT INTO `yoshop_region` VALUES ('2841', '2826', '吉隆', '吉隆县', '中国,西藏自治区,日喀则市,吉隆县', '3', 'jilong', '0892', '858700', 'J', '85.29846', '28.85382'); +INSERT INTO `yoshop_region` VALUES ('2842', '2826', '聂拉木', '聂拉木县', '中国,西藏自治区,日喀则市,聂拉木县', '3', 'nielamu', '0892', '858300', 'N', '85.97998', '28.15645'); +INSERT INTO `yoshop_region` VALUES ('2843', '2826', '萨嘎', '萨嘎县', '中国,西藏自治区,日喀则市,萨嘎县', '3', 'saga', '0892', '857800', 'S', '85.23413', '29.32936'); +INSERT INTO `yoshop_region` VALUES ('2844', '2826', '岗巴', '岗巴县', '中国,西藏自治区,日喀则市,岗巴县', '3', 'gangba', '0892', '857700', 'G', '88.52069', '28.27504'); +INSERT INTO `yoshop_region` VALUES ('2845', '2816', '昌都', '昌都市', '中国,西藏自治区,昌都市', '2', 'qamdo', '0895', '854000', 'C', '97.178452', '31.136875'); +INSERT INTO `yoshop_region` VALUES ('2846', '2845', '昌都', '卡若区', '中国,西藏自治区,昌都市,卡若区', '3', 'karuo', '0895', '854000', 'K', '97.18043', '31.1385'); +INSERT INTO `yoshop_region` VALUES ('2847', '2845', '江达', '江达县', '中国,西藏自治区,昌都市,江达县', '3', 'jiangda', '0895', '854100', 'J', '98.21865', '31.50343'); +INSERT INTO `yoshop_region` VALUES ('2848', '2845', '贡觉', '贡觉县', '中国,西藏自治区,昌都市,贡觉县', '3', 'gongjue', '0895', '854200', 'G', '98.27163', '30.85941'); +INSERT INTO `yoshop_region` VALUES ('2849', '2845', '类乌齐', '类乌齐县', '中国,西藏自治区,昌都市,类乌齐县', '3', 'leiwuqi', '0895', '855600', 'L', '96.60015', '31.21207'); +INSERT INTO `yoshop_region` VALUES ('2850', '2845', '丁青', '丁青县', '中国,西藏自治区,昌都市,丁青县', '3', 'dingqing', '0895', '855700', 'D', '95.59362', '31.41621'); +INSERT INTO `yoshop_region` VALUES ('2851', '2845', '察雅', '察雅县', '中国,西藏自治区,昌都市,察雅县', '3', 'chaya', '0895', '854300', 'C', '97.56521', '30.65336'); +INSERT INTO `yoshop_region` VALUES ('2852', '2845', '八宿', '八宿县', '中国,西藏自治区,昌都市,八宿县', '3', 'basu', '0895', '854600', 'B', '96.9176', '30.05346'); +INSERT INTO `yoshop_region` VALUES ('2853', '2845', '左贡', '左贡县', '中国,西藏自治区,昌都市,左贡县', '3', 'zuogong', '0895', '854400', 'Z', '97.84429', '29.67108'); +INSERT INTO `yoshop_region` VALUES ('2854', '2845', '芒康', '芒康县', '中国,西藏自治区,昌都市,芒康县', '3', 'mangkang', '0895', '854500', 'M', '98.59378', '29.67946'); +INSERT INTO `yoshop_region` VALUES ('2855', '2845', '洛隆', '洛隆县', '中国,西藏自治区,昌都市,洛隆县', '3', 'luolong', '0895', '855400', 'L', '95.82644', '30.74049'); +INSERT INTO `yoshop_region` VALUES ('2856', '2845', '边坝', '边坝县', '中国,西藏自治区,昌都市,边坝县', '3', 'bianba', '0895', '855500', 'B', '94.70687', '30.93434'); +INSERT INTO `yoshop_region` VALUES ('2857', '2816', '山南', '山南地区', '中国,西藏自治区,山南地区', '2', 'shannan', '0893', '856000', 'S', '91.766529', '29.236023'); +INSERT INTO `yoshop_region` VALUES ('2858', '2857', '乃东', '乃东县', '中国,西藏自治区,山南地区,乃东县', '3', 'naidong', '0893', '856100', 'N', '91.76153', '29.2249'); +INSERT INTO `yoshop_region` VALUES ('2859', '2857', '扎囊', '扎囊县', '中国,西藏自治区,山南地区,扎囊县', '3', 'zhanang', '0893', '850800', 'Z', '91.33288', '29.2399'); +INSERT INTO `yoshop_region` VALUES ('2860', '2857', '贡嘎', '贡嘎县', '中国,西藏自治区,山南地区,贡嘎县', '3', 'gongga', '0893', '850700', 'G', '90.98867', '29.29408'); +INSERT INTO `yoshop_region` VALUES ('2861', '2857', '桑日', '桑日县', '中国,西藏自治区,山南地区,桑日县', '3', 'sangri', '0893', '856200', 'S', '92.02005', '29.26643'); +INSERT INTO `yoshop_region` VALUES ('2862', '2857', '琼结', '琼结县', '中国,西藏自治区,山南地区,琼结县', '3', 'qiongjie', '0893', '856800', 'Q', '91.68093', '29.02632'); +INSERT INTO `yoshop_region` VALUES ('2863', '2857', '曲松', '曲松县', '中国,西藏自治区,山南地区,曲松县', '3', 'qusong', '0893', '856300', 'Q', '92.20263', '29.06412'); +INSERT INTO `yoshop_region` VALUES ('2864', '2857', '措美', '措美县', '中国,西藏自治区,山南地区,措美县', '3', 'cuomei', '0893', '856900', 'C', '91.43237', '28.43794'); +INSERT INTO `yoshop_region` VALUES ('2865', '2857', '洛扎', '洛扎县', '中国,西藏自治区,山南地区,洛扎县', '3', 'luozha', '0893', '856600', 'L', '90.86035', '28.3872'); +INSERT INTO `yoshop_region` VALUES ('2866', '2857', '加查', '加查县', '中国,西藏自治区,山南地区,加查县', '3', 'jiacha', '0893', '856400', 'J', '92.57702', '29.13973'); +INSERT INTO `yoshop_region` VALUES ('2867', '2857', '隆子', '隆子县', '中国,西藏自治区,山南地区,隆子县', '3', 'longzi', '0893', '856600', 'L', '92.46148', '28.40797'); +INSERT INTO `yoshop_region` VALUES ('2868', '2857', '错那', '错那县', '中国,西藏自治区,山南地区,错那县', '3', 'cuona', '0893', '856700', 'C', '91.95752', '27.99224'); +INSERT INTO `yoshop_region` VALUES ('2869', '2857', '浪卡子', '浪卡子县', '中国,西藏自治区,山南地区,浪卡子县', '3', 'langkazi', '0893', '851100', 'L', '90.40002', '28.96948'); +INSERT INTO `yoshop_region` VALUES ('2870', '2816', '那曲', '那曲地区', '中国,西藏自治区,那曲地区', '2', 'nagqu', '0896', '852000', 'N', '92.060214', '31.476004'); +INSERT INTO `yoshop_region` VALUES ('2871', '2870', '那曲', '那曲县', '中国,西藏自治区,那曲地区,那曲县', '3', 'naqu', '0896', '852000', 'N', '92.0535', '31.46964'); +INSERT INTO `yoshop_region` VALUES ('2872', '2870', '嘉黎', '嘉黎县', '中国,西藏自治区,那曲地区,嘉黎县', '3', 'jiali', '0896', '852400', 'J', '93.24987', '30.64233'); +INSERT INTO `yoshop_region` VALUES ('2873', '2870', '比如', '比如县', '中国,西藏自治区,那曲地区,比如县', '3', 'biru', '0896', '852300', 'B', '93.68685', '31.4779'); +INSERT INTO `yoshop_region` VALUES ('2874', '2870', '聂荣', '聂荣县', '中国,西藏自治区,那曲地区,聂荣县', '3', 'nierong', '0896', '853500', 'N', '92.29574', '32.11193'); +INSERT INTO `yoshop_region` VALUES ('2875', '2870', '安多', '安多县', '中国,西藏自治区,那曲地区,安多县', '3', 'anduo', '0896', '853400', 'A', '91.6795', '32.26125'); +INSERT INTO `yoshop_region` VALUES ('2876', '2870', '申扎', '申扎县', '中国,西藏自治区,那曲地区,申扎县', '3', 'shenzha', '0896', '853100', 'S', '88.70776', '30.92995'); +INSERT INTO `yoshop_region` VALUES ('2877', '2870', '索县', '索县', '中国,西藏自治区,那曲地区,索县', '3', 'suoxian', '0896', '852200', 'S', '93.78295', '31.88427'); +INSERT INTO `yoshop_region` VALUES ('2878', '2870', '班戈', '班戈县', '中国,西藏自治区,那曲地区,班戈县', '3', 'bange', '0896', '852500', 'B', '90.01907', '31.36149'); +INSERT INTO `yoshop_region` VALUES ('2879', '2870', '巴青', '巴青县', '中国,西藏自治区,那曲地区,巴青县', '3', 'baqing', '0896', '852100', 'B', '94.05316', '31.91833'); +INSERT INTO `yoshop_region` VALUES ('2880', '2870', '尼玛', '尼玛县', '中国,西藏自治区,那曲地区,尼玛县', '3', 'nima', '0896', '852600', 'N', '87.25256', '31.79654'); +INSERT INTO `yoshop_region` VALUES ('2881', '2870', '双湖', '双湖县', '中国,西藏自治区,那曲地区,双湖县', '3', 'shuanghu', '0896', '852600', 'S', '88.837776', '33.189032'); +INSERT INTO `yoshop_region` VALUES ('2882', '2816', '阿里', '阿里地区', '中国,西藏自治区,阿里地区', '2', 'ngari', '0897', '859000', 'A', '80.105498', '32.503187'); +INSERT INTO `yoshop_region` VALUES ('2883', '2882', '普兰', '普兰县', '中国,西藏自治区,阿里地区,普兰县', '3', 'pulan', '0897', '859500', 'P', '81.177', '30.30002'); +INSERT INTO `yoshop_region` VALUES ('2884', '2882', '札达', '札达县', '中国,西藏自治区,阿里地区,札达县', '3', 'zhada', '0897', '859600', 'Z', '79.80255', '31.48345'); +INSERT INTO `yoshop_region` VALUES ('2885', '2882', '噶尔', '噶尔县', '中国,西藏自治区,阿里地区,噶尔县', '3', 'gaer', '0897', '859400', 'G', '80.09579', '32.50024'); +INSERT INTO `yoshop_region` VALUES ('2886', '2882', '日土', '日土县', '中国,西藏自治区,阿里地区,日土县', '3', 'ritu', '0897', '859700', 'R', '79.7131', '33.38741'); +INSERT INTO `yoshop_region` VALUES ('2887', '2882', '革吉', '革吉县', '中国,西藏自治区,阿里地区,革吉县', '3', 'geji', '0897', '859100', 'G', '81.151', '32.3964'); +INSERT INTO `yoshop_region` VALUES ('2888', '2882', '改则', '改则县', '中国,西藏自治区,阿里地区,改则县', '3', 'gaize', '0897', '859200', 'G', '84.06295', '32.30446'); +INSERT INTO `yoshop_region` VALUES ('2889', '2882', '措勤', '措勤县', '中国,西藏自治区,阿里地区,措勤县', '3', 'cuoqin', '0897', '859300', 'C', '85.16616', '31.02095'); +INSERT INTO `yoshop_region` VALUES ('2890', '2816', '林芝', '林芝地区', '中国,西藏自治区,林芝地区', '2', 'nyingchi', '0894', '850400', 'L', '94.362348', '29.654693'); +INSERT INTO `yoshop_region` VALUES ('2891', '2890', '林芝', '林芝县', '中国,西藏自治区,林芝地区,林芝县', '3', 'linzhi', '0894', '850400', 'L', '94.48391', '29.57562'); +INSERT INTO `yoshop_region` VALUES ('2892', '2890', '工布江达', '工布江达县', '中国,西藏自治区,林芝地区,工布江达县', '3', 'gongbujiangda', '0894', '850300', 'G', '93.2452', '29.88576'); +INSERT INTO `yoshop_region` VALUES ('2893', '2890', '米林', '米林县', '中国,西藏自治区,林芝地区,米林县', '3', 'milin', '0894', '850500', 'M', '94.21316', '29.21535'); +INSERT INTO `yoshop_region` VALUES ('2894', '2890', '墨脱', '墨脱县', '中国,西藏自治区,林芝地区,墨脱县', '3', 'motuo', '0894', '855300', 'M', '95.3316', '29.32698'); +INSERT INTO `yoshop_region` VALUES ('2895', '2890', '波密', '波密县', '中国,西藏自治区,林芝地区,波密县', '3', 'bomi', '0894', '855200', 'B', '95.77096', '29.85907'); +INSERT INTO `yoshop_region` VALUES ('2896', '2890', '察隅', '察隅县', '中国,西藏自治区,林芝地区,察隅县', '3', 'chayu', '0894', '855100', 'C', '97.46679', '28.6618'); +INSERT INTO `yoshop_region` VALUES ('2897', '2890', '朗县', '朗县', '中国,西藏自治区,林芝地区,朗县', '3', 'langxian', '0894', '856500', 'L', '93.0754', '29.04549'); +INSERT INTO `yoshop_region` VALUES ('2898', '0', '陕西', '陕西省', '中国,陕西省', '1', 'shaanxi', '', '', 'S', '108.948024', '34.263161'); +INSERT INTO `yoshop_region` VALUES ('2899', '2898', '西安', '西安市', '中国,陕西省,西安市', '2', 'xi\'an', '029', '710003', 'X', '108.948024', '34.263161'); +INSERT INTO `yoshop_region` VALUES ('2900', '2899', '新城', '新城区', '中国,陕西省,西安市,新城区', '3', 'xincheng', '029', '710004', 'X', '108.9608', '34.26641'); +INSERT INTO `yoshop_region` VALUES ('2901', '2899', '碑林', '碑林区', '中国,陕西省,西安市,碑林区', '3', 'beilin', '029', '710001', 'B', '108.93426', '34.2304'); +INSERT INTO `yoshop_region` VALUES ('2902', '2899', '莲湖', '莲湖区', '中国,陕西省,西安市,莲湖区', '3', 'lianhu', '029', '710003', 'L', '108.9401', '34.26709'); +INSERT INTO `yoshop_region` VALUES ('2903', '2899', '灞桥', '灞桥区', '中国,陕西省,西安市,灞桥区', '3', 'baqiao', '029', '710038', null, '109.06451', '34.27264'); +INSERT INTO `yoshop_region` VALUES ('2904', '2899', '未央', '未央区', '中国,陕西省,西安市,未央区', '3', 'weiyang', '029', '710014', 'W', '108.94683', '34.29296'); +INSERT INTO `yoshop_region` VALUES ('2905', '2899', '雁塔', '雁塔区', '中国,陕西省,西安市,雁塔区', '3', 'yanta', '029', '710061', 'Y', '108.94866', '34.22245'); +INSERT INTO `yoshop_region` VALUES ('2906', '2899', '阎良', '阎良区', '中国,陕西省,西安市,阎良区', '3', 'yanliang', '029', '710087', 'Y', '109.22616', '34.66221'); +INSERT INTO `yoshop_region` VALUES ('2907', '2899', '临潼', '临潼区', '中国,陕西省,西安市,临潼区', '3', 'lintong', '029', '710600', 'L', '109.21417', '34.36665'); +INSERT INTO `yoshop_region` VALUES ('2908', '2899', '长安', '长安区', '中国,陕西省,西安市,长安区', '3', 'chang\'an', '029', '710100', 'C', '108.94586', '34.15559'); +INSERT INTO `yoshop_region` VALUES ('2909', '2899', '蓝田', '蓝田县', '中国,陕西省,西安市,蓝田县', '3', 'lantian', '029', '710500', 'L', '109.32339', '34.15128'); +INSERT INTO `yoshop_region` VALUES ('2910', '2899', '周至', '周至县', '中国,陕西省,西安市,周至县', '3', 'zhouzhi', '029', '710400', 'Z', '108.22207', '34.16337'); +INSERT INTO `yoshop_region` VALUES ('2911', '2899', '户县', '户县', '中国,陕西省,西安市,户县', '3', 'huxian', '029', '710300', 'H', '108.60513', '34.10856'); +INSERT INTO `yoshop_region` VALUES ('2912', '2899', '高陵', '高陵区', '中国,陕西省,西安市,高陵区', '3', 'gaoling', '029', '710200', 'G', '109.08816', '34.53483'); +INSERT INTO `yoshop_region` VALUES ('2913', '2898', '铜川', '铜川市', '中国,陕西省,铜川市', '2', 'tongchuan', '0919', '727100', 'T', '108.963122', '34.90892'); +INSERT INTO `yoshop_region` VALUES ('2914', '2913', '王益', '王益区', '中国,陕西省,铜川市,王益区', '3', 'wangyi', '0919', '727000', 'W', '109.07564', '35.06896'); +INSERT INTO `yoshop_region` VALUES ('2915', '2913', '印台', '印台区', '中国,陕西省,铜川市,印台区', '3', 'yintai', '0919', '727007', 'Y', '109.10208', '35.1169'); +INSERT INTO `yoshop_region` VALUES ('2916', '2913', '耀州', '耀州区', '中国,陕西省,铜川市,耀州区', '3', 'yaozhou', '0919', '727100', 'Y', '108.98556', '34.91308'); +INSERT INTO `yoshop_region` VALUES ('2917', '2913', '宜君', '宜君县', '中国,陕西省,铜川市,宜君县', '3', 'yijun', '0919', '727200', 'Y', '109.11813', '35.40108'); +INSERT INTO `yoshop_region` VALUES ('2918', '2898', '宝鸡', '宝鸡市', '中国,陕西省,宝鸡市', '2', 'baoji', '0917', '721000', 'B', '107.14487', '34.369315'); +INSERT INTO `yoshop_region` VALUES ('2919', '2918', '渭滨', '渭滨区', '中国,陕西省,宝鸡市,渭滨区', '3', 'weibin', '0917', '721000', 'W', '107.14991', '34.37116'); +INSERT INTO `yoshop_region` VALUES ('2920', '2918', '金台', '金台区', '中国,陕西省,宝鸡市,金台区', '3', 'jintai', '0917', '721000', 'J', '107.14691', '34.37612'); +INSERT INTO `yoshop_region` VALUES ('2921', '2918', '陈仓', '陈仓区', '中国,陕西省,宝鸡市,陈仓区', '3', 'chencang', '0917', '721300', 'C', '107.38742', '34.35451'); +INSERT INTO `yoshop_region` VALUES ('2922', '2918', '凤翔', '凤翔县', '中国,陕西省,宝鸡市,凤翔县', '3', 'fengxiang', '0917', '721400', 'F', '107.39645', '34.52321'); +INSERT INTO `yoshop_region` VALUES ('2923', '2918', '岐山', '岐山县', '中国,陕西省,宝鸡市,岐山县', '3', 'qishan', '0917', '722400', null, '107.62173', '34.44378'); +INSERT INTO `yoshop_region` VALUES ('2924', '2918', '扶风', '扶风县', '中国,陕西省,宝鸡市,扶风县', '3', 'fufeng', '0917', '722200', 'F', '107.90017', '34.37524'); +INSERT INTO `yoshop_region` VALUES ('2925', '2918', '眉县', '眉县', '中国,陕西省,宝鸡市,眉县', '3', 'meixian', '0917', '722300', 'M', '107.75079', '34.27569'); +INSERT INTO `yoshop_region` VALUES ('2926', '2918', '陇县', '陇县', '中国,陕西省,宝鸡市,陇县', '3', 'longxian', '0917', '721200', 'L', '106.85946', '34.89404'); +INSERT INTO `yoshop_region` VALUES ('2927', '2918', '千阳', '千阳县', '中国,陕西省,宝鸡市,千阳县', '3', 'qianyang', '0917', '721100', 'Q', '107.13043', '34.64219'); +INSERT INTO `yoshop_region` VALUES ('2928', '2918', '麟游', '麟游县', '中国,陕西省,宝鸡市,麟游县', '3', 'linyou', '0917', '721500', null, '107.79623', '34.67844'); +INSERT INTO `yoshop_region` VALUES ('2929', '2918', '凤县', '凤县', '中国,陕西省,宝鸡市,凤县', '3', 'fengxian', '0917', '721700', 'F', '106.52356', '33.91172'); +INSERT INTO `yoshop_region` VALUES ('2930', '2918', '太白', '太白县', '中国,陕西省,宝鸡市,太白县', '3', 'taibai', '0917', '721600', 'T', '107.31646', '34.06207'); +INSERT INTO `yoshop_region` VALUES ('2931', '2898', '咸阳', '咸阳市', '中国,陕西省,咸阳市', '2', 'xianyang', '029', '712000', 'X', '108.705117', '34.333439'); +INSERT INTO `yoshop_region` VALUES ('2932', '2931', '秦都', '秦都区', '中国,陕西省,咸阳市,秦都区', '3', 'qindu', '029', '712000', 'Q', '108.71493', '34.33804'); +INSERT INTO `yoshop_region` VALUES ('2933', '2931', '杨陵', '杨陵区', '中国,陕西省,咸阳市,杨陵区', '3', 'yangling', '029', '712100', 'Y', '108.083481', '34.270434'); +INSERT INTO `yoshop_region` VALUES ('2934', '2931', '渭城', '渭城区', '中国,陕西省,咸阳市,渭城区', '3', 'weicheng', '029', '712000', 'W', '108.72227', '34.33198'); +INSERT INTO `yoshop_region` VALUES ('2935', '2931', '三原', '三原县', '中国,陕西省,咸阳市,三原县', '3', 'sanyuan', '029', '713800', 'S', '108.93194', '34.61556'); +INSERT INTO `yoshop_region` VALUES ('2936', '2931', '泾阳', '泾阳县', '中国,陕西省,咸阳市,泾阳县', '3', 'jingyang', '029', '713700', null, '108.84259', '34.52705'); +INSERT INTO `yoshop_region` VALUES ('2937', '2931', '乾县', '乾县', '中国,陕西省,咸阳市,乾县', '3', 'qianxian', '029', '713300', 'Q', '108.24231', '34.52946'); +INSERT INTO `yoshop_region` VALUES ('2938', '2931', '礼泉', '礼泉县', '中国,陕西省,咸阳市,礼泉县', '3', 'liquan', '029', '713200', 'L', '108.4263', '34.48455'); +INSERT INTO `yoshop_region` VALUES ('2939', '2931', '永寿', '永寿县', '中国,陕西省,咸阳市,永寿县', '3', 'yongshou', '029', '713400', 'Y', '108.14474', '34.69081'); +INSERT INTO `yoshop_region` VALUES ('2940', '2931', '彬县', '彬县', '中国,陕西省,咸阳市,彬县', '3', 'binxian', '029', '713500', 'B', '108.08468', '35.0342'); +INSERT INTO `yoshop_region` VALUES ('2941', '2931', '长武', '长武县', '中国,陕西省,咸阳市,长武县', '3', 'changwu', '029', '713600', 'C', '107.7951', '35.2067'); +INSERT INTO `yoshop_region` VALUES ('2942', '2931', '旬邑', '旬邑县', '中国,陕西省,咸阳市,旬邑县', '3', 'xunyi', '029', '711300', 'X', '108.3341', '35.11338'); +INSERT INTO `yoshop_region` VALUES ('2943', '2931', '淳化', '淳化县', '中国,陕西省,咸阳市,淳化县', '3', 'chunhua', '029', '711200', 'C', '108.58026', '34.79886'); +INSERT INTO `yoshop_region` VALUES ('2944', '2931', '武功', '武功县', '中国,陕西省,咸阳市,武功县', '3', 'wugong', '029', '712200', 'W', '108.20434', '34.26003'); +INSERT INTO `yoshop_region` VALUES ('2945', '2931', '兴平', '兴平市', '中国,陕西省,咸阳市,兴平市', '3', 'xingping', '029', '713100', 'X', '108.49057', '34.29785'); +INSERT INTO `yoshop_region` VALUES ('2946', '2898', '渭南', '渭南市', '中国,陕西省,渭南市', '2', 'weinan', '0913', '714000', 'W', '109.502882', '34.499381'); +INSERT INTO `yoshop_region` VALUES ('2947', '2946', '临渭', '临渭区', '中国,陕西省,渭南市,临渭区', '3', 'linwei', '0913', '714000', 'L', '109.49296', '34.49822'); +INSERT INTO `yoshop_region` VALUES ('2948', '2946', '华县', '华县', '中国,陕西省,渭南市,华县', '3', 'huaxian', '0913', '714100', 'H', '109.77185', '34.51255'); +INSERT INTO `yoshop_region` VALUES ('2949', '2946', '潼关', '潼关县', '中国,陕西省,渭南市,潼关县', '3', 'tongguan', '0913', '714300', null, '110.24362', '34.54284'); +INSERT INTO `yoshop_region` VALUES ('2950', '2946', '大荔', '大荔县', '中国,陕西省,渭南市,大荔县', '3', 'dali', '0913', '715100', 'D', '109.94216', '34.79565'); +INSERT INTO `yoshop_region` VALUES ('2951', '2946', '合阳', '合阳县', '中国,陕西省,渭南市,合阳县', '3', 'heyang', '0913', '715300', 'H', '110.14862', '35.23805'); +INSERT INTO `yoshop_region` VALUES ('2952', '2946', '澄城', '澄城县', '中国,陕西省,渭南市,澄城县', '3', 'chengcheng', '0913', '715200', 'C', '109.93444', '35.18396'); +INSERT INTO `yoshop_region` VALUES ('2953', '2946', '蒲城', '蒲城县', '中国,陕西省,渭南市,蒲城县', '3', 'pucheng', '0913', '715500', 'P', '109.5903', '34.9568'); +INSERT INTO `yoshop_region` VALUES ('2954', '2946', '白水', '白水县', '中国,陕西省,渭南市,白水县', '3', 'baishui', '0913', '715600', 'B', '109.59286', '35.17863'); +INSERT INTO `yoshop_region` VALUES ('2955', '2946', '富平', '富平县', '中国,陕西省,渭南市,富平县', '3', 'fuping', '0913', '711700', 'F', '109.1802', '34.75109'); +INSERT INTO `yoshop_region` VALUES ('2956', '2946', '韩城', '韩城市', '中国,陕西省,渭南市,韩城市', '3', 'hancheng', '0913', '715400', 'H', '110.44238', '35.47926'); +INSERT INTO `yoshop_region` VALUES ('2957', '2946', '华阴', '华阴市', '中国,陕西省,渭南市,华阴市', '3', 'huayin', '0913', '714200', 'H', '110.08752', '34.56608'); +INSERT INTO `yoshop_region` VALUES ('2958', '2898', '延安', '延安市', '中国,陕西省,延安市', '2', 'yan\'an', '0911', '716000', 'Y', '109.49081', '36.596537'); +INSERT INTO `yoshop_region` VALUES ('2959', '2958', '宝塔', '宝塔区', '中国,陕西省,延安市,宝塔区', '3', 'baota', '0911', '716000', 'B', '109.49336', '36.59154'); +INSERT INTO `yoshop_region` VALUES ('2960', '2958', '延长', '延长县', '中国,陕西省,延安市,延长县', '3', 'yanchang', '0911', '717100', 'Y', '110.01083', '36.57904'); +INSERT INTO `yoshop_region` VALUES ('2961', '2958', '延川', '延川县', '中国,陕西省,延安市,延川县', '3', 'yanchuan', '0911', '717200', 'Y', '110.19415', '36.87817'); +INSERT INTO `yoshop_region` VALUES ('2962', '2958', '子长', '子长县', '中国,陕西省,延安市,子长县', '3', 'zichang', '0911', '717300', 'Z', '109.67532', '37.14253'); +INSERT INTO `yoshop_region` VALUES ('2963', '2958', '安塞', '安塞县', '中国,陕西省,延安市,安塞县', '3', 'ansai', '0911', '717400', 'A', '109.32708', '36.86507'); +INSERT INTO `yoshop_region` VALUES ('2964', '2958', '志丹', '志丹县', '中国,陕西省,延安市,志丹县', '3', 'zhidan', '0911', '717500', 'Z', '108.76815', '36.82177'); +INSERT INTO `yoshop_region` VALUES ('2965', '2958', '吴起', '吴起县', '中国,陕西省,延安市,吴起县', '3', 'wuqi', '0911', '717600', 'W', '108.17611', '36.92785'); +INSERT INTO `yoshop_region` VALUES ('2966', '2958', '甘泉', '甘泉县', '中国,陕西省,延安市,甘泉县', '3', 'ganquan', '0911', '716100', 'G', '109.35012', '36.27754'); +INSERT INTO `yoshop_region` VALUES ('2967', '2958', '富县', '富县', '中国,陕西省,延安市,富县', '3', 'fuxian', '0911', '727500', 'F', '109.37927', '35.98803'); +INSERT INTO `yoshop_region` VALUES ('2968', '2958', '洛川', '洛川县', '中国,陕西省,延安市,洛川县', '3', 'luochuan', '0911', '727400', 'L', '109.43286', '35.76076'); +INSERT INTO `yoshop_region` VALUES ('2969', '2958', '宜川', '宜川县', '中国,陕西省,延安市,宜川县', '3', 'yichuan', '0911', '716200', 'Y', '110.17196', '36.04732'); +INSERT INTO `yoshop_region` VALUES ('2970', '2958', '黄龙', '黄龙县', '中国,陕西省,延安市,黄龙县', '3', 'huanglong', '0911', '715700', 'H', '109.84259', '35.58349'); +INSERT INTO `yoshop_region` VALUES ('2971', '2958', '黄陵', '黄陵县', '中国,陕西省,延安市,黄陵县', '3', 'huangling', '0911', '727300', 'H', '109.26333', '35.58357'); +INSERT INTO `yoshop_region` VALUES ('2972', '2898', '汉中', '汉中市', '中国,陕西省,汉中市', '2', 'hanzhong', '0916', '723000', 'H', '107.028621', '33.077668'); +INSERT INTO `yoshop_region` VALUES ('2973', '2972', '汉台', '汉台区', '中国,陕西省,汉中市,汉台区', '3', 'hantai', '0916', '723000', 'H', '107.03187', '33.06774'); +INSERT INTO `yoshop_region` VALUES ('2974', '2972', '南郑', '南郑县', '中国,陕西省,汉中市,南郑县', '3', 'nanzheng', '0916', '723100', 'N', '106.94024', '33.00299'); +INSERT INTO `yoshop_region` VALUES ('2975', '2972', '城固', '城固县', '中国,陕西省,汉中市,城固县', '3', 'chenggu', '0916', '723200', 'C', '107.33367', '33.15661'); +INSERT INTO `yoshop_region` VALUES ('2976', '2972', '洋县', '洋县', '中国,陕西省,汉中市,洋县', '3', 'yangxian', '0916', '723300', 'Y', '107.54672', '33.22102'); +INSERT INTO `yoshop_region` VALUES ('2977', '2972', '西乡', '西乡县', '中国,陕西省,汉中市,西乡县', '3', 'xixiang', '0916', '723500', 'X', '107.76867', '32.98411'); +INSERT INTO `yoshop_region` VALUES ('2978', '2972', '勉县', '勉县', '中国,陕西省,汉中市,勉县', '3', 'mianxian', '0916', '724200', 'M', '106.67584', '33.15273'); +INSERT INTO `yoshop_region` VALUES ('2979', '2972', '宁强', '宁强县', '中国,陕西省,汉中市,宁强县', '3', 'ningqiang', '0916', '724400', 'N', '106.25958', '32.82881'); +INSERT INTO `yoshop_region` VALUES ('2980', '2972', '略阳', '略阳县', '中国,陕西省,汉中市,略阳县', '3', 'lueyang', '0916', '724300', 'L', '106.15399', '33.33009'); +INSERT INTO `yoshop_region` VALUES ('2981', '2972', '镇巴', '镇巴县', '中国,陕西省,汉中市,镇巴县', '3', 'zhenba', '0916', '723600', 'Z', '107.89648', '32.53487'); +INSERT INTO `yoshop_region` VALUES ('2982', '2972', '留坝', '留坝县', '中国,陕西省,汉中市,留坝县', '3', 'liuba', '0916', '724100', 'L', '106.92233', '33.61606'); +INSERT INTO `yoshop_region` VALUES ('2983', '2972', '佛坪', '佛坪县', '中国,陕西省,汉中市,佛坪县', '3', 'foping', '0916', '723400', 'F', '107.98974', '33.52496'); +INSERT INTO `yoshop_region` VALUES ('2984', '2898', '榆林', '榆林市', '中国,陕西省,榆林市', '2', 'yulin', '0912', '719000', 'Y', '109.741193', '38.290162'); +INSERT INTO `yoshop_region` VALUES ('2985', '2984', '榆阳', '榆阳区', '中国,陕西省,榆林市,榆阳区', '3', 'yuyang', '0912', '719000', 'Y', '109.73473', '38.27843'); +INSERT INTO `yoshop_region` VALUES ('2986', '2984', '神木', '神木县', '中国,陕西省,榆林市,神木县', '3', 'shenmu', '0912', '719300', 'S', '110.4989', '38.84234'); +INSERT INTO `yoshop_region` VALUES ('2987', '2984', '府谷', '府谷县', '中国,陕西省,榆林市,府谷县', '3', 'fugu', '0912', '719400', 'F', '111.06723', '39.02805'); +INSERT INTO `yoshop_region` VALUES ('2988', '2984', '横山', '横山县', '中国,陕西省,榆林市,横山县', '3', 'hengshan', '0912', '719100', 'H', '109.29568', '37.958'); +INSERT INTO `yoshop_region` VALUES ('2989', '2984', '靖边', '靖边县', '中国,陕西省,榆林市,靖边县', '3', 'jingbian', '0912', '718500', 'J', '108.79412', '37.59938'); +INSERT INTO `yoshop_region` VALUES ('2990', '2984', '定边', '定边县', '中国,陕西省,榆林市,定边县', '3', 'dingbian', '0912', '718600', 'D', '107.59793', '37.59037'); +INSERT INTO `yoshop_region` VALUES ('2991', '2984', '绥德', '绥德县', '中国,陕西省,榆林市,绥德县', '3', 'suide', '0912', '718000', 'S', '110.26126', '37.49778'); +INSERT INTO `yoshop_region` VALUES ('2992', '2984', '米脂', '米脂县', '中国,陕西省,榆林市,米脂县', '3', 'mizhi', '0912', '718100', 'M', '110.18417', '37.75529'); +INSERT INTO `yoshop_region` VALUES ('2993', '2984', '佳县', '佳县', '中国,陕西省,榆林市,佳县', '3', 'jiaxian', '0912', '719200', 'J', '110.49362', '38.02248'); +INSERT INTO `yoshop_region` VALUES ('2994', '2984', '吴堡', '吴堡县', '中国,陕西省,榆林市,吴堡县', '3', 'wubu', '0912', '718200', 'W', '110.74533', '37.45709'); +INSERT INTO `yoshop_region` VALUES ('2995', '2984', '清涧', '清涧县', '中国,陕西省,榆林市,清涧县', '3', 'qingjian', '0912', '718300', 'Q', '110.12173', '37.08854'); +INSERT INTO `yoshop_region` VALUES ('2996', '2984', '子洲', '子洲县', '中国,陕西省,榆林市,子洲县', '3', 'zizhou', '0912', '718400', 'Z', '110.03488', '37.61238'); +INSERT INTO `yoshop_region` VALUES ('2997', '2898', '安康', '安康市', '中国,陕西省,安康市', '2', 'ankang', '0915', '725000', 'A', '109.029273', '32.6903'); +INSERT INTO `yoshop_region` VALUES ('2998', '2997', '汉滨', '汉滨区', '中国,陕西省,安康市,汉滨区', '3', 'hanbin', '0915', '725000', 'H', '109.02683', '32.69517'); +INSERT INTO `yoshop_region` VALUES ('2999', '2997', '汉阴', '汉阴县', '中国,陕西省,安康市,汉阴县', '3', 'hanyin', '0915', '725100', 'H', '108.51098', '32.89129'); +INSERT INTO `yoshop_region` VALUES ('3000', '2997', '石泉', '石泉县', '中国,陕西省,安康市,石泉县', '3', 'shiquan', '0915', '725200', 'S', '108.24755', '33.03971'); +INSERT INTO `yoshop_region` VALUES ('3001', '2997', '宁陕', '宁陕县', '中国,陕西省,安康市,宁陕县', '3', 'ningshan', '0915', '711600', 'N', '108.31515', '33.31726'); +INSERT INTO `yoshop_region` VALUES ('3002', '2997', '紫阳', '紫阳县', '中国,陕西省,安康市,紫阳县', '3', 'ziyang', '0915', '725300', 'Z', '108.5368', '32.52115'); +INSERT INTO `yoshop_region` VALUES ('3003', '2997', '岚皋', '岚皋县', '中国,陕西省,安康市,岚皋县', '3', 'langao', '0915', '725400', null, '108.90289', '32.30794'); +INSERT INTO `yoshop_region` VALUES ('3004', '2997', '平利', '平利县', '中国,陕西省,安康市,平利县', '3', 'pingli', '0915', '725500', 'P', '109.35775', '32.39111'); +INSERT INTO `yoshop_region` VALUES ('3005', '2997', '镇坪', '镇坪县', '中国,陕西省,安康市,镇坪县', '3', 'zhenping', '0915', '725600', 'Z', '109.52456', '31.8833'); +INSERT INTO `yoshop_region` VALUES ('3006', '2997', '旬阳', '旬阳县', '中国,陕西省,安康市,旬阳县', '3', 'xunyang', '0915', '725700', 'X', '109.3619', '32.83207'); +INSERT INTO `yoshop_region` VALUES ('3007', '2997', '白河', '白河县', '中国,陕西省,安康市,白河县', '3', 'baihe', '0915', '725800', 'B', '110.11315', '32.80955'); +INSERT INTO `yoshop_region` VALUES ('3008', '2898', '商洛', '商洛市', '中国,陕西省,商洛市', '2', 'shangluo', '0914', '726000', 'S', '109.939776', '33.868319'); +INSERT INTO `yoshop_region` VALUES ('3009', '3008', '商州', '商州区', '中国,陕西省,商洛市,商州区', '3', 'shangzhou', '0914', '726000', 'S', '109.94126', '33.8627'); +INSERT INTO `yoshop_region` VALUES ('3010', '3008', '洛南', '洛南县', '中国,陕西省,商洛市,洛南县', '3', 'luonan', '0914', '726100', 'L', '110.14645', '34.08994'); +INSERT INTO `yoshop_region` VALUES ('3011', '3008', '丹凤', '丹凤县', '中国,陕西省,商洛市,丹凤县', '3', 'danfeng', '0914', '726200', 'D', '110.33486', '33.69468'); +INSERT INTO `yoshop_region` VALUES ('3012', '3008', '商南', '商南县', '中国,陕西省,商洛市,商南县', '3', 'shangnan', '0914', '726300', 'S', '110.88375', '33.52581'); +INSERT INTO `yoshop_region` VALUES ('3013', '3008', '山阳', '山阳县', '中国,陕西省,商洛市,山阳县', '3', 'shanyang', '0914', '726400', 'S', '109.88784', '33.52931'); +INSERT INTO `yoshop_region` VALUES ('3014', '3008', '镇安', '镇安县', '中国,陕西省,商洛市,镇安县', '3', 'zhen\'an', '0914', '711500', 'Z', '109.15374', '33.42366'); +INSERT INTO `yoshop_region` VALUES ('3015', '3008', '柞水', '柞水县', '中国,陕西省,商洛市,柞水县', '3', 'zhashui', '0914', '711400', 'Z', '109.11105', '33.6831'); +INSERT INTO `yoshop_region` VALUES ('3016', '2898', '西咸', '西咸新区', '中国,陕西省,西咸新区', '2', 'xixian', '029', '712000', 'X', '108.810654', '34.307144'); +INSERT INTO `yoshop_region` VALUES ('3017', '3016', '空港', '空港新城', '中国,陕西省,西咸新区,空港新城', '3', 'konggang', '0374', '461000', 'K', '108.760529', '34.440894'); +INSERT INTO `yoshop_region` VALUES ('3018', '3016', '沣东', '沣东新城', '中国,陕西省,西咸新区,沣东新城', '3', 'fengdong', '029', '710000', null, '108.82988', '34.267431'); +INSERT INTO `yoshop_region` VALUES ('3019', '3016', '秦汉', '秦汉新城', '中国,陕西省,西咸新区,秦汉新城', '3', 'qinhan', '029', '712000', 'Q', '108.83812', '34.386513'); +INSERT INTO `yoshop_region` VALUES ('3020', '3016', '沣西', '沣西新城', '中国,陕西省,西咸新区,沣西新城', '3', 'fengxi', '029', '710000', null, '108.71215', '34.190453'); +INSERT INTO `yoshop_region` VALUES ('3021', '3016', '泾河', '泾河新城', '中国,陕西省,西咸新区,泾河新城', '3', 'jinghe', '029', '713700', null, '109.049603', '34.460587'); +INSERT INTO `yoshop_region` VALUES ('3022', '0', '甘肃', '甘肃省', '中国,甘肃省', '1', 'gansu', '', '', 'G', '103.823557', '36.058039'); +INSERT INTO `yoshop_region` VALUES ('3023', '3022', '兰州', '兰州市', '中国,甘肃省,兰州市', '2', 'lanzhou', '0931', '730030', 'L', '103.823557', '36.058039'); +INSERT INTO `yoshop_region` VALUES ('3024', '3023', '城关', '城关区', '中国,甘肃省,兰州市,城关区', '3', 'chengguan', '0931', '730030', 'C', '103.8252', '36.05725'); +INSERT INTO `yoshop_region` VALUES ('3025', '3023', '七里河', '七里河区', '中国,甘肃省,兰州市,七里河区', '3', 'qilihe', '0931', '730050', 'Q', '103.78564', '36.06585'); +INSERT INTO `yoshop_region` VALUES ('3026', '3023', '西固', '西固区', '中国,甘肃省,兰州市,西固区', '3', 'xigu', '0931', '730060', 'X', '103.62811', '36.08858'); +INSERT INTO `yoshop_region` VALUES ('3027', '3023', '安宁', '安宁区', '中国,甘肃省,兰州市,安宁区', '3', 'anning', '0931', '730070', 'A', '103.7189', '36.10384'); +INSERT INTO `yoshop_region` VALUES ('3028', '3023', '红古', '红古区', '中国,甘肃省,兰州市,红古区', '3', 'honggu', '0931', '730084', 'H', '102.85955', '36.34537'); +INSERT INTO `yoshop_region` VALUES ('3029', '3023', '永登', '永登县', '中国,甘肃省,兰州市,永登县', '3', 'yongdeng', '0931', '730300', 'Y', '103.26055', '36.73522'); +INSERT INTO `yoshop_region` VALUES ('3030', '3023', '皋兰', '皋兰县', '中国,甘肃省,兰州市,皋兰县', '3', 'gaolan', '0931', '730200', 'G', '103.94506', '36.33215'); +INSERT INTO `yoshop_region` VALUES ('3031', '3023', '榆中', '榆中县', '中国,甘肃省,兰州市,榆中县', '3', 'yuzhong', '0931', '730100', 'Y', '104.1145', '35.84415'); +INSERT INTO `yoshop_region` VALUES ('3032', '3022', '嘉峪关', '嘉峪关市', '中国,甘肃省,嘉峪关市', '2', 'jiayuguan', '0937', '735100', 'J', '98.277304', '39.786529'); +INSERT INTO `yoshop_region` VALUES ('3033', '3032', '雄关', '雄关区', '中国,甘肃省,嘉峪关市,雄关区', '3', 'xiongguan', '0937', '735100', 'X', '98.277398', '39.77925'); +INSERT INTO `yoshop_region` VALUES ('3034', '3032', '长城', '长城区', '中国,甘肃省,嘉峪关市,长城区', '3', 'changcheng', '0937', '735106', 'C', '98.273523', '39.787431'); +INSERT INTO `yoshop_region` VALUES ('3035', '3032', '镜铁', '镜铁区', '中国,甘肃省,嘉峪关市,镜铁区', '3', 'jingtie', '0937', '735100', 'J', '98.277304', '39.786529'); +INSERT INTO `yoshop_region` VALUES ('3036', '3022', '金昌', '金昌市', '中国,甘肃省,金昌市', '2', 'jinchang', '0935', '737100', 'J', '102.187888', '38.514238'); +INSERT INTO `yoshop_region` VALUES ('3037', '3036', '金川', '金川区', '中国,甘肃省,金昌市,金川区', '3', 'jinchuan', '0935', '737100', 'J', '102.19376', '38.52101'); +INSERT INTO `yoshop_region` VALUES ('3038', '3036', '永昌', '永昌县', '中国,甘肃省,金昌市,永昌县', '3', 'yongchang', '0935', '737200', 'Y', '101.97222', '38.24711'); +INSERT INTO `yoshop_region` VALUES ('3039', '3022', '白银', '白银市', '中国,甘肃省,白银市', '2', 'baiyin', '0943', '730900', 'B', '104.173606', '36.54568'); +INSERT INTO `yoshop_region` VALUES ('3040', '3039', '白银', '白银区', '中国,甘肃省,白银市,白银区', '3', 'baiyin', '0943', '730900', 'B', '104.17355', '36.54411'); +INSERT INTO `yoshop_region` VALUES ('3041', '3039', '平川', '平川区', '中国,甘肃省,白银市,平川区', '3', 'pingchuan', '0943', '730913', 'P', '104.82498', '36.7277'); +INSERT INTO `yoshop_region` VALUES ('3042', '3039', '靖远', '靖远县', '中国,甘肃省,白银市,靖远县', '3', 'jingyuan', '0943', '730600', 'J', '104.68325', '36.56602'); +INSERT INTO `yoshop_region` VALUES ('3043', '3039', '会宁', '会宁县', '中国,甘肃省,白银市,会宁县', '3', 'huining', '0943', '730700', 'H', '105.05297', '35.69626'); +INSERT INTO `yoshop_region` VALUES ('3044', '3039', '景泰', '景泰县', '中国,甘肃省,白银市,景泰县', '3', 'jingtai', '0943', '730400', 'J', '104.06295', '37.18359'); +INSERT INTO `yoshop_region` VALUES ('3045', '3022', '天水', '天水市', '中国,甘肃省,天水市', '2', 'tianshui', '0938', '741000', 'T', '105.724998', '34.578529'); +INSERT INTO `yoshop_region` VALUES ('3046', '3045', '秦州', '秦州区', '中国,甘肃省,天水市,秦州区', '3', 'qinzhou', '0938', '741000', 'Q', '105.72421', '34.58089'); +INSERT INTO `yoshop_region` VALUES ('3047', '3045', '麦积', '麦积区', '中国,甘肃省,天水市,麦积区', '3', 'maiji', '0938', '741020', 'M', '105.89013', '34.57069'); +INSERT INTO `yoshop_region` VALUES ('3048', '3045', '清水', '清水县', '中国,甘肃省,天水市,清水县', '3', 'qingshui', '0938', '741400', 'Q', '106.13671', '34.75032'); +INSERT INTO `yoshop_region` VALUES ('3049', '3045', '秦安', '秦安县', '中国,甘肃省,天水市,秦安县', '3', 'qin\'an', '0938', '741600', 'Q', '105.66955', '34.85894'); +INSERT INTO `yoshop_region` VALUES ('3050', '3045', '甘谷', '甘谷县', '中国,甘肃省,天水市,甘谷县', '3', 'gangu', '0938', '741200', 'G', '105.33291', '34.73665'); +INSERT INTO `yoshop_region` VALUES ('3051', '3045', '武山', '武山县', '中国,甘肃省,天水市,武山县', '3', 'wushan', '0938', '741300', 'W', '104.88382', '34.72123'); +INSERT INTO `yoshop_region` VALUES ('3052', '3045', '张家川', '张家川回族自治县', '中国,甘肃省,天水市,张家川回族自治县', '3', 'zhangjiachuan', '0938', '741500', 'Z', '106.21582', '34.99582'); +INSERT INTO `yoshop_region` VALUES ('3053', '3022', '武威', '武威市', '中国,甘肃省,武威市', '2', 'wuwei', '0935', '733000', 'W', '102.634697', '37.929996'); +INSERT INTO `yoshop_region` VALUES ('3054', '3053', '凉州', '凉州区', '中国,甘肃省,武威市,凉州区', '3', 'liangzhou', '0935', '733000', 'L', '102.64203', '37.92832'); +INSERT INTO `yoshop_region` VALUES ('3055', '3053', '民勤', '民勤县', '中国,甘肃省,武威市,民勤县', '3', 'minqin', '0935', '733300', 'M', '103.09011', '38.62487'); +INSERT INTO `yoshop_region` VALUES ('3056', '3053', '古浪', '古浪县', '中国,甘肃省,武威市,古浪县', '3', 'gulang', '0935', '733100', 'G', '102.89154', '37.46508'); +INSERT INTO `yoshop_region` VALUES ('3057', '3053', '天祝', '天祝藏族自治县', '中国,甘肃省,武威市,天祝藏族自治县', '3', 'tianzhu', '0935', '733200', 'T', '103.1361', '36.97715'); +INSERT INTO `yoshop_region` VALUES ('3058', '3022', '张掖', '张掖市', '中国,甘肃省,张掖市', '2', 'zhangye', '0936', '734000', 'Z', '100.455472', '38.932897'); +INSERT INTO `yoshop_region` VALUES ('3059', '3058', '甘州', '甘州区', '中国,甘肃省,张掖市,甘州区', '3', 'ganzhou', '0936', '734000', 'G', '100.4527', '38.92947'); +INSERT INTO `yoshop_region` VALUES ('3060', '3058', '肃南', '肃南裕固族自治县', '中国,甘肃省,张掖市,肃南裕固族自治县', '3', 'sunan', '0936', '734400', 'S', '99.61407', '38.83776'); +INSERT INTO `yoshop_region` VALUES ('3061', '3058', '民乐', '民乐县', '中国,甘肃省,张掖市,民乐县', '3', 'minle', '0936', '734500', 'M', '100.81091', '38.43479'); +INSERT INTO `yoshop_region` VALUES ('3062', '3058', '临泽', '临泽县', '中国,甘肃省,张掖市,临泽县', '3', 'linze', '0936', '734200', 'L', '100.16445', '39.15252'); +INSERT INTO `yoshop_region` VALUES ('3063', '3058', '高台', '高台县', '中国,甘肃省,张掖市,高台县', '3', 'gaotai', '0936', '734300', 'G', '99.81918', '39.37829'); +INSERT INTO `yoshop_region` VALUES ('3064', '3058', '山丹', '山丹县', '中国,甘肃省,张掖市,山丹县', '3', 'shandan', '0936', '734100', 'S', '101.09359', '38.78468'); +INSERT INTO `yoshop_region` VALUES ('3065', '3022', '平凉', '平凉市', '中国,甘肃省,平凉市', '2', 'pingliang', '0933', '744000', 'P', '106.684691', '35.54279'); +INSERT INTO `yoshop_region` VALUES ('3066', '3065', '崆峒', '崆峒区', '中国,甘肃省,平凉市,崆峒区', '3', 'kongtong', '0933', '744000', null, '106.67483', '35.54262'); +INSERT INTO `yoshop_region` VALUES ('3067', '3065', '泾川', '泾川县', '中国,甘肃省,平凉市,泾川县', '3', 'jingchuan', '0933', '744300', null, '107.36581', '35.33223'); +INSERT INTO `yoshop_region` VALUES ('3068', '3065', '灵台', '灵台县', '中国,甘肃省,平凉市,灵台县', '3', 'lingtai', '0933', '744400', 'L', '107.6174', '35.06768'); +INSERT INTO `yoshop_region` VALUES ('3069', '3065', '崇信', '崇信县', '中国,甘肃省,平凉市,崇信县', '3', 'chongxin', '0933', '744200', 'C', '107.03738', '35.30344'); +INSERT INTO `yoshop_region` VALUES ('3070', '3065', '华亭', '华亭县', '中国,甘肃省,平凉市,华亭县', '3', 'huating', '0933', '744100', 'H', '106.65463', '35.2183'); +INSERT INTO `yoshop_region` VALUES ('3071', '3065', '庄浪', '庄浪县', '中国,甘肃省,平凉市,庄浪县', '3', 'zhuanglang', '0933', '744600', 'Z', '106.03662', '35.20235'); +INSERT INTO `yoshop_region` VALUES ('3072', '3065', '静宁', '静宁县', '中国,甘肃省,平凉市,静宁县', '3', 'jingning', '0933', '743400', 'J', '105.72723', '35.51991'); +INSERT INTO `yoshop_region` VALUES ('3073', '3022', '酒泉', '酒泉市', '中国,甘肃省,酒泉市', '2', 'jiuquan', '0937', '735000', 'J', '98.510795', '39.744023'); +INSERT INTO `yoshop_region` VALUES ('3074', '3073', '肃州', '肃州区', '中国,甘肃省,酒泉市,肃州区', '3', 'suzhou', '0937', '735000', 'S', '98.50775', '39.74506'); +INSERT INTO `yoshop_region` VALUES ('3075', '3073', '金塔', '金塔县', '中国,甘肃省,酒泉市,金塔县', '3', 'jinta', '0937', '735300', 'J', '98.90002', '39.97733'); +INSERT INTO `yoshop_region` VALUES ('3076', '3073', '瓜州', '瓜州县', '中国,甘肃省,酒泉市,瓜州县', '3', 'guazhou', '0937', '736100', 'G', '95.78271', '40.51548'); +INSERT INTO `yoshop_region` VALUES ('3077', '3073', '肃北', '肃北蒙古族自治县', '中国,甘肃省,酒泉市,肃北蒙古族自治县', '3', 'subei', '0937', '736300', 'S', '94.87649', '39.51214'); +INSERT INTO `yoshop_region` VALUES ('3078', '3073', '阿克塞', '阿克塞哈萨克族自治县', '中国,甘肃省,酒泉市,阿克塞哈萨克族自治县', '3', 'akesai', '0937', '736400', 'A', '94.34097', '39.63435'); +INSERT INTO `yoshop_region` VALUES ('3079', '3073', '玉门', '玉门市', '中国,甘肃省,酒泉市,玉门市', '3', 'yumen', '0937', '735200', 'Y', '97.04538', '40.29172'); +INSERT INTO `yoshop_region` VALUES ('3080', '3073', '敦煌', '敦煌市', '中国,甘肃省,酒泉市,敦煌市', '3', 'dunhuang', '0937', '736200', 'D', '94.66159', '40.14211'); +INSERT INTO `yoshop_region` VALUES ('3081', '3022', '庆阳', '庆阳市', '中国,甘肃省,庆阳市', '2', 'qingyang', '0934', '745000', 'Q', '107.638372', '35.734218'); +INSERT INTO `yoshop_region` VALUES ('3082', '3081', '西峰', '西峰区', '中国,甘肃省,庆阳市,西峰区', '3', 'xifeng', '0934', '745000', 'X', '107.65107', '35.73065'); +INSERT INTO `yoshop_region` VALUES ('3083', '3081', '庆城', '庆城县', '中国,甘肃省,庆阳市,庆城县', '3', 'qingcheng', '0934', '745100', 'Q', '107.88272', '36.01507'); +INSERT INTO `yoshop_region` VALUES ('3084', '3081', '环县', '环县', '中国,甘肃省,庆阳市,环县', '3', 'huanxian', '0934', '745700', 'H', '107.30835', '36.56846'); +INSERT INTO `yoshop_region` VALUES ('3085', '3081', '华池', '华池县', '中国,甘肃省,庆阳市,华池县', '3', 'huachi', '0934', '745600', 'H', '107.9891', '36.46108'); +INSERT INTO `yoshop_region` VALUES ('3086', '3081', '合水', '合水县', '中国,甘肃省,庆阳市,合水县', '3', 'heshui', '0934', '745400', 'H', '108.02032', '35.81911'); +INSERT INTO `yoshop_region` VALUES ('3087', '3081', '正宁', '正宁县', '中国,甘肃省,庆阳市,正宁县', '3', 'zhengning', '0934', '745300', 'Z', '108.36007', '35.49174'); +INSERT INTO `yoshop_region` VALUES ('3088', '3081', '宁县', '宁县', '中国,甘肃省,庆阳市,宁县', '3', 'ningxian', '0934', '745200', 'N', '107.92517', '35.50164'); +INSERT INTO `yoshop_region` VALUES ('3089', '3081', '镇原', '镇原县', '中国,甘肃省,庆阳市,镇原县', '3', 'zhenyuan', '0934', '744500', 'Z', '107.199', '35.67712'); +INSERT INTO `yoshop_region` VALUES ('3090', '3022', '定西', '定西市', '中国,甘肃省,定西市', '2', 'dingxi', '0932', '743000', 'D', '104.626294', '35.579578'); +INSERT INTO `yoshop_region` VALUES ('3091', '3090', '安定', '安定区', '中国,甘肃省,定西市,安定区', '3', 'anding', '0932', '743000', 'A', '104.6106', '35.58066'); +INSERT INTO `yoshop_region` VALUES ('3092', '3090', '通渭', '通渭县', '中国,甘肃省,定西市,通渭县', '3', 'tongwei', '0932', '743300', 'T', '105.24224', '35.21101'); +INSERT INTO `yoshop_region` VALUES ('3093', '3090', '陇西', '陇西县', '中国,甘肃省,定西市,陇西县', '3', 'longxi', '0932', '748100', 'L', '104.63446', '35.00238'); +INSERT INTO `yoshop_region` VALUES ('3094', '3090', '渭源', '渭源县', '中国,甘肃省,定西市,渭源县', '3', 'weiyuan', '0932', '748200', 'W', '104.21435', '35.13649'); +INSERT INTO `yoshop_region` VALUES ('3095', '3090', '临洮', '临洮县', '中国,甘肃省,定西市,临洮县', '3', 'lintao', '0932', '730500', 'L', '103.86196', '35.3751'); +INSERT INTO `yoshop_region` VALUES ('3096', '3090', '漳县', '漳县', '中国,甘肃省,定西市,漳县', '3', 'zhangxian', '0932', '748300', 'Z', '104.46704', '34.84977'); +INSERT INTO `yoshop_region` VALUES ('3097', '3090', '岷县', '岷县', '中国,甘肃省,定西市,岷县', '3', 'minxian', '0932', '748400', null, '104.03772', '34.43444'); +INSERT INTO `yoshop_region` VALUES ('3098', '3022', '陇南', '陇南市', '中国,甘肃省,陇南市', '2', 'longnan', '0939', '746000', 'L', '104.929379', '33.388598'); +INSERT INTO `yoshop_region` VALUES ('3099', '3098', '武都', '武都区', '中国,甘肃省,陇南市,武都区', '3', 'wudu', '0939', '746000', 'W', '104.92652', '33.39239'); +INSERT INTO `yoshop_region` VALUES ('3100', '3098', '成县', '成县', '中国,甘肃省,陇南市,成县', '3', 'chengxian', '0939', '742500', 'C', '105.72586', '33.73925'); +INSERT INTO `yoshop_region` VALUES ('3101', '3098', '文县', '文县', '中国,甘肃省,陇南市,文县', '3', 'wenxian', '0939', '746400', 'W', '104.68362', '32.94337'); +INSERT INTO `yoshop_region` VALUES ('3102', '3098', '宕昌', '宕昌县', '中国,甘肃省,陇南市,宕昌县', '3', 'dangchang', '0939', '748500', null, '104.39349', '34.04732'); +INSERT INTO `yoshop_region` VALUES ('3103', '3098', '康县', '康县', '中国,甘肃省,陇南市,康县', '3', 'kangxian', '0939', '746500', 'K', '105.60711', '33.32912'); +INSERT INTO `yoshop_region` VALUES ('3104', '3098', '西和', '西和县', '中国,甘肃省,陇南市,西和县', '3', 'xihe', '0939', '742100', 'X', '105.30099', '34.01432'); +INSERT INTO `yoshop_region` VALUES ('3105', '3098', '礼县', '礼县', '中国,甘肃省,陇南市,礼县', '3', 'lixian', '0939', '742200', 'L', '105.17785', '34.18935'); +INSERT INTO `yoshop_region` VALUES ('3106', '3098', '徽县', '徽县', '中国,甘肃省,陇南市,徽县', '3', 'huixian', '0939', '742300', 'H', '106.08529', '33.76898'); +INSERT INTO `yoshop_region` VALUES ('3107', '3098', '两当', '两当县', '中国,甘肃省,陇南市,两当县', '3', 'liangdang', '0939', '742400', 'L', '106.30484', '33.9096'); +INSERT INTO `yoshop_region` VALUES ('3108', '3022', '临夏', '临夏回族自治州', '中国,甘肃省,临夏回族自治州', '2', 'linxia', '0930', '731100', 'L', '103.212006', '35.599446'); +INSERT INTO `yoshop_region` VALUES ('3109', '3108', '临夏', '临夏市', '中国,甘肃省,临夏回族自治州,临夏市', '3', 'linxia', '0930', '731100', 'L', '103.21', '35.59916'); +INSERT INTO `yoshop_region` VALUES ('3110', '3108', '临夏', '临夏县', '中国,甘肃省,临夏回族自治州,临夏县', '3', 'linxia', '0930', '731800', 'L', '102.9938', '35.49519'); +INSERT INTO `yoshop_region` VALUES ('3111', '3108', '康乐', '康乐县', '中国,甘肃省,临夏回族自治州,康乐县', '3', 'kangle', '0930', '731500', 'K', '103.71093', '35.37219'); +INSERT INTO `yoshop_region` VALUES ('3112', '3108', '永靖', '永靖县', '中国,甘肃省,临夏回族自治州,永靖县', '3', 'yongjing', '0930', '731600', 'Y', '103.32043', '35.93835'); +INSERT INTO `yoshop_region` VALUES ('3113', '3108', '广河', '广河县', '中国,甘肃省,临夏回族自治州,广河县', '3', 'guanghe', '0930', '731300', 'G', '103.56933', '35.48097'); +INSERT INTO `yoshop_region` VALUES ('3114', '3108', '和政', '和政县', '中国,甘肃省,临夏回族自治州,和政县', '3', 'hezheng', '0930', '731200', 'H', '103.34936', '35.42592'); +INSERT INTO `yoshop_region` VALUES ('3115', '3108', '东乡族', '东乡族自治县', '中国,甘肃省,临夏回族自治州,东乡族自治县', '3', 'dongxiangzu', '0930', '731400', 'D', '103.39477', '35.66471'); +INSERT INTO `yoshop_region` VALUES ('3116', '3108', '积石山', '积石山保安族东乡族撒拉族自治县', '中国,甘肃省,临夏回族自治州,积石山保安族东乡族撒拉族自治县', '3', 'jishishan', '0930', '731700', 'J', '102.87374', '35.7182'); +INSERT INTO `yoshop_region` VALUES ('3117', '3022', '甘南', '甘南藏族自治州', '中国,甘肃省,甘南藏族自治州', '2', 'gannan', '0941', '747000', 'G', '102.911008', '34.986354'); +INSERT INTO `yoshop_region` VALUES ('3118', '3117', '合作', '合作市', '中国,甘肃省,甘南藏族自治州,合作市', '3', 'hezuo', '0941', '747000', 'H', '102.91082', '35.00016'); +INSERT INTO `yoshop_region` VALUES ('3119', '3117', '临潭', '临潭县', '中国,甘肃省,甘南藏族自治州,临潭县', '3', 'lintan', '0941', '747500', 'L', '103.35287', '34.69515'); +INSERT INTO `yoshop_region` VALUES ('3120', '3117', '卓尼', '卓尼县', '中国,甘肃省,甘南藏族自治州,卓尼县', '3', 'zhuoni', '0941', '747600', 'Z', '103.50811', '34.58919'); +INSERT INTO `yoshop_region` VALUES ('3121', '3117', '舟曲', '舟曲县', '中国,甘肃省,甘南藏族自治州,舟曲县', '3', 'zhouqu', '0941', '746300', 'Z', '104.37155', '33.78468'); +INSERT INTO `yoshop_region` VALUES ('3122', '3117', '迭部', '迭部县', '中国,甘肃省,甘南藏族自治州,迭部县', '3', 'diebu', '0941', '747400', 'D', '103.22274', '34.05623'); +INSERT INTO `yoshop_region` VALUES ('3123', '3117', '玛曲', '玛曲县', '中国,甘肃省,甘南藏族自治州,玛曲县', '3', 'maqu', '0941', '747300', 'M', '102.0754', '33.997'); +INSERT INTO `yoshop_region` VALUES ('3124', '3117', '碌曲', '碌曲县', '中国,甘肃省,甘南藏族自治州,碌曲县', '3', 'luqu', '0941', '747200', 'L', '102.49176', '34.58872'); +INSERT INTO `yoshop_region` VALUES ('3125', '3117', '夏河', '夏河县', '中国,甘肃省,甘南藏族自治州,夏河县', '3', 'xiahe', '0941', '747100', 'X', '102.52215', '35.20487'); +INSERT INTO `yoshop_region` VALUES ('3126', '0', '青海', '青海省', '中国,青海省', '1', 'qinghai', '', '', 'Q', '101.778916', '36.623178'); +INSERT INTO `yoshop_region` VALUES ('3127', '3126', '西宁', '西宁市', '中国,青海省,西宁市', '2', 'xining', '0971', '810000', 'X', '101.778916', '36.623178'); +INSERT INTO `yoshop_region` VALUES ('3128', '3127', '城东', '城东区', '中国,青海省,西宁市,城东区', '3', 'chengdong', '0971', '810007', 'C', '101.80373', '36.59969'); +INSERT INTO `yoshop_region` VALUES ('3129', '3127', '城中', '城中区', '中国,青海省,西宁市,城中区', '3', 'chengzhong', '0971', '810000', 'C', '101.78394', '36.62279'); +INSERT INTO `yoshop_region` VALUES ('3130', '3127', '城西', '城西区', '中国,青海省,西宁市,城西区', '3', 'chengxi', '0971', '810001', 'C', '101.76588', '36.62828'); +INSERT INTO `yoshop_region` VALUES ('3131', '3127', '城北', '城北区', '中国,青海省,西宁市,城北区', '3', 'chengbei', '0971', '810003', 'C', '101.7662', '36.65014'); +INSERT INTO `yoshop_region` VALUES ('3132', '3127', '大通', '大通回族土族自治县', '中国,青海省,西宁市,大通回族土族自治县', '3', 'datong', '0971', '810100', 'D', '101.70236', '36.93489'); +INSERT INTO `yoshop_region` VALUES ('3133', '3127', '湟中', '湟中县', '中国,青海省,西宁市,湟中县', '3', 'huangzhong', '0971', '811600', null, '101.57159', '36.50083'); +INSERT INTO `yoshop_region` VALUES ('3134', '3127', '湟源', '湟源县', '中国,青海省,西宁市,湟源县', '3', 'huangyuan', '0971', '812100', null, '101.25643', '36.68243'); +INSERT INTO `yoshop_region` VALUES ('3135', '3126', '海东', '海东市', '中国,青海省,海东市', '2', 'haidong', '0972', '810700', 'H', '102.10327', '36.502916'); +INSERT INTO `yoshop_region` VALUES ('3136', '3135', '乐都', '乐都区', '中国,青海省,海东市,乐都区', '3', 'ledu', '0972', '810700', 'L', '102.402431', '36.480291'); +INSERT INTO `yoshop_region` VALUES ('3137', '3135', '平安', '平安县', '中国,青海省,海东市,平安县', '3', 'ping\'an', '0972', '810600', 'P', '102.104295', '36.502714'); +INSERT INTO `yoshop_region` VALUES ('3138', '3135', '民和', '民和回族土族自治县', '中国,青海省,海东市,民和回族土族自治县', '3', 'minhe', '0972', '810800', 'M', '102.804209', '36.329451'); +INSERT INTO `yoshop_region` VALUES ('3139', '3135', '互助', '互助土族自治县', '中国,青海省,海东市,互助土族自治县', '3', 'huzhu', '0972', '810500', 'H', '101.956734', '36.83994'); +INSERT INTO `yoshop_region` VALUES ('3140', '3135', '化隆', '化隆回族自治县', '中国,青海省,海东市,化隆回族自治县', '3', 'hualong', '0972', '810900', 'H', '102.262329', '36.098322'); +INSERT INTO `yoshop_region` VALUES ('3141', '3135', '循化', '循化撒拉族自治县', '中国,青海省,海东市,循化撒拉族自治县', '3', 'xunhua', '0972', '811100', 'X', '102.486534', '35.847247'); +INSERT INTO `yoshop_region` VALUES ('3142', '3126', '海北', '海北藏族自治州', '中国,青海省,海北藏族自治州', '2', 'haibei', '0970', '812200', 'H', '100.901059', '36.959435'); +INSERT INTO `yoshop_region` VALUES ('3143', '3142', '门源', '门源回族自治县', '中国,青海省,海北藏族自治州,门源回族自治县', '3', 'menyuan', '0970', '810300', 'M', '101.62228', '37.37611'); +INSERT INTO `yoshop_region` VALUES ('3144', '3142', '祁连', '祁连县', '中国,青海省,海北藏族自治州,祁连县', '3', 'qilian', '0970', '810400', 'Q', '100.24618', '38.17901'); +INSERT INTO `yoshop_region` VALUES ('3145', '3142', '海晏', '海晏县', '中国,青海省,海北藏族自治州,海晏县', '3', 'haiyan', '0970', '812200', 'H', '100.9927', '36.89902'); +INSERT INTO `yoshop_region` VALUES ('3146', '3142', '刚察', '刚察县', '中国,青海省,海北藏族自治州,刚察县', '3', 'gangcha', '0970', '812300', 'G', '100.14675', '37.32161'); +INSERT INTO `yoshop_region` VALUES ('3147', '3126', '黄南', '黄南藏族自治州', '中国,青海省,黄南藏族自治州', '2', 'huangnan', '0973', '811300', 'H', '102.019988', '35.517744'); +INSERT INTO `yoshop_region` VALUES ('3148', '3147', '同仁', '同仁县', '中国,青海省,黄南藏族自治州,同仁县', '3', 'tongren', '0973', '811300', 'T', '102.0184', '35.51603'); +INSERT INTO `yoshop_region` VALUES ('3149', '3147', '尖扎', '尖扎县', '中国,青海省,黄南藏族自治州,尖扎县', '3', 'jianzha', '0973', '811200', 'J', '102.03411', '35.93947'); +INSERT INTO `yoshop_region` VALUES ('3150', '3147', '泽库', '泽库县', '中国,青海省,黄南藏族自治州,泽库县', '3', 'zeku', '0973', '811400', 'Z', '101.46444', '35.03519'); +INSERT INTO `yoshop_region` VALUES ('3151', '3147', '河南', '河南蒙古族自治县', '中国,青海省,黄南藏族自治州,河南蒙古族自治县', '3', 'henan', '0973', '811500', 'H', '101.60864', '34.73476'); +INSERT INTO `yoshop_region` VALUES ('3152', '3126', '海南', '海南藏族自治州', '中国,青海省,海南藏族自治州', '2', 'hainan', '0974', '813000', 'H', '100.619542', '36.280353'); +INSERT INTO `yoshop_region` VALUES ('3153', '3152', '共和', '共和县', '中国,青海省,海南藏族自治州,共和县', '3', 'gonghe', '0974', '813000', 'G', '100.62003', '36.2841'); +INSERT INTO `yoshop_region` VALUES ('3154', '3152', '同德', '同德县', '中国,青海省,海南藏族自治州,同德县', '3', 'tongde', '0974', '813200', 'T', '100.57159', '35.25488'); +INSERT INTO `yoshop_region` VALUES ('3155', '3152', '贵德', '贵德县', '中国,青海省,海南藏族自治州,贵德县', '3', 'guide', '0974', '811700', 'G', '101.432', '36.044'); +INSERT INTO `yoshop_region` VALUES ('3156', '3152', '兴海', '兴海县', '中国,青海省,海南藏族自治州,兴海县', '3', 'xinghai', '0974', '813300', 'X', '99.98846', '35.59031'); +INSERT INTO `yoshop_region` VALUES ('3157', '3152', '贵南', '贵南县', '中国,青海省,海南藏族自治州,贵南县', '3', 'guinan', '0974', '813100', 'G', '100.74716', '35.58667'); +INSERT INTO `yoshop_region` VALUES ('3158', '3126', '果洛', '果洛藏族自治州', '中国,青海省,果洛藏族自治州', '2', 'golog', '0975', '814000', 'G', '100.242143', '34.4736'); +INSERT INTO `yoshop_region` VALUES ('3159', '3158', '玛沁', '玛沁县', '中国,青海省,果洛藏族自治州,玛沁县', '3', 'maqin', '0975', '814000', 'M', '100.23901', '34.47746'); +INSERT INTO `yoshop_region` VALUES ('3160', '3158', '班玛', '班玛县', '中国,青海省,果洛藏族自治州,班玛县', '3', 'banma', '0975', '814300', 'B', '100.73745', '32.93253'); +INSERT INTO `yoshop_region` VALUES ('3161', '3158', '甘德', '甘德县', '中国,青海省,果洛藏族自治州,甘德县', '3', 'gande', '0975', '814100', 'G', '99.90246', '33.96838'); +INSERT INTO `yoshop_region` VALUES ('3162', '3158', '达日', '达日县', '中国,青海省,果洛藏族自治州,达日县', '3', 'dari', '0975', '814200', 'D', '99.65179', '33.75193'); +INSERT INTO `yoshop_region` VALUES ('3163', '3158', '久治', '久治县', '中国,青海省,果洛藏族自治州,久治县', '3', 'jiuzhi', '0975', '624700', 'J', '101.48342', '33.42989'); +INSERT INTO `yoshop_region` VALUES ('3164', '3158', '玛多', '玛多县', '中国,青海省,果洛藏族自治州,玛多县', '3', 'maduo', '0975', '813500', 'M', '98.20996', '34.91567'); +INSERT INTO `yoshop_region` VALUES ('3165', '3126', '玉树', '玉树藏族自治州', '中国,青海省,玉树藏族自治州', '2', 'yushu', '0976', '815000', 'Y', '97.008522', '33.004049'); +INSERT INTO `yoshop_region` VALUES ('3166', '3165', '玉树', '玉树市', '中国,青海省,玉树藏族自治州,玉树市', '3', 'yushu', '0976', '815000', 'Y', '97.008762', '33.00393'); +INSERT INTO `yoshop_region` VALUES ('3167', '3165', '杂多', '杂多县', '中国,青海省,玉树藏族自治州,杂多县', '3', 'zaduo', '0976', '815300', 'Z', '95.29864', '32.89318'); +INSERT INTO `yoshop_region` VALUES ('3168', '3165', '称多', '称多县', '中国,青海省,玉树藏族自治州,称多县', '3', 'chenduo', '0976', '815100', 'C', '97.10788', '33.36899'); +INSERT INTO `yoshop_region` VALUES ('3169', '3165', '治多', '治多县', '中国,青海省,玉树藏族自治州,治多县', '3', 'zhiduo', '0976', '815400', 'Z', '95.61572', '33.8528'); +INSERT INTO `yoshop_region` VALUES ('3170', '3165', '囊谦', '囊谦县', '中国,青海省,玉树藏族自治州,囊谦县', '3', 'nangqian', '0976', '815200', 'N', '96.47753', '32.20359'); +INSERT INTO `yoshop_region` VALUES ('3171', '3165', '曲麻莱', '曲麻莱县', '中国,青海省,玉树藏族自治州,曲麻莱县', '3', 'qumalai', '0976', '815500', 'Q', '95.79757', '34.12609'); +INSERT INTO `yoshop_region` VALUES ('3172', '3126', '海西', '海西蒙古族藏族自治州', '中国,青海省,海西蒙古族藏族自治州', '2', 'haixi', '0977', '817000', 'H', '97.370785', '37.374663'); +INSERT INTO `yoshop_region` VALUES ('3173', '3172', '格尔木', '格尔木市', '中国,青海省,海西蒙古族藏族自治州,格尔木市', '3', 'geermu', '0977', '816000', 'G', '94.90329', '36.40236'); +INSERT INTO `yoshop_region` VALUES ('3174', '3172', '德令哈', '德令哈市', '中国,青海省,海西蒙古族藏族自治州,德令哈市', '3', 'delingha', '0977', '817000', 'D', '97.36084', '37.36946'); +INSERT INTO `yoshop_region` VALUES ('3175', '3172', '乌兰', '乌兰县', '中国,青海省,海西蒙古族藏族自治州,乌兰县', '3', 'wulan', '0977', '817100', 'W', '98.48196', '36.93471'); +INSERT INTO `yoshop_region` VALUES ('3176', '3172', '都兰', '都兰县', '中国,青海省,海西蒙古族藏族自治州,都兰县', '3', 'dulan', '0977', '816100', 'D', '98.09228', '36.30135'); +INSERT INTO `yoshop_region` VALUES ('3177', '3172', '天峻', '天峻县', '中国,青海省,海西蒙古族藏族自治州,天峻县', '3', 'tianjun', '0977', '817200', 'T', '99.02453', '37.30326'); +INSERT INTO `yoshop_region` VALUES ('3178', '0', '宁夏', '宁夏回族自治区', '中国,宁夏回族自治区', '1', 'ningxia', '', '', 'N', '106.278179', '38.46637'); +INSERT INTO `yoshop_region` VALUES ('3179', '3178', '银川', '银川市', '中国,宁夏回族自治区,银川市', '2', 'yinchuan', '0951', '750004', 'Y', '106.278179', '38.46637'); +INSERT INTO `yoshop_region` VALUES ('3180', '3179', '兴庆', '兴庆区', '中国,宁夏回族自治区,银川市,兴庆区', '3', 'xingqing', '0951', '750001', 'X', '106.28872', '38.47392'); +INSERT INTO `yoshop_region` VALUES ('3181', '3179', '西夏', '西夏区', '中国,宁夏回族自治区,银川市,西夏区', '3', 'xixia', '0951', '750021', 'X', '106.15023', '38.49137'); +INSERT INTO `yoshop_region` VALUES ('3182', '3179', '金凤', '金凤区', '中国,宁夏回族自治区,银川市,金凤区', '3', 'jinfeng', '0951', '750011', 'J', '106.24261', '38.47294'); +INSERT INTO `yoshop_region` VALUES ('3183', '3179', '永宁', '永宁县', '中国,宁夏回族自治区,银川市,永宁县', '3', 'yongning', '0951', '750100', 'Y', '106.2517', '38.27559'); +INSERT INTO `yoshop_region` VALUES ('3184', '3179', '贺兰', '贺兰县', '中国,宁夏回族自治区,银川市,贺兰县', '3', 'helan', '0951', '750200', 'H', '106.34982', '38.5544'); +INSERT INTO `yoshop_region` VALUES ('3185', '3179', '灵武', '灵武市', '中国,宁夏回族自治区,银川市,灵武市', '3', 'lingwu', '0951', '750004', 'L', '106.33999', '38.10266'); +INSERT INTO `yoshop_region` VALUES ('3186', '3178', '石嘴山', '石嘴山市', '中国,宁夏回族自治区,石嘴山市', '2', 'shizuishan', '0952', '753000', 'S', '106.376173', '39.01333'); +INSERT INTO `yoshop_region` VALUES ('3187', '3186', '大武口', '大武口区', '中国,宁夏回族自治区,石嘴山市,大武口区', '3', 'dawukou', '0952', '753000', 'D', '106.37717', '39.01226'); +INSERT INTO `yoshop_region` VALUES ('3188', '3186', '惠农', '惠农区', '中国,宁夏回族自治区,石嘴山市,惠农区', '3', 'huinong', '0952', '753600', 'H', '106.71145', '39.13193'); +INSERT INTO `yoshop_region` VALUES ('3189', '3186', '平罗', '平罗县', '中国,宁夏回族自治区,石嘴山市,平罗县', '3', 'pingluo', '0952', '753400', 'P', '106.54538', '38.90429'); +INSERT INTO `yoshop_region` VALUES ('3190', '3178', '吴忠', '吴忠市', '中国,宁夏回族自治区,吴忠市', '2', 'wuzhong', '0953', '751100', 'W', '106.199409', '37.986165'); +INSERT INTO `yoshop_region` VALUES ('3191', '3190', '利通', '利通区', '中国,宁夏回族自治区,吴忠市,利通区', '3', 'litong', '0953', '751100', 'L', '106.20311', '37.98512'); +INSERT INTO `yoshop_region` VALUES ('3192', '3190', '红寺堡', '红寺堡区', '中国,宁夏回族自治区,吴忠市,红寺堡区', '3', 'hongsibao', '0953', '751900', 'H', '106.19822', '37.99747'); +INSERT INTO `yoshop_region` VALUES ('3193', '3190', '盐池', '盐池县', '中国,宁夏回族自治区,吴忠市,盐池县', '3', 'yanchi', '0953', '751500', 'Y', '107.40707', '37.7833'); +INSERT INTO `yoshop_region` VALUES ('3194', '3190', '同心', '同心县', '中国,宁夏回族自治区,吴忠市,同心县', '3', 'tongxin', '0953', '751300', 'T', '105.91418', '36.98116'); +INSERT INTO `yoshop_region` VALUES ('3195', '3190', '青铜峡', '青铜峡市', '中国,宁夏回族自治区,吴忠市,青铜峡市', '3', 'qingtongxia', '0953', '751600', 'Q', '106.07489', '38.02004'); +INSERT INTO `yoshop_region` VALUES ('3196', '3178', '固原', '固原市', '中国,宁夏回族自治区,固原市', '2', 'guyuan', '0954', '756000', 'G', '106.285241', '36.004561'); +INSERT INTO `yoshop_region` VALUES ('3197', '3196', '原州', '原州区', '中国,宁夏回族自治区,固原市,原州区', '3', 'yuanzhou', '0954', '756000', 'Y', '106.28778', '36.00374'); +INSERT INTO `yoshop_region` VALUES ('3198', '3196', '西吉', '西吉县', '中国,宁夏回族自治区,固原市,西吉县', '3', 'xiji', '0954', '756200', 'X', '105.73107', '35.96616'); +INSERT INTO `yoshop_region` VALUES ('3199', '3196', '隆德', '隆德县', '中国,宁夏回族自治区,固原市,隆德县', '3', 'longde', '0954', '756300', 'L', '106.12426', '35.61718'); +INSERT INTO `yoshop_region` VALUES ('3200', '3196', '泾源', '泾源县', '中国,宁夏回族自治区,固原市,泾源县', '3', 'jingyuan', '0954', '756400', null, '106.33902', '35.49072'); +INSERT INTO `yoshop_region` VALUES ('3201', '3196', '彭阳', '彭阳县', '中国,宁夏回族自治区,固原市,彭阳县', '3', 'pengyang', '0954', '756500', 'P', '106.64462', '35.85076'); +INSERT INTO `yoshop_region` VALUES ('3202', '3178', '中卫', '中卫市', '中国,宁夏回族自治区,中卫市', '2', 'zhongwei', '0955', '751700', 'Z', '105.189568', '37.514951'); +INSERT INTO `yoshop_region` VALUES ('3203', '3202', '沙坡头', '沙坡头区', '中国,宁夏回族自治区,中卫市,沙坡头区', '3', 'shapotou', '0955', '755000', 'S', '105.18962', '37.51044'); +INSERT INTO `yoshop_region` VALUES ('3204', '3202', '中宁', '中宁县', '中国,宁夏回族自治区,中卫市,中宁县', '3', 'zhongning', '0955', '751200', 'Z', '105.68515', '37.49149'); +INSERT INTO `yoshop_region` VALUES ('3205', '3202', '海原', '海原县', '中国,宁夏回族自治区,中卫市,海原县', '3', 'haiyuan', '0955', '751800', 'H', '105.64712', '36.56498'); +INSERT INTO `yoshop_region` VALUES ('3206', '0', '新疆', '新疆维吾尔自治区', '中国,新疆维吾尔自治区', '1', 'xinjiang', '', '', 'X', '87.617733', '43.792818'); +INSERT INTO `yoshop_region` VALUES ('3207', '3206', '乌鲁木齐', '乌鲁木齐市', '中国,新疆维吾尔自治区,乌鲁木齐市', '2', 'urumqi', '0991', '830002', 'W', '87.617733', '43.792818'); +INSERT INTO `yoshop_region` VALUES ('3208', '3207', '天山', '天山区', '中国,新疆维吾尔自治区,乌鲁木齐市,天山区', '3', 'tianshan', '0991', '830002', 'T', '87.63167', '43.79439'); +INSERT INTO `yoshop_region` VALUES ('3209', '3207', '沙依巴克', '沙依巴克区', '中国,新疆维吾尔自治区,乌鲁木齐市,沙依巴克区', '3', 'shayibake', '0991', '830000', 'S', '87.59788', '43.80118'); +INSERT INTO `yoshop_region` VALUES ('3210', '3207', '新市', '新市区', '中国,新疆维吾尔自治区,乌鲁木齐市,新市区', '3', 'xinshi', '0991', '830011', 'X', '87.57406', '43.84348'); +INSERT INTO `yoshop_region` VALUES ('3211', '3207', '水磨沟', '水磨沟区', '中国,新疆维吾尔自治区,乌鲁木齐市,水磨沟区', '3', 'shuimogou', '0991', '830017', 'S', '87.64249', '43.83247'); +INSERT INTO `yoshop_region` VALUES ('3212', '3207', '头屯河', '头屯河区', '中国,新疆维吾尔自治区,乌鲁木齐市,头屯河区', '3', 'toutunhe', '0991', '830022', 'T', '87.29138', '43.85487'); +INSERT INTO `yoshop_region` VALUES ('3213', '3207', '达坂城', '达坂城区', '中国,新疆维吾尔自治区,乌鲁木齐市,达坂城区', '3', 'dabancheng', '0991', '830039', 'D', '88.30697', '43.35797'); +INSERT INTO `yoshop_region` VALUES ('3214', '3207', '米东', '米东区', '中国,新疆维吾尔自治区,乌鲁木齐市,米东区', '3', 'midong', '0991', '830019', 'M', '87.68583', '43.94739'); +INSERT INTO `yoshop_region` VALUES ('3215', '3207', '乌鲁木齐', '乌鲁木齐县', '中国,新疆维吾尔自治区,乌鲁木齐市,乌鲁木齐县', '3', 'wulumuqi', '0991', '830063', 'W', '87.40939', '43.47125'); +INSERT INTO `yoshop_region` VALUES ('3216', '3206', '克拉玛依', '克拉玛依市', '中国,新疆维吾尔自治区,克拉玛依市', '2', 'karamay', '0990', '834000', 'K', '84.873946', '45.595886'); +INSERT INTO `yoshop_region` VALUES ('3217', '3216', '独山子', '独山子区', '中国,新疆维吾尔自治区,克拉玛依市,独山子区', '3', 'dushanzi', '0992', '834021', 'D', '84.88671', '44.32867'); +INSERT INTO `yoshop_region` VALUES ('3218', '3216', '克拉玛依', '克拉玛依区', '中国,新疆维吾尔自治区,克拉玛依市,克拉玛依区', '3', 'kelamayi', '0990', '834000', 'K', '84.86225', '45.59089'); +INSERT INTO `yoshop_region` VALUES ('3219', '3216', '白碱滩', '白碱滩区', '中国,新疆维吾尔自治区,克拉玛依市,白碱滩区', '3', 'baijiantan', '0990', '834008', 'B', '85.13244', '45.68768'); +INSERT INTO `yoshop_region` VALUES ('3220', '3216', '乌尔禾', '乌尔禾区', '中国,新疆维吾尔自治区,克拉玛依市,乌尔禾区', '3', 'wuerhe', '0990', '834012', 'W', '85.69143', '46.09006'); +INSERT INTO `yoshop_region` VALUES ('3221', '3206', '吐鲁番', '吐鲁番地区', '中国,新疆维吾尔自治区,吐鲁番地区', '2', 'turpan', '0995', '838000', 'T', '89.184078', '42.947613'); +INSERT INTO `yoshop_region` VALUES ('3222', '3221', '吐鲁番', '吐鲁番市', '中国,新疆维吾尔自治区,吐鲁番地区,吐鲁番市', '3', 'tulufan', '0995', '838000', 'T', '89.18579', '42.93505'); +INSERT INTO `yoshop_region` VALUES ('3223', '3221', '鄯善', '鄯善县', '中国,新疆维吾尔自治区,吐鲁番地区,鄯善县', '3', 'shanshan', '0995', '838200', null, '90.21402', '42.8635'); +INSERT INTO `yoshop_region` VALUES ('3224', '3221', '托克逊', '托克逊县', '中国,新疆维吾尔自治区,吐鲁番地区,托克逊县', '3', 'tuokexun', '0995', '838100', 'T', '88.65823', '42.79231'); +INSERT INTO `yoshop_region` VALUES ('3225', '3206', '哈密', '哈密地区', '中国,新疆维吾尔自治区,哈密地区', '2', 'hami', '0902', '839000', 'H', '93.51316', '42.833248'); +INSERT INTO `yoshop_region` VALUES ('3226', '3225', '哈密', '哈密市', '中国,新疆维吾尔自治区,哈密地区,哈密市', '3', 'hami', '0902', '839000', 'H', '93.51452', '42.82699'); +INSERT INTO `yoshop_region` VALUES ('3227', '3225', '巴里坤', '巴里坤哈萨克自治县', '中国,新疆维吾尔自治区,哈密地区,巴里坤哈萨克自治县', '3', 'balikun', '0902', '839200', 'B', '93.01236', '43.59993'); +INSERT INTO `yoshop_region` VALUES ('3228', '3225', '伊吾', '伊吾县', '中国,新疆维吾尔自治区,哈密地区,伊吾县', '3', 'yiwu', '0902', '839300', 'Y', '94.69403', '43.2537'); +INSERT INTO `yoshop_region` VALUES ('3229', '3206', '昌吉', '昌吉回族自治州', '中国,新疆维吾尔自治区,昌吉回族自治州', '2', 'changji', '0994', '831100', 'C', '87.304012', '44.014577'); +INSERT INTO `yoshop_region` VALUES ('3230', '3229', '昌吉', '昌吉市', '中国,新疆维吾尔自治区,昌吉回族自治州,昌吉市', '3', 'changji', '0994', '831100', 'C', '87.30249', '44.01267'); +INSERT INTO `yoshop_region` VALUES ('3231', '3229', '阜康', '阜康市', '中国,新疆维吾尔自治区,昌吉回族自治州,阜康市', '3', 'fukang', '0994', '831500', 'F', '87.98529', '44.1584'); +INSERT INTO `yoshop_region` VALUES ('3232', '3229', '呼图壁', '呼图壁县', '中国,新疆维吾尔自治区,昌吉回族自治州,呼图壁县', '3', 'hutubi', '0994', '831200', 'H', '86.89892', '44.18977'); +INSERT INTO `yoshop_region` VALUES ('3233', '3229', '玛纳斯', '玛纳斯县', '中国,新疆维吾尔自治区,昌吉回族自治州,玛纳斯县', '3', 'manasi', '0994', '832200', 'M', '86.2145', '44.30438'); +INSERT INTO `yoshop_region` VALUES ('3234', '3229', '奇台', '奇台县', '中国,新疆维吾尔自治区,昌吉回族自治州,奇台县', '3', 'qitai', '0994', '831800', 'Q', '89.5932', '44.02221'); +INSERT INTO `yoshop_region` VALUES ('3235', '3229', '吉木萨尔', '吉木萨尔县', '中国,新疆维吾尔自治区,昌吉回族自治州,吉木萨尔县', '3', 'jimusaer', '0994', '831700', 'J', '89.18078', '44.00048'); +INSERT INTO `yoshop_region` VALUES ('3236', '3229', '木垒', '木垒哈萨克自治县', '中国,新疆维吾尔自治区,昌吉回族自治州,木垒哈萨克自治县', '3', 'mulei', '0994', '831900', 'M', '90.28897', '43.83508'); +INSERT INTO `yoshop_region` VALUES ('3237', '3206', '博尔塔拉', '博尔塔拉蒙古自治州', '中国,新疆维吾尔自治区,博尔塔拉蒙古自治州', '2', 'bortala', '0909', '833400', 'B', '82.074778', '44.903258'); +INSERT INTO `yoshop_region` VALUES ('3238', '3237', '博乐', '博乐市', '中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,博乐市', '3', 'bole', '0909', '833400', 'B', '82.0713', '44.90052'); +INSERT INTO `yoshop_region` VALUES ('3239', '3237', '阿拉山口', '阿拉山口市', '中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,阿拉山口市', '3', 'alashankou', '0909', '833400', 'A', '82.567721', '45.170616'); +INSERT INTO `yoshop_region` VALUES ('3240', '3237', '精河', '精河县', '中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,精河县', '3', 'jinghe', '0909', '833300', 'J', '82.89419', '44.60774'); +INSERT INTO `yoshop_region` VALUES ('3241', '3237', '温泉', '温泉县', '中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,温泉县', '3', 'wenquan', '0909', '833500', 'W', '81.03134', '44.97373'); +INSERT INTO `yoshop_region` VALUES ('3242', '3206', '巴音郭楞', '巴音郭楞蒙古自治州', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州', '2', 'bayingol', '0996', '841000', 'B', '86.150969', '41.768552'); +INSERT INTO `yoshop_region` VALUES ('3243', '3242', '库尔勒', '库尔勒市', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,库尔勒市', '3', 'kuerle', '0996', '841000', 'K', '86.15528', '41.76602'); +INSERT INTO `yoshop_region` VALUES ('3244', '3242', '轮台', '轮台县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,轮台县', '3', 'luntai', '0996', '841600', 'L', '84.26101', '41.77642'); +INSERT INTO `yoshop_region` VALUES ('3245', '3242', '尉犁', '尉犁县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,尉犁县', '3', 'yuli', '0996', '841500', 'W', '86.25903', '41.33632'); +INSERT INTO `yoshop_region` VALUES ('3246', '3242', '若羌', '若羌县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,若羌县', '3', 'ruoqiang', '0996', '841800', 'R', '88.16812', '39.0179'); +INSERT INTO `yoshop_region` VALUES ('3247', '3242', '且末', '且末县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,且末县', '3', 'qiemo', '0996', '841900', 'Q', '85.52975', '38.14534'); +INSERT INTO `yoshop_region` VALUES ('3248', '3242', '焉耆', '焉耆回族自治县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,焉耆回族自治县', '3', 'yanqi', '0996', '841100', 'Y', '86.5744', '42.059'); +INSERT INTO `yoshop_region` VALUES ('3249', '3242', '和静', '和静县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,和静县', '3', 'hejing', '0996', '841300', 'H', '86.39611', '42.31838'); +INSERT INTO `yoshop_region` VALUES ('3250', '3242', '和硕', '和硕县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,和硕县', '3', 'heshuo', '0996', '841200', 'H', '86.86392', '42.26814'); +INSERT INTO `yoshop_region` VALUES ('3251', '3242', '博湖', '博湖县', '中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,博湖县', '3', 'bohu', '0996', '841400', 'B', '86.63333', '41.98014'); +INSERT INTO `yoshop_region` VALUES ('3252', '3206', '阿克苏', '阿克苏地区', '中国,新疆维吾尔自治区,阿克苏地区', '2', 'aksu', '0997', '843000', 'A', '80.265068', '41.170712'); +INSERT INTO `yoshop_region` VALUES ('3253', '3252', '阿克苏', '阿克苏市', '中国,新疆维吾尔自治区,阿克苏地区,阿克苏市', '3', 'akesu', '0997', '843000', 'A', '80.26338', '41.16754'); +INSERT INTO `yoshop_region` VALUES ('3254', '3252', '温宿', '温宿县', '中国,新疆维吾尔自治区,阿克苏地区,温宿县', '3', 'wensu', '0997', '843100', 'W', '80.24173', '41.27679'); +INSERT INTO `yoshop_region` VALUES ('3255', '3252', '库车', '库车县', '中国,新疆维吾尔自治区,阿克苏地区,库车县', '3', 'kuche', '0997', '842000', 'K', '82.96209', '41.71793'); +INSERT INTO `yoshop_region` VALUES ('3256', '3252', '沙雅', '沙雅县', '中国,新疆维吾尔自治区,阿克苏地区,沙雅县', '3', 'shaya', '0997', '842200', 'S', '82.78131', '41.22497'); +INSERT INTO `yoshop_region` VALUES ('3257', '3252', '新和', '新和县', '中国,新疆维吾尔自治区,阿克苏地区,新和县', '3', 'xinhe', '0997', '842100', 'X', '82.61053', '41.54964'); +INSERT INTO `yoshop_region` VALUES ('3258', '3252', '拜城', '拜城县', '中国,新疆维吾尔自治区,阿克苏地区,拜城县', '3', 'baicheng', '0997', '842300', 'B', '81.87564', '41.79801'); +INSERT INTO `yoshop_region` VALUES ('3259', '3252', '乌什', '乌什县', '中国,新疆维吾尔自治区,阿克苏地区,乌什县', '3', 'wushi', '0997', '843400', 'W', '79.22937', '41.21569'); +INSERT INTO `yoshop_region` VALUES ('3260', '3252', '阿瓦提', '阿瓦提县', '中国,新疆维吾尔自治区,阿克苏地区,阿瓦提县', '3', 'awati', '0997', '843200', 'A', '80.38336', '40.63926'); +INSERT INTO `yoshop_region` VALUES ('3261', '3252', '柯坪', '柯坪县', '中国,新疆维吾尔自治区,阿克苏地区,柯坪县', '3', 'keping', '0997', '843600', 'K', '79.04751', '40.50585'); +INSERT INTO `yoshop_region` VALUES ('3262', '3206', '克孜勒苏', '克孜勒苏柯尔克孜自治州', '中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州', '2', 'kizilsu', '0908', '845350', 'K', '76.172825', '39.713431'); +INSERT INTO `yoshop_region` VALUES ('3263', '3262', '阿图什', '阿图什市', '中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿图什市', '3', 'atushi', '0908', '845350', 'A', '76.16827', '39.71615'); +INSERT INTO `yoshop_region` VALUES ('3264', '3262', '阿克陶', '阿克陶县', '中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿克陶县', '3', 'aketao', '0908', '845550', 'A', '75.94692', '39.14892'); +INSERT INTO `yoshop_region` VALUES ('3265', '3262', '阿合奇', '阿合奇县', '中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿合奇县', '3', 'aheqi', '0997', '843500', 'A', '78.44848', '40.93947'); +INSERT INTO `yoshop_region` VALUES ('3266', '3262', '乌恰', '乌恰县', '中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,乌恰县', '3', 'wuqia', '0908', '845450', 'W', '75.25839', '39.71984'); +INSERT INTO `yoshop_region` VALUES ('3267', '3206', '喀什', '喀什地区', '中国,新疆维吾尔自治区,喀什地区', '2', 'kashgar', '0998', '844000', 'K', '75.989138', '39.467664'); +INSERT INTO `yoshop_region` VALUES ('3268', '3267', '喀什', '喀什市', '中国,新疆维吾尔自治区,喀什地区,喀什市', '3', 'kashi', '0998', '844000', 'K', '75.99379', '39.46768'); +INSERT INTO `yoshop_region` VALUES ('3269', '3267', '疏附', '疏附县', '中国,新疆维吾尔自治区,喀什地区,疏附县', '3', 'shufu', '0998', '844100', 'S', '75.86029', '39.37534'); +INSERT INTO `yoshop_region` VALUES ('3270', '3267', '疏勒', '疏勒县', '中国,新疆维吾尔自治区,喀什地区,疏勒县', '3', 'shule', '0998', '844200', 'S', '76.05398', '39.40625'); +INSERT INTO `yoshop_region` VALUES ('3271', '3267', '英吉沙', '英吉沙县', '中国,新疆维吾尔自治区,喀什地区,英吉沙县', '3', 'yingjisha', '0998', '844500', 'Y', '76.17565', '38.92968'); +INSERT INTO `yoshop_region` VALUES ('3272', '3267', '泽普', '泽普县', '中国,新疆维吾尔自治区,喀什地区,泽普县', '3', 'zepu', '0998', '844800', 'Z', '77.27145', '38.18935'); +INSERT INTO `yoshop_region` VALUES ('3273', '3267', '莎车', '莎车县', '中国,新疆维吾尔自治区,喀什地区,莎车县', '3', 'shache', '0998', '844700', 'S', '77.24316', '38.41601'); +INSERT INTO `yoshop_region` VALUES ('3274', '3267', '叶城', '叶城县', '中国,新疆维吾尔自治区,喀什地区,叶城县', '3', 'yecheng', '0998', '844900', 'Y', '77.41659', '37.88324'); +INSERT INTO `yoshop_region` VALUES ('3275', '3267', '麦盖提', '麦盖提县', '中国,新疆维吾尔自治区,喀什地区,麦盖提县', '3', 'maigaiti', '0998', '844600', 'M', '77.64224', '38.89662'); +INSERT INTO `yoshop_region` VALUES ('3276', '3267', '岳普湖', '岳普湖县', '中国,新疆维吾尔自治区,喀什地区,岳普湖县', '3', 'yuepuhu', '0998', '844400', 'Y', '76.77233', '39.23561'); +INSERT INTO `yoshop_region` VALUES ('3277', '3267', '伽师', '伽师县', '中国,新疆维吾尔自治区,喀什地区,伽师县', '3', 'jiashi', '0998', '844300', null, '76.72372', '39.48801'); +INSERT INTO `yoshop_region` VALUES ('3278', '3267', '巴楚', '巴楚县', '中国,新疆维吾尔自治区,喀什地区,巴楚县', '3', 'bachu', '0998', '843800', 'B', '78.54888', '39.7855'); +INSERT INTO `yoshop_region` VALUES ('3279', '3267', '塔什库尔干塔吉克', '塔什库尔干塔吉克自治县', '中国,新疆维吾尔自治区,喀什地区,塔什库尔干塔吉克自治县', '3', 'tashikuergantajike', '0998', '845250', 'T', '75.23196', '37.77893'); +INSERT INTO `yoshop_region` VALUES ('3280', '3206', '和田', '和田地区', '中国,新疆维吾尔自治区,和田地区', '2', 'hotan', '0903', '848000', 'H', '79.92533', '37.110687'); +INSERT INTO `yoshop_region` VALUES ('3281', '3280', '和田市', '和田市', '中国,新疆维吾尔自治区,和田地区,和田市', '3', 'hetianshi', '0903', '848000', 'H', '79.91353', '37.11214'); +INSERT INTO `yoshop_region` VALUES ('3282', '3280', '和田县', '和田县', '中国,新疆维吾尔自治区,和田地区,和田县', '3', 'hetianxian', '0903', '848000', 'H', '79.82874', '37.08922'); +INSERT INTO `yoshop_region` VALUES ('3283', '3280', '墨玉', '墨玉县', '中国,新疆维吾尔自治区,和田地区,墨玉县', '3', 'moyu', '0903', '848100', 'M', '79.74035', '37.27248'); +INSERT INTO `yoshop_region` VALUES ('3284', '3280', '皮山', '皮山县', '中国,新疆维吾尔自治区,和田地区,皮山县', '3', 'pishan', '0903', '845150', 'P', '78.28125', '37.62007'); +INSERT INTO `yoshop_region` VALUES ('3285', '3280', '洛浦', '洛浦县', '中国,新疆维吾尔自治区,和田地区,洛浦县', '3', 'luopu', '0903', '848200', 'L', '80.18536', '37.07364'); +INSERT INTO `yoshop_region` VALUES ('3286', '3280', '策勒', '策勒县', '中国,新疆维吾尔自治区,和田地区,策勒县', '3', 'cele', '0903', '848300', 'C', '80.80999', '36.99843'); +INSERT INTO `yoshop_region` VALUES ('3287', '3280', '于田', '于田县', '中国,新疆维吾尔自治区,和田地区,于田县', '3', 'yutian', '0903', '848400', 'Y', '81.66717', '36.854'); +INSERT INTO `yoshop_region` VALUES ('3288', '3280', '民丰', '民丰县', '中国,新疆维吾尔自治区,和田地区,民丰县', '3', 'minfeng', '0903', '848500', 'M', '82.68444', '37.06577'); +INSERT INTO `yoshop_region` VALUES ('3289', '3206', '伊犁', '伊犁哈萨克自治州', '中国,新疆维吾尔自治区,伊犁哈萨克自治州', '2', 'ili', '0999', '835100', 'Y', '81.317946', '43.92186'); +INSERT INTO `yoshop_region` VALUES ('3290', '3289', '伊宁', '伊宁市', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,伊宁市', '3', 'yining', '0999', '835000', 'Y', '81.32932', '43.91294'); +INSERT INTO `yoshop_region` VALUES ('3291', '3289', '奎屯', '奎屯市', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,奎屯市', '3', 'kuitun', '0992', '833200', 'K', '84.90228', '44.425'); +INSERT INTO `yoshop_region` VALUES ('3292', '3289', '霍尔果斯', '霍尔果斯市', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,霍尔果斯市', '3', 'huoerguosi', '0999', '835221', 'H', '80.418189', '44.205778'); +INSERT INTO `yoshop_region` VALUES ('3293', '3289', '伊宁', '伊宁县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,伊宁县', '3', 'yining', '0999', '835100', 'Y', '81.52764', '43.97863'); +INSERT INTO `yoshop_region` VALUES ('3294', '3289', '察布查尔锡伯', '察布查尔锡伯自治县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,察布查尔锡伯自治县', '3', 'chabuchaerxibo', '0999', '835300', 'C', '81.14956', '43.84023'); +INSERT INTO `yoshop_region` VALUES ('3295', '3289', '霍城', '霍城县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,霍城县', '3', 'huocheng', '0999', '835200', 'H', '80.87826', '44.0533'); +INSERT INTO `yoshop_region` VALUES ('3296', '3289', '巩留', '巩留县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,巩留县', '3', 'gongliu', '0999', '835400', 'G', '82.22851', '43.48429'); +INSERT INTO `yoshop_region` VALUES ('3297', '3289', '新源', '新源县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,新源县', '3', 'xinyuan', '0999', '835800', 'X', '83.26095', '43.4284'); +INSERT INTO `yoshop_region` VALUES ('3298', '3289', '昭苏', '昭苏县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,昭苏县', '3', 'zhaosu', '0999', '835600', 'Z', '81.1307', '43.15828'); +INSERT INTO `yoshop_region` VALUES ('3299', '3289', '特克斯', '特克斯县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,特克斯县', '3', 'tekesi', '0999', '835500', 'T', '81.84005', '43.21938'); +INSERT INTO `yoshop_region` VALUES ('3300', '3289', '尼勒克', '尼勒克县', '中国,新疆维吾尔自治区,伊犁哈萨克自治州,尼勒克县', '3', 'nileke', '0999', '835700', 'N', '82.51184', '43.79901'); +INSERT INTO `yoshop_region` VALUES ('3301', '3206', '塔城', '塔城地区', '中国,新疆维吾尔自治区,塔城地区', '2', 'qoqek', '0901', '834700', 'T', '82.985732', '46.746301'); +INSERT INTO `yoshop_region` VALUES ('3302', '3301', '塔城', '塔城市', '中国,新疆维吾尔自治区,塔城地区,塔城市', '3', 'tacheng', '0901', '834700', 'T', '82.97892', '46.74852'); +INSERT INTO `yoshop_region` VALUES ('3303', '3301', '乌苏', '乌苏市', '中国,新疆维吾尔自治区,塔城地区,乌苏市', '3', 'wusu', '0992', '833000', 'W', '84.68258', '44.43729'); +INSERT INTO `yoshop_region` VALUES ('3304', '3301', '额敏', '额敏县', '中国,新疆维吾尔自治区,塔城地区,额敏县', '3', 'emin', '0901', '834600', 'E', '83.62872', '46.5284'); +INSERT INTO `yoshop_region` VALUES ('3305', '3301', '沙湾', '沙湾县', '中国,新疆维吾尔自治区,塔城地区,沙湾县', '3', 'shawan', '0993', '832100', 'S', '85.61932', '44.33144'); +INSERT INTO `yoshop_region` VALUES ('3306', '3301', '托里', '托里县', '中国,新疆维吾尔自治区,塔城地区,托里县', '3', 'tuoli', '0901', '834500', 'T', '83.60592', '45.93623'); +INSERT INTO `yoshop_region` VALUES ('3307', '3301', '裕民', '裕民县', '中国,新疆维吾尔自治区,塔城地区,裕民县', '3', 'yumin', '0901', '834800', 'Y', '82.99002', '46.20377'); +INSERT INTO `yoshop_region` VALUES ('3308', '3301', '和布克赛尔', '和布克赛尔蒙古自治县', '中国,新疆维吾尔自治区,塔城地区,和布克赛尔蒙古自治县', '3', 'hebukesaier', '0990', '834400', 'H', '85.72662', '46.79362'); +INSERT INTO `yoshop_region` VALUES ('3309', '3206', '阿勒泰', '阿勒泰地区', '中国,新疆维吾尔自治区,阿勒泰地区', '2', 'altay', '0906', '836500', 'A', '88.13963', '47.848393'); +INSERT INTO `yoshop_region` VALUES ('3310', '3309', '阿勒泰', '阿勒泰市', '中国,新疆维吾尔自治区,阿勒泰地区,阿勒泰市', '3', 'aletai', '0906', '836500', 'A', '88.13913', '47.8317'); +INSERT INTO `yoshop_region` VALUES ('3311', '3309', '布尔津', '布尔津县', '中国,新疆维吾尔自治区,阿勒泰地区,布尔津县', '3', 'buerjin', '0906', '836600', 'B', '86.85751', '47.70062'); +INSERT INTO `yoshop_region` VALUES ('3312', '3309', '富蕴', '富蕴县', '中国,新疆维吾尔自治区,阿勒泰地区,富蕴县', '3', 'fuyun', '0906', '836100', 'F', '89.52679', '46.99444'); +INSERT INTO `yoshop_region` VALUES ('3313', '3309', '福海', '福海县', '中国,新疆维吾尔自治区,阿勒泰地区,福海县', '3', 'fuhai', '0906', '836400', 'F', '87.49508', '47.11065'); +INSERT INTO `yoshop_region` VALUES ('3314', '3309', '哈巴河', '哈巴河县', '中国,新疆维吾尔自治区,阿勒泰地区,哈巴河县', '3', 'habahe', '0906', '836700', 'H', '86.42092', '48.06046'); +INSERT INTO `yoshop_region` VALUES ('3315', '3309', '青河', '青河县', '中国,新疆维吾尔自治区,阿勒泰地区,青河县', '3', 'qinghe', '0906', '836200', 'Q', '90.38305', '46.67382'); +INSERT INTO `yoshop_region` VALUES ('3316', '3309', '吉木乃', '吉木乃县', '中国,新疆维吾尔自治区,阿勒泰地区,吉木乃县', '3', 'jimunai', '0906', '836800', 'J', '85.87814', '47.43359'); +INSERT INTO `yoshop_region` VALUES ('3317', '3206', ' ', '直辖县级', '中国,新疆维吾尔自治区,直辖县级', '2', '', '', '', 'Z', '91.132212', '29.660361'); +INSERT INTO `yoshop_region` VALUES ('3318', '3317', '石河子', '石河子市', '中国,新疆维吾尔自治区,直辖县级,石河子市', '3', 'shihezi', '0993', '832000', 'S', '86.041075', '44.305886'); +INSERT INTO `yoshop_region` VALUES ('3319', '3317', '阿拉尔', '阿拉尔市', '中国,新疆维吾尔自治区,直辖县级,阿拉尔市', '3', 'aral', '0997', '843300', 'A', '81.285884', '40.541914'); +INSERT INTO `yoshop_region` VALUES ('3320', '3317', '图木舒克', '图木舒克市', '中国,新疆维吾尔自治区,直辖县级,图木舒克市', '3', 'tumxuk', '0998', '843806', 'T', '79.077978', '39.867316'); +INSERT INTO `yoshop_region` VALUES ('3321', '3317', '五家渠', '五家渠市', '中国,新疆维吾尔自治区,直辖县级,五家渠市', '3', 'wujiaqu', '0994', '831300', 'W', '87.526884', '44.167401'); +INSERT INTO `yoshop_region` VALUES ('3322', '3317', '北屯', '北屯市', '中国,新疆维吾尔自治区,直辖县级,北屯市', '3', 'beitun', '0906', '836000', 'B', '87.808456', '47.362308'); +INSERT INTO `yoshop_region` VALUES ('3323', '3317', '铁门关', '铁门关市', '中国,新疆维吾尔自治区,直辖县级,铁门关市', '3', 'tiemenguan', '0906', '836000', 'T', '86.194687', '41.811007'); +INSERT INTO `yoshop_region` VALUES ('3324', '3317', '双河', '双河市', '中国,新疆维吾尔自治区,直辖县级,双河市', '3', 'shuanghe', '0909', '833408', 'S', '91.132212', '29.660361'); +INSERT INTO `yoshop_region` VALUES ('3325', '0', '台湾', '台湾省', '中国,台湾省', '1', 'taiwan', '', '', 'T', '121.509062', '25.044332'); +INSERT INTO `yoshop_region` VALUES ('3326', '3325', '台北', '台北市', '中国,台湾省,台北市', '2', 'taipei', '02', '1', 'T', '121.565170', '25.037798'); +INSERT INTO `yoshop_region` VALUES ('3327', '3326', '松山', '松山区', '中国,台湾省,台北市,松山区', '3', 'songshan', '02', '105', 'S', '121.577206', '25.049698'); +INSERT INTO `yoshop_region` VALUES ('3328', '3326', '信义', '信义区', '中国,台湾省,台北市,信义区', '3', 'xinyi', '02', '110', 'X', '121.751381', '25.129407'); +INSERT INTO `yoshop_region` VALUES ('3329', '3326', '大安', '大安区', '中国,台湾省,台北市,大安区', '3', 'da\'an', '02', '106', 'D', '121.534648', '25.026406'); +INSERT INTO `yoshop_region` VALUES ('3330', '3326', '中山', '中山区', '中国,台湾省,台北市,中山区', '3', 'zhongshan', '02', '104', 'Z', '121.533468', '25.064361'); +INSERT INTO `yoshop_region` VALUES ('3331', '3326', '中正', '中正区', '中国,台湾省,台北市,中正区', '3', 'zhongzheng', '02', '100', 'Z', '121.518267', '25.032361'); +INSERT INTO `yoshop_region` VALUES ('3332', '3326', '大同', '大同区', '中国,台湾省,台北市,大同区', '3', 'datong', '02', '103', 'D', '121.515514', '25.065986'); +INSERT INTO `yoshop_region` VALUES ('3333', '3326', '万华', '万华区', '中国,台湾省,台北市,万华区', '3', 'wanhua', '02', '108', 'W', '121.499332', '25.031933'); +INSERT INTO `yoshop_region` VALUES ('3334', '3326', '文山', '文山区', '中国,台湾省,台北市,文山区', '3', 'wenshan', '02', '116', 'W', '121.570458', '24.989786'); +INSERT INTO `yoshop_region` VALUES ('3335', '3326', '南港', '南港区', '中国,台湾省,台北市,南港区', '3', 'nangang', '02', '115', 'N', '121.606858', '25.054656'); +INSERT INTO `yoshop_region` VALUES ('3336', '3326', '内湖', '内湖区', '中国,台湾省,台北市,内湖区', '3', 'nahu', '02', '114', 'N', '121.588998', '25.069664'); +INSERT INTO `yoshop_region` VALUES ('3337', '3326', '士林', '士林区', '中国,台湾省,台北市,士林区', '3', 'shilin', '02', '111', 'S', '121.519874', '25.092822'); +INSERT INTO `yoshop_region` VALUES ('3338', '3326', '北投', '北投区', '中国,台湾省,台北市,北投区', '3', 'beitou', '02', '112', 'B', '121.501379', '25.132419'); +INSERT INTO `yoshop_region` VALUES ('3339', '3325', '高雄', '高雄市', '中国,台湾省,高雄市', '2', 'kaohsiung', '07', '8', 'G', '120.311922', '22.620856'); +INSERT INTO `yoshop_region` VALUES ('3340', '3339', '盐埕', '盐埕区', '中国,台湾省,高雄市,盐埕区', '3', 'yancheng', '07', '803', 'Y', '120.286795', '22.624666'); +INSERT INTO `yoshop_region` VALUES ('3341', '3339', '鼓山', '鼓山区', '中国,台湾省,高雄市,鼓山区', '3', 'gushan', '07', '804', 'G', '120.281154', '22.636797'); +INSERT INTO `yoshop_region` VALUES ('3342', '3339', '左营', '左营区', '中国,台湾省,高雄市,左营区', '3', 'zuoying', '07', '813', 'Z', '120.294958', '22.690124'); +INSERT INTO `yoshop_region` VALUES ('3343', '3339', '楠梓', '楠梓区', '中国,台湾省,高雄市,楠梓区', '3', 'nanzi', '07', '811', null, '120.326314', '22.728401'); +INSERT INTO `yoshop_region` VALUES ('3344', '3339', '三民', '三民区', '中国,台湾省,高雄市,三民区', '3', 'sanmin', '07', '807', 'S', '120.299622', '22.647695'); +INSERT INTO `yoshop_region` VALUES ('3345', '3339', '新兴', '新兴区', '中国,台湾省,高雄市,新兴区', '3', 'xinxing', '07', '800', 'X', '120.309535', '22.631147'); +INSERT INTO `yoshop_region` VALUES ('3346', '3339', '前金', '前金区', '中国,台湾省,高雄市,前金区', '3', 'qianjin', '07', '801', 'Q', '120.294159', '22.627421'); +INSERT INTO `yoshop_region` VALUES ('3347', '3339', '苓雅', '苓雅区', '中国,台湾省,高雄市,苓雅区', '3', 'lingya', '07', '802', null, '120.312347', '22.621770'); +INSERT INTO `yoshop_region` VALUES ('3348', '3339', '前镇', '前镇区', '中国,台湾省,高雄市,前镇区', '3', 'qianzhen', '07', '806', 'Q', '120.318583', '22.586425'); +INSERT INTO `yoshop_region` VALUES ('3349', '3339', '旗津', '旗津区', '中国,台湾省,高雄市,旗津区', '3', 'qijin', '07', '805', 'Q', '120.284429', '22.590565'); +INSERT INTO `yoshop_region` VALUES ('3350', '3339', '小港', '小港区', '中国,台湾省,高雄市,小港区', '3', 'xiaogang', '07', '812', 'X', '120.337970', '22.565354'); +INSERT INTO `yoshop_region` VALUES ('3351', '3339', '凤山', '凤山区', '中国,台湾省,高雄市,凤山区', '3', 'fengshan', '07', '830', 'F', '120.356892', '22.626945'); +INSERT INTO `yoshop_region` VALUES ('3352', '3339', '林园', '林园区', '中国,台湾省,高雄市,林园区', '3', 'linyuan', '07', '832', 'L', '120.395977', '22.501490'); +INSERT INTO `yoshop_region` VALUES ('3353', '3339', '大寮', '大寮区', '中国,台湾省,高雄市,大寮区', '3', 'daliao', '07', '831', 'D', '120.395422', '22.605386'); +INSERT INTO `yoshop_region` VALUES ('3354', '3339', '大树', '大树区', '中国,台湾省,高雄市,大树区', '3', 'dashu', '07', '840', 'D', '120.433095', '22.693394'); +INSERT INTO `yoshop_region` VALUES ('3355', '3339', '大社', '大社区', '中国,台湾省,高雄市,大社区', '3', 'dashe', '07', '815', 'D', '120.346635', '22.729966'); +INSERT INTO `yoshop_region` VALUES ('3356', '3339', '仁武', '仁武区', '中国,台湾省,高雄市,仁武区', '3', 'renwu', '07', '814', 'R', '120.347779', '22.701901'); +INSERT INTO `yoshop_region` VALUES ('3357', '3339', '鸟松', '鸟松区', '中国,台湾省,高雄市,鸟松区', '3', 'niaosong', '07', '833', 'N', '120.364402', '22.659340'); +INSERT INTO `yoshop_region` VALUES ('3358', '3339', '冈山', '冈山区', '中国,台湾省,高雄市,冈山区', '3', 'gangshan', '07', '820', 'G', '120.295820', '22.796762'); +INSERT INTO `yoshop_region` VALUES ('3359', '3339', '桥头', '桥头区', '中国,台湾省,高雄市,桥头区', '3', 'qiaotou', '07', '825', 'Q', '120.305741', '22.757501'); +INSERT INTO `yoshop_region` VALUES ('3360', '3339', '燕巢', '燕巢区', '中国,台湾省,高雄市,燕巢区', '3', 'yanchao', '07', '824', 'Y', '120.361956', '22.793370'); +INSERT INTO `yoshop_region` VALUES ('3361', '3339', '田寮', '田寮区', '中国,台湾省,高雄市,田寮区', '3', 'tianliao', '07', '823', 'T', '120.359636', '22.869307'); +INSERT INTO `yoshop_region` VALUES ('3362', '3339', '阿莲', '阿莲区', '中国,台湾省,高雄市,阿莲区', '3', 'alian', '07', '822', 'A', '120.327036', '22.883703'); +INSERT INTO `yoshop_region` VALUES ('3363', '3339', '路竹', '路竹区', '中国,台湾省,高雄市,路竹区', '3', 'luzhu', '07', '821', 'L', '120.261828', '22.856851'); +INSERT INTO `yoshop_region` VALUES ('3364', '3339', '湖内', '湖内区', '中国,台湾省,高雄市,湖内区', '3', 'huna', '07', '829', 'H', '120.211530', '22.908188'); +INSERT INTO `yoshop_region` VALUES ('3365', '3339', '茄萣', '茄萣区', '中国,台湾省,高雄市,茄萣区', '3', 'qieding', '07', '852', null, '120.182815', '22.906556'); +INSERT INTO `yoshop_region` VALUES ('3366', '3339', '永安', '永安区', '中国,台湾省,高雄市,永安区', '3', 'yong\'an', '07', '828', 'Y', '120.225308', '22.818580'); +INSERT INTO `yoshop_region` VALUES ('3367', '3339', '弥陀', '弥陀区', '中国,台湾省,高雄市,弥陀区', '3', 'mituo', '07', '827', 'M', '120.247344', '22.782879'); +INSERT INTO `yoshop_region` VALUES ('3368', '3339', '梓官', '梓官区', '中国,台湾省,高雄市,梓官区', '3', 'ziguan', '07', '826', null, '120.267322', '22.760475'); +INSERT INTO `yoshop_region` VALUES ('3369', '3339', '旗山', '旗山区', '中国,台湾省,高雄市,旗山区', '3', 'qishan', '07', '842', 'Q', '120.483550', '22.888491'); +INSERT INTO `yoshop_region` VALUES ('3370', '3339', '美浓', '美浓区', '中国,台湾省,高雄市,美浓区', '3', 'meinong', '07', '843', 'M', '120.541530', '22.897880'); +INSERT INTO `yoshop_region` VALUES ('3371', '3339', '六龟', '六龟区', '中国,台湾省,高雄市,六龟区', '3', 'liugui', '07', '844', 'L', '120.633418', '22.997914'); +INSERT INTO `yoshop_region` VALUES ('3372', '3339', '甲仙', '甲仙区', '中国,台湾省,高雄市,甲仙区', '3', 'jiaxian', '07', '847', 'J', '120.591185', '23.084688'); +INSERT INTO `yoshop_region` VALUES ('3373', '3339', '杉林', '杉林区', '中国,台湾省,高雄市,杉林区', '3', 'shanlin', '07', '846', 'S', '120.538980', '22.970712'); +INSERT INTO `yoshop_region` VALUES ('3374', '3339', '内门', '内门区', '中国,台湾省,高雄市,内门区', '3', 'namen', '07', '845', 'N', '120.462351', '22.943437'); +INSERT INTO `yoshop_region` VALUES ('3375', '3339', '茂林', '茂林区', '中国,台湾省,高雄市,茂林区', '3', 'maolin', '07', '851', 'M', '120.663217', '22.886248'); +INSERT INTO `yoshop_region` VALUES ('3376', '3339', '桃源', '桃源区', '中国,台湾省,高雄市,桃源区', '3', 'taoyuan', '07', '848', 'T', '120.760049', '23.159137'); +INSERT INTO `yoshop_region` VALUES ('3377', '3339', '那玛夏', '那玛夏区', '中国,台湾省,高雄市,那玛夏区', '3', 'namaxia', '07', '849', 'N', '120.692799', '23.216964'); +INSERT INTO `yoshop_region` VALUES ('3378', '3325', '基隆', '基隆市', '中国,台湾省,基隆市', '2', 'keelung', '02', '2', 'J', '121.746248', '25.130741'); +INSERT INTO `yoshop_region` VALUES ('3379', '3378', '中正', '中正区', '中国,台湾省,基隆市,中正区', '3', 'zhongzheng', '02', '202', 'Z', '121.518267', '25.032361'); +INSERT INTO `yoshop_region` VALUES ('3380', '3378', '七堵', '七堵区', '中国,台湾省,基隆市,七堵区', '3', 'qidu', '02', '206', 'Q', '121.713032', '25.095739'); +INSERT INTO `yoshop_region` VALUES ('3381', '3378', '暖暖', '暖暖区', '中国,台湾省,基隆市,暖暖区', '3', 'nuannuan', '02', '205', 'N', '121.736102', '25.099777'); +INSERT INTO `yoshop_region` VALUES ('3382', '3378', '仁爱', '仁爱区', '中国,台湾省,基隆市,仁爱区', '3', 'renai', '02', '200', 'R', '121.740940', '25.127526'); +INSERT INTO `yoshop_region` VALUES ('3383', '3378', '中山', '中山区', '中国,台湾省,基隆市,中山区', '3', 'zhongshan', '02', '203', 'Z', '121.739132', '25.133991'); +INSERT INTO `yoshop_region` VALUES ('3384', '3378', '安乐', '安乐区', '中国,台湾省,基隆市,安乐区', '3', 'anle', '02', '204', 'A', '121.723203', '25.120910'); +INSERT INTO `yoshop_region` VALUES ('3385', '3378', '信义', '信义区', '中国,台湾省,基隆市,信义区', '3', 'xinyi', '02', '201', 'X', '121.751381', '25.129407'); +INSERT INTO `yoshop_region` VALUES ('3386', '3325', '台中', '台中市', '中国,台湾省,台中市', '2', 'taichung', '04', '4', 'T', '120.679040', '24.138620'); +INSERT INTO `yoshop_region` VALUES ('3387', '3386', '中区', '中区', '中国,台湾省,台中市,中区', '3', 'zhongqu', '04', '400', 'Z', '120.679510', '24.143830'); +INSERT INTO `yoshop_region` VALUES ('3388', '3386', '东区', '东区', '中国,台湾省,台中市,东区', '3', 'dongqu', '04', '401', 'D', '120.704', '24.13662'); +INSERT INTO `yoshop_region` VALUES ('3389', '3386', '南区', '南区', '中国,台湾省,台中市,南区', '3', 'nanqu', '04', '402', 'N', '120.188648', '22.960944'); +INSERT INTO `yoshop_region` VALUES ('3390', '3386', '西区', '西区', '中国,台湾省,台中市,西区', '3', 'xiqu', '04', '403', 'X', '120.67104', '24.14138'); +INSERT INTO `yoshop_region` VALUES ('3391', '3386', '北区', '北区', '中国,台湾省,台中市,北区', '3', 'beiqu', '04', '404', 'B', '120.682410', '24.166040'); +INSERT INTO `yoshop_region` VALUES ('3392', '3386', '西屯', '西屯区', '中国,台湾省,台中市,西屯区', '3', 'xitun', '04', '407', 'X', '120.639820', '24.181340'); +INSERT INTO `yoshop_region` VALUES ('3393', '3386', '南屯', '南屯区', '中国,台湾省,台中市,南屯区', '3', 'nantun', '04', '408', 'N', '120.643080', '24.138270'); +INSERT INTO `yoshop_region` VALUES ('3394', '3386', '北屯', '北屯区', '中国,台湾省,台中市,北屯区', '3', 'beitun', '04', '406', 'B', '120.686250', '24.182220'); +INSERT INTO `yoshop_region` VALUES ('3395', '3386', '丰原', '丰原区', '中国,台湾省,台中市,丰原区', '3', 'fengyuan', '04', '420', 'F', '120.718460', '24.242190'); +INSERT INTO `yoshop_region` VALUES ('3396', '3386', '东势', '东势区', '中国,台湾省,台中市,东势区', '3', 'dongshi', '04', '423', 'D', '120.827770', '24.258610'); +INSERT INTO `yoshop_region` VALUES ('3397', '3386', '大甲', '大甲区', '中国,台湾省,台中市,大甲区', '3', 'dajia', '04', '437', 'D', '120.622390', '24.348920'); +INSERT INTO `yoshop_region` VALUES ('3398', '3386', '清水', '清水区', '中国,台湾省,台中市,清水区', '3', 'qingshui', '04', '436', 'Q', '120.559780', '24.268650'); +INSERT INTO `yoshop_region` VALUES ('3399', '3386', '沙鹿', '沙鹿区', '中国,台湾省,台中市,沙鹿区', '3', 'shalu', '04', '433', 'S', '120.565700', '24.233480'); +INSERT INTO `yoshop_region` VALUES ('3400', '3386', '梧栖', '梧栖区', '中国,台湾省,台中市,梧栖区', '3', 'wuqi', '04', '435', 'W', '120.531520', '24.254960'); +INSERT INTO `yoshop_region` VALUES ('3401', '3386', '后里', '后里区', '中国,台湾省,台中市,后里区', '3', 'houli', '04', '421', 'H', '120.710710', '24.304910'); +INSERT INTO `yoshop_region` VALUES ('3402', '3386', '神冈', '神冈区', '中国,台湾省,台中市,神冈区', '3', 'shengang', '04', '429', 'S', '120.661550', '24.257770'); +INSERT INTO `yoshop_region` VALUES ('3403', '3386', '潭子', '潭子区', '中国,台湾省,台中市,潭子区', '3', 'tanzi', '04', '427', 'T', '120.705160', '24.209530'); +INSERT INTO `yoshop_region` VALUES ('3404', '3386', '大雅', '大雅区', '中国,台湾省,台中市,大雅区', '3', 'daya', '04', '428', 'D', '120.647780', '24.229230'); +INSERT INTO `yoshop_region` VALUES ('3405', '3386', '新社', '新社区', '中国,台湾省,台中市,新社区', '3', 'xinshe', '04', '426', 'X', '120.809500', '24.234140'); +INSERT INTO `yoshop_region` VALUES ('3406', '3386', '石冈', '石冈区', '中国,台湾省,台中市,石冈区', '3', 'shigang', '04', '422', 'S', '120.780410', '24.274980'); +INSERT INTO `yoshop_region` VALUES ('3407', '3386', '外埔', '外埔区', '中国,台湾省,台中市,外埔区', '3', 'waipu', '04', '438', 'W', '120.654370', '24.332010'); +INSERT INTO `yoshop_region` VALUES ('3408', '3386', '大安', '大安区', '中国,台湾省,台中市,大安区', '3', 'da\'an', '04', '439', 'D', '120.58652', '24.34607'); +INSERT INTO `yoshop_region` VALUES ('3409', '3386', '乌日', '乌日区', '中国,台湾省,台中市,乌日区', '3', 'wuri', '04', '414', 'W', '120.623810', '24.104500'); +INSERT INTO `yoshop_region` VALUES ('3410', '3386', '大肚', '大肚区', '中国,台湾省,台中市,大肚区', '3', 'dadu', '04', '432', 'D', '120.540960', '24.153660'); +INSERT INTO `yoshop_region` VALUES ('3411', '3386', '龙井', '龙井区', '中国,台湾省,台中市,龙井区', '3', 'longjing', '04', '434', 'L', '120.545940', '24.192710'); +INSERT INTO `yoshop_region` VALUES ('3412', '3386', '雾峰', '雾峰区', '中国,台湾省,台中市,雾峰区', '3', 'wufeng', '04', '413', 'W', '120.700200', '24.061520'); +INSERT INTO `yoshop_region` VALUES ('3413', '3386', '太平', '太平区', '中国,台湾省,台中市,太平区', '3', 'taiping', '04', '411', 'T', '120.718523', '24.126472'); +INSERT INTO `yoshop_region` VALUES ('3414', '3386', '大里', '大里区', '中国,台湾省,台中市,大里区', '3', 'dali', '04', '412', 'D', '120.677860', '24.099390'); +INSERT INTO `yoshop_region` VALUES ('3415', '3386', '和平', '和平区', '中国,台湾省,台中市,和平区', '3', 'heping', '04', '424', 'H', '120.88349', '24.17477'); +INSERT INTO `yoshop_region` VALUES ('3416', '3325', '台南', '台南市', '中国,台湾省,台南市', '2', 'tainan', '06', '7', 'T', '120.279363', '23.172478'); +INSERT INTO `yoshop_region` VALUES ('3417', '3416', '东区', '东区', '中国,台湾省,台南市,东区', '3', 'dongqu', '06', '701', 'D', '120.224198', '22.980072'); +INSERT INTO `yoshop_region` VALUES ('3418', '3416', '南区', '南区', '中国,台湾省,台南市,南区', '3', 'nanqu', '06', '702', 'N', '120.188648', '22.960944'); +INSERT INTO `yoshop_region` VALUES ('3419', '3416', '北区', '北区', '中国,台湾省,台南市,北区', '3', 'beiqu', '06', '704', 'B', '120.682410', '24.166040'); +INSERT INTO `yoshop_region` VALUES ('3420', '3416', '安南', '安南区', '中国,台湾省,台南市,安南区', '3', 'annan', '06', '709', 'A', '120.184617', '23.047230'); +INSERT INTO `yoshop_region` VALUES ('3421', '3416', '安平', '安平区', '中国,台湾省,台南市,安平区', '3', 'anping', '06', '708', 'A', '120.166810', '23.000763'); +INSERT INTO `yoshop_region` VALUES ('3422', '3416', '中西', '中西区', '中国,台湾省,台南市,中西区', '3', 'zhongxi', '06', '700', 'Z', '120.205957', '22.992152'); +INSERT INTO `yoshop_region` VALUES ('3423', '3416', '新营', '新营区', '中国,台湾省,台南市,新营区', '3', 'xinying', '06', '730', 'X', '120.316698', '23.310274'); +INSERT INTO `yoshop_region` VALUES ('3424', '3416', '盐水', '盐水区', '中国,台湾省,台南市,盐水区', '3', 'yanshui', '06', '737', 'Y', '120.266398', '23.319828'); +INSERT INTO `yoshop_region` VALUES ('3425', '3416', '白河', '白河区', '中国,台湾省,台南市,白河区', '3', 'baihe', '06', '732', 'B', '120.415810', '23.351221'); +INSERT INTO `yoshop_region` VALUES ('3426', '3416', '柳营', '柳营区', '中国,台湾省,台南市,柳营区', '3', 'liuying', '06', '736', 'L', '120.311286', '23.278133'); +INSERT INTO `yoshop_region` VALUES ('3427', '3416', '后壁', '后壁区', '中国,台湾省,台南市,后壁区', '3', 'houbi', '06', '731', 'H', '120.362726', '23.366721'); +INSERT INTO `yoshop_region` VALUES ('3428', '3416', '东山', '东山区', '中国,台湾省,台南市,东山区', '3', 'dongshan', '06', '733', 'D', '120.403984', '23.326092'); +INSERT INTO `yoshop_region` VALUES ('3429', '3416', '麻豆', '麻豆区', '中国,台湾省,台南市,麻豆区', '3', 'madou', '06', '721', 'M', '120.248179', '23.181680'); +INSERT INTO `yoshop_region` VALUES ('3430', '3416', '下营', '下营区', '中国,台湾省,台南市,下营区', '3', 'xiaying', '06', '735', 'X', '120.264484', '23.235413'); +INSERT INTO `yoshop_region` VALUES ('3431', '3416', '六甲', '六甲区', '中国,台湾省,台南市,六甲区', '3', 'liujia', '06', '734', 'L', '120.347600', '23.231931'); +INSERT INTO `yoshop_region` VALUES ('3432', '3416', '官田', '官田区', '中国,台湾省,台南市,官田区', '3', 'guantian', '06', '720', 'G', '120.314374', '23.194652'); +INSERT INTO `yoshop_region` VALUES ('3433', '3416', '大内', '大内区', '中国,台湾省,台南市,大内区', '3', 'dana', '06', '742', 'D', '120.348853', '23.119460'); +INSERT INTO `yoshop_region` VALUES ('3434', '3416', '佳里', '佳里区', '中国,台湾省,台南市,佳里区', '3', 'jiali', '06', '722', 'J', '120.177211', '23.165121'); +INSERT INTO `yoshop_region` VALUES ('3435', '3416', '学甲', '学甲区', '中国,台湾省,台南市,学甲区', '3', 'xuejia', '06', '726', 'X', '120.180255', '23.232348'); +INSERT INTO `yoshop_region` VALUES ('3436', '3416', '西港', '西港区', '中国,台湾省,台南市,西港区', '3', 'xigang', '06', '723', 'X', '120.203618', '23.123057'); +INSERT INTO `yoshop_region` VALUES ('3437', '3416', '七股', '七股区', '中国,台湾省,台南市,七股区', '3', 'qigu', '06', '724', 'Q', '120.140003', '23.140545'); +INSERT INTO `yoshop_region` VALUES ('3438', '3416', '将军', '将军区', '中国,台湾省,台南市,将军区', '3', 'jiangjun', '06', '725', 'J', '120.156871', '23.199543'); +INSERT INTO `yoshop_region` VALUES ('3439', '3416', '北门', '北门区', '中国,台湾省,台南市,北门区', '3', 'beimen', '06', '727', 'B', '120.125821', '23.267148'); +INSERT INTO `yoshop_region` VALUES ('3440', '3416', '新化', '新化区', '中国,台湾省,台南市,新化区', '3', 'xinhua', '06', '712', 'X', '120.310807', '23.038533'); +INSERT INTO `yoshop_region` VALUES ('3441', '3416', '善化', '善化区', '中国,台湾省,台南市,善化区', '3', 'shanhua', '06', '741', 'S', '120.296895', '23.132261'); +INSERT INTO `yoshop_region` VALUES ('3442', '3416', '新市', '新市区', '中国,台湾省,台南市,新市区', '3', 'xinshi', '06', '744', 'X', '120.295138', '23.07897'); +INSERT INTO `yoshop_region` VALUES ('3443', '3416', '安定', '安定区', '中国,台湾省,台南市,安定区', '3', 'anding', '06', '745', 'A', '120.237083', '23.121498'); +INSERT INTO `yoshop_region` VALUES ('3444', '3416', '山上', '山上区', '中国,台湾省,台南市,山上区', '3', 'shanshang', '06', '743', 'S', '120.352908', '23.103223'); +INSERT INTO `yoshop_region` VALUES ('3445', '3416', '玉井', '玉井区', '中国,台湾省,台南市,玉井区', '3', 'yujing', '06', '714', 'Y', '120.460110', '23.123859'); +INSERT INTO `yoshop_region` VALUES ('3446', '3416', '楠西', '楠西区', '中国,台湾省,台南市,楠西区', '3', 'nanxi', '06', '715', null, '120.485396', '23.173454'); +INSERT INTO `yoshop_region` VALUES ('3447', '3416', '南化', '南化区', '中国,台湾省,台南市,南化区', '3', 'nanhua', '06', '716', 'N', '120.477116', '23.042614'); +INSERT INTO `yoshop_region` VALUES ('3448', '3416', '左镇', '左镇区', '中国,台湾省,台南市,左镇区', '3', 'zuozhen', '06', '713', 'Z', '120.407309', '23.057955'); +INSERT INTO `yoshop_region` VALUES ('3449', '3416', '仁德', '仁德区', '中国,台湾省,台南市,仁德区', '3', 'rende', '06', '717', 'R', '120.251520', '22.972212'); +INSERT INTO `yoshop_region` VALUES ('3450', '3416', '归仁', '归仁区', '中国,台湾省,台南市,归仁区', '3', 'guiren', '06', '711', 'G', '120.293791', '22.967081'); +INSERT INTO `yoshop_region` VALUES ('3451', '3416', '关庙', '关庙区', '中国,台湾省,台南市,关庙区', '3', 'guanmiao', '06', '718', 'G', '120.327689', '22.962949'); +INSERT INTO `yoshop_region` VALUES ('3452', '3416', '龙崎', '龙崎区', '中国,台湾省,台南市,龙崎区', '3', 'longqi', '06', '719', 'L', '120.360824', '22.965681'); +INSERT INTO `yoshop_region` VALUES ('3453', '3416', '永康', '永康区', '中国,台湾省,台南市,永康区', '3', 'yongkang', '06', '710', 'Y', '120.257069', '23.026061'); +INSERT INTO `yoshop_region` VALUES ('3454', '3325', '新竹', '新竹市', '中国,台湾省,新竹市', '2', 'hsinchu', '03', '3', 'X', '120.968798', '24.806738'); +INSERT INTO `yoshop_region` VALUES ('3455', '3454', '东区', '东区', '中国,台湾省,新竹市,东区', '3', 'dongqu', '03', '300', 'D', '120.970239', '24.801337'); +INSERT INTO `yoshop_region` VALUES ('3456', '3454', '北区', '北区', '中国,台湾省,新竹市,北区', '3', 'beiqu', '03', '', 'B', '120.682410', '24.166040'); +INSERT INTO `yoshop_region` VALUES ('3457', '3454', '香山', '香山区', '中国,台湾省,新竹市,香山区', '3', 'xiangshan', '03', '', 'X', '120.956679', '24.768933'); +INSERT INTO `yoshop_region` VALUES ('3458', '3325', '嘉义', '嘉义市', '中国,台湾省,嘉义市', '2', 'chiayi', '05', '6', 'J', '120.452538', '23.481568'); +INSERT INTO `yoshop_region` VALUES ('3459', '3458', '东区', '东区', '中国,台湾省,嘉义市,东区', '3', 'dongqu', '05', '600', 'D', '120.458009', '23.486213'); +INSERT INTO `yoshop_region` VALUES ('3460', '3458', '西区', '西区', '中国,台湾省,嘉义市,西区', '3', 'xiqu', '05', '600', 'X', '120.437493', '23.473029'); +INSERT INTO `yoshop_region` VALUES ('3461', '3325', '新北', '新北市', '中国,台湾省,新北市', '2', 'newtaipei', '02', '2', 'X', '121.465746', '25.012366'); +INSERT INTO `yoshop_region` VALUES ('3462', '3461', '板桥', '板桥区', '中国,台湾省,新北市,板桥区', '3', 'banqiao', '02', '220', 'B', '121.459084', '25.009578'); +INSERT INTO `yoshop_region` VALUES ('3463', '3461', '三重', '三重区', '中国,台湾省,新北市,三重区', '3', 'sanzhong', '02', '241', 'S', '121.488102', '25.061486'); +INSERT INTO `yoshop_region` VALUES ('3464', '3461', '中和', '中和区', '中国,台湾省,新北市,中和区', '3', 'zhonghe', '02', '235', 'Z', '121.498980', '24.999397'); +INSERT INTO `yoshop_region` VALUES ('3465', '3461', '永和', '永和区', '中国,台湾省,新北市,永和区', '3', 'yonghe', '02', '234', 'Y', '121.513660', '25.007802'); +INSERT INTO `yoshop_region` VALUES ('3466', '3461', '新庄', '新庄区', '中国,台湾省,新北市,新庄区', '3', 'xinzhuang', '02', '242', 'X', '121.450413', '25.035947'); +INSERT INTO `yoshop_region` VALUES ('3467', '3461', '新店', '新店区', '中国,台湾省,新北市,新店区', '3', 'xindian', '02', '231', 'X', '121.541750', '24.967558'); +INSERT INTO `yoshop_region` VALUES ('3468', '3461', '树林', '树林区', '中国,台湾省,新北市,树林区', '3', 'shulin', '02', '238', 'S', '121.420533', '24.990706'); +INSERT INTO `yoshop_region` VALUES ('3469', '3461', '莺歌', '莺歌区', '中国,台湾省,新北市,莺歌区', '3', 'yingge', '02', '239', null, '121.354573', '24.955413'); +INSERT INTO `yoshop_region` VALUES ('3470', '3461', '三峡', '三峡区', '中国,台湾省,新北市,三峡区', '3', 'sanxia', '02', '237', 'S', '121.368905', '24.934339'); +INSERT INTO `yoshop_region` VALUES ('3471', '3461', '淡水', '淡水区', '中国,台湾省,新北市,淡水区', '3', 'danshui', '02', '251', 'D', '121.440915', '25.169452'); +INSERT INTO `yoshop_region` VALUES ('3472', '3461', '汐止', '汐止区', '中国,台湾省,新北市,汐止区', '3', 'xizhi', '02', '221', 'X', '121.629470', '25.062999'); +INSERT INTO `yoshop_region` VALUES ('3473', '3461', '瑞芳', '瑞芳区', '中国,台湾省,新北市,瑞芳区', '3', 'ruifang', '02', '224', 'R', '121.810061', '25.108895'); +INSERT INTO `yoshop_region` VALUES ('3474', '3461', '土城', '土城区', '中国,台湾省,新北市,土城区', '3', 'tucheng', '02', '236', 'T', '121.443348', '24.972201'); +INSERT INTO `yoshop_region` VALUES ('3475', '3461', '芦洲', '芦洲区', '中国,台湾省,新北市,芦洲区', '3', 'luzhou', '02', '247', 'L', '121.473700', '25.084923'); +INSERT INTO `yoshop_region` VALUES ('3476', '3461', '五股', '五股区', '中国,台湾省,新北市,五股区', '3', 'wugu', '02', '248', 'W', '121.438156', '25.082743'); +INSERT INTO `yoshop_region` VALUES ('3477', '3461', '泰山', '泰山区', '中国,台湾省,新北市,泰山区', '3', 'taishan', '02', '243', 'T', '121.430811', '25.058864'); +INSERT INTO `yoshop_region` VALUES ('3478', '3461', '林口', '林口区', '中国,台湾省,新北市,林口区', '3', 'linkou', '02', '244', 'L', '121.391602', '25.077531'); +INSERT INTO `yoshop_region` VALUES ('3479', '3461', '深坑', '深坑区', '中国,台湾省,新北市,深坑区', '3', 'shenkeng', '02', '222', 'S', '121.615670', '25.002329'); +INSERT INTO `yoshop_region` VALUES ('3480', '3461', '石碇', '石碇区', '中国,台湾省,新北市,石碇区', '3', 'shiding', '02', '223', 'S', '121.658567', '24.991679'); +INSERT INTO `yoshop_region` VALUES ('3481', '3461', '坪林', '坪林区', '中国,台湾省,新北市,坪林区', '3', 'pinglin', '02', '232', 'P', '121.711185', '24.937388'); +INSERT INTO `yoshop_region` VALUES ('3482', '3461', '三芝', '三芝区', '中国,台湾省,新北市,三芝区', '3', 'sanzhi', '02', '252', 'S', '121.500866', '25.258047'); +INSERT INTO `yoshop_region` VALUES ('3483', '3461', '石门', '石门区', '中国,台湾省,新北市,石门区', '3', 'shimen', '02', '253', 'S', '121.568491', '25.290412'); +INSERT INTO `yoshop_region` VALUES ('3484', '3461', '八里', '八里区', '中国,台湾省,新北市,八里区', '3', 'bali', '02', '249', 'B', '121.398227', '25.146680'); +INSERT INTO `yoshop_region` VALUES ('3485', '3461', '平溪', '平溪区', '中国,台湾省,新北市,平溪区', '3', 'pingxi', '02', '226', 'P', '121.738255', '25.025725'); +INSERT INTO `yoshop_region` VALUES ('3486', '3461', '双溪', '双溪区', '中国,台湾省,新北市,双溪区', '3', 'shuangxi', '02', '227', 'S', '121.865676', '25.033409'); +INSERT INTO `yoshop_region` VALUES ('3487', '3461', '贡寮', '贡寮区', '中国,台湾省,新北市,贡寮区', '3', 'gongliao', '02', '228', 'G', '121.908185', '25.022388'); +INSERT INTO `yoshop_region` VALUES ('3488', '3461', '金山', '金山区', '中国,台湾省,新北市,金山区', '3', 'jinshan', '02', '208', 'J', '121.636427', '25.221883'); +INSERT INTO `yoshop_region` VALUES ('3489', '3461', '万里', '万里区', '中国,台湾省,新北市,万里区', '3', 'wanli', '02', '207', 'W', '121.688687', '25.181234'); +INSERT INTO `yoshop_region` VALUES ('3490', '3461', '乌来', '乌来区', '中国,台湾省,新北市,乌来区', '3', 'wulai', '02', '233', 'W', '121.550531', '24.865296'); +INSERT INTO `yoshop_region` VALUES ('3491', '3325', '宜兰', '宜兰县', '中国,台湾省,宜兰县', '2', 'yilan', '03', '2', 'Y', '121.500000', '24.600000'); +INSERT INTO `yoshop_region` VALUES ('3492', '3491', '宜兰', '宜兰市', '中国,台湾省,宜兰县,宜兰市', '3', 'yilan', '03', '260', 'Y', '121.753476', '24.751682'); +INSERT INTO `yoshop_region` VALUES ('3493', '3491', '罗东', '罗东镇', '中国,台湾省,宜兰县,罗东镇', '3', 'luodong', '03', '265', 'L', '121.766919', '24.677033'); +INSERT INTO `yoshop_region` VALUES ('3494', '3491', '苏澳', '苏澳镇', '中国,台湾省,宜兰县,苏澳镇', '3', 'suao', '03', '270', 'S', '121.842656', '24.594622'); +INSERT INTO `yoshop_region` VALUES ('3495', '3491', '头城', '头城镇', '中国,台湾省,宜兰县,头城镇', '3', 'toucheng', '03', '261', 'T', '121.823307', '24.859217'); +INSERT INTO `yoshop_region` VALUES ('3496', '3491', '礁溪', '礁溪乡', '中国,台湾省,宜兰县,礁溪乡', '3', 'jiaoxi', '03', '262', 'J', '121.766680', '24.822345'); +INSERT INTO `yoshop_region` VALUES ('3497', '3491', '壮围', '壮围乡', '中国,台湾省,宜兰县,壮围乡', '3', 'zhuangwei', '03', '263', 'Z', '121.781619', '24.744949'); +INSERT INTO `yoshop_region` VALUES ('3498', '3491', '员山', '员山乡', '中国,台湾省,宜兰县,员山乡', '3', 'yuanshan', '03', '264', 'Y', '121.721733', '24.741771'); +INSERT INTO `yoshop_region` VALUES ('3499', '3491', '冬山', '冬山乡', '中国,台湾省,宜兰县,冬山乡', '3', 'dongshan', '03', '269', 'D', '121.792280', '24.634514'); +INSERT INTO `yoshop_region` VALUES ('3500', '3491', '五结', '五结乡', '中国,台湾省,宜兰县,五结乡', '3', 'wujie', '03', '268', 'W', '121.798297', '24.684640'); +INSERT INTO `yoshop_region` VALUES ('3501', '3491', '三星', '三星乡', '中国,台湾省,宜兰县,三星乡', '3', 'sanxing', '03', '266', 'S', '121.003418', '23.775291'); +INSERT INTO `yoshop_region` VALUES ('3502', '3491', '大同', '大同乡', '中国,台湾省,宜兰县,大同乡', '3', 'datong', '03', '267', 'D', '121.605557', '24.675997'); +INSERT INTO `yoshop_region` VALUES ('3503', '3491', '南澳', '南澳乡', '中国,台湾省,宜兰县,南澳乡', '3', 'nanao', '03', '272', 'N', '121.799810', '24.465393'); +INSERT INTO `yoshop_region` VALUES ('3504', '3325', '桃园', '桃园县', '中国,台湾省,桃园县', '2', 'taoyuan', '03', '3', 'T', '121.083000', '25.000000'); +INSERT INTO `yoshop_region` VALUES ('3505', '3504', '桃园', '桃园市', '中国,台湾省,桃园县,桃园市', '3', 'taoyuan', '03', '330', 'T', '121.301337', '24.993777'); +INSERT INTO `yoshop_region` VALUES ('3506', '3504', '中坜', '中坜市', '中国,台湾省,桃园县,中坜市', '3', 'zhongli', '03', '320', 'Z', '121.224926', '24.965353'); +INSERT INTO `yoshop_region` VALUES ('3507', '3504', '平镇', '平镇市', '中国,台湾省,桃园县,平镇市', '3', 'pingzhen', '03', '324', 'P', '121.218359', '24.945752'); +INSERT INTO `yoshop_region` VALUES ('3508', '3504', '八德', '八德市', '中国,台湾省,桃园县,八德市', '3', 'bade', '03', '334', 'B', '121.284655', '24.928651'); +INSERT INTO `yoshop_region` VALUES ('3509', '3504', '杨梅', '杨梅市', '中国,台湾省,桃园县,杨梅市', '3', 'yangmei', '03', '326', 'Y', '121.145873', '24.907575'); +INSERT INTO `yoshop_region` VALUES ('3510', '3504', '芦竹', '芦竹市', '中国,台湾省,桃园县,芦竹市', '3', 'luzhu', '03', '338', 'L', '121.292064', '25.045392'); +INSERT INTO `yoshop_region` VALUES ('3511', '3504', '大溪', '大溪镇', '中国,台湾省,桃园县,大溪镇', '3', 'daxi', '03', '335', 'D', '121.286962', '24.880584'); +INSERT INTO `yoshop_region` VALUES ('3512', '3504', '大园', '大园乡', '中国,台湾省,桃园县,大园乡', '3', 'dayuan', '03', '337', 'D', '121.196292', '25.064471'); +INSERT INTO `yoshop_region` VALUES ('3513', '3504', '龟山', '龟山乡', '中国,台湾省,桃园县,龟山乡', '3', 'guishan', '03', '333', 'G', '121.337767', '24.992517'); +INSERT INTO `yoshop_region` VALUES ('3514', '3504', '龙潭', '龙潭乡', '中国,台湾省,桃园县,龙潭乡', '3', 'longtan', '03', '325', 'L', '121.216392', '24.863851'); +INSERT INTO `yoshop_region` VALUES ('3515', '3504', '新屋', '新屋乡', '中国,台湾省,桃园县,新屋乡', '3', 'xinwu', '03', '327', 'X', '121.105801', '24.972203'); +INSERT INTO `yoshop_region` VALUES ('3516', '3504', '观音', '观音乡', '中国,台湾省,桃园县,观音乡', '3', 'guanyin', '03', '328', 'G', '121.077519', '25.033303'); +INSERT INTO `yoshop_region` VALUES ('3517', '3504', '复兴', '复兴乡', '中国,台湾省,桃园县,复兴乡', '3', 'fuxing', '03', '336', 'F', '121.352613', '24.820908'); +INSERT INTO `yoshop_region` VALUES ('3518', '3325', '新竹', '新竹县', '中国,台湾省,新竹县', '2', 'hsinchu', '03', '3', 'X', '121.160000', '24.600000'); +INSERT INTO `yoshop_region` VALUES ('3519', '3518', '竹北', '竹北市', '中国,台湾省,新竹县,竹北市', '3', 'zhubei', '03', '302', 'Z', '121.004317', '24.839652'); +INSERT INTO `yoshop_region` VALUES ('3520', '3518', '竹东', '竹东镇', '中国,台湾省,新竹县,竹东镇', '3', 'zhudong', '03', '310', 'Z', '121.086418', '24.733601'); +INSERT INTO `yoshop_region` VALUES ('3521', '3518', '新埔', '新埔镇', '中国,台湾省,新竹县,新埔镇', '3', 'xinpu', '03', '305', 'X', '121.072804', '24.824820'); +INSERT INTO `yoshop_region` VALUES ('3522', '3518', '关西', '关西镇', '中国,台湾省,新竹县,关西镇', '3', 'guanxi', '03', '306', 'G', '121.177301', '24.788842'); +INSERT INTO `yoshop_region` VALUES ('3523', '3518', '湖口', '湖口乡', '中国,台湾省,新竹县,湖口乡', '3', 'hukou', '03', '303', 'H', '121.043691', '24.903943'); +INSERT INTO `yoshop_region` VALUES ('3524', '3518', '新丰', '新丰乡', '中国,台湾省,新竹县,新丰乡', '3', 'xinfeng', '03', '304', 'X', '120.983006', '24.899600'); +INSERT INTO `yoshop_region` VALUES ('3525', '3518', '芎林', '芎林乡', '中国,台湾省,新竹县,芎林乡', '3', 'xionglin', '03', '307', null, '121.076924', '24.774436'); +INSERT INTO `yoshop_region` VALUES ('3526', '3518', '横山', '横山乡', '中国,台湾省,新竹县,横山乡', '3', 'hengshan', '03', '312', 'H', '121.116244', '24.720807'); +INSERT INTO `yoshop_region` VALUES ('3527', '3518', '北埔', '北埔乡', '中国,台湾省,新竹县,北埔乡', '3', 'beipu', '03', '314', 'B', '121.053156', '24.697126'); +INSERT INTO `yoshop_region` VALUES ('3528', '3518', '宝山', '宝山乡', '中国,台湾省,新竹县,宝山乡', '3', 'baoshan', '03', '308', 'B', '120.985752', '24.760975'); +INSERT INTO `yoshop_region` VALUES ('3529', '3518', '峨眉', '峨眉乡', '中国,台湾省,新竹县,峨眉乡', '3', 'emei', '03', '315', 'E', '121.015291', '24.686127'); +INSERT INTO `yoshop_region` VALUES ('3530', '3518', '尖石', '尖石乡', '中国,台湾省,新竹县,尖石乡', '3', 'jianshi', '03', '313', 'J', '121.197802', '24.704360'); +INSERT INTO `yoshop_region` VALUES ('3531', '3518', '五峰', '五峰乡', '中国,台湾省,新竹县,五峰乡', '3', 'wufeng', '03', '311', 'W', '121.003418', '23.775291'); +INSERT INTO `yoshop_region` VALUES ('3532', '3325', '苗栗', '苗栗县', '中国,台湾省,苗栗县', '2', 'miaoli', '037', '3', 'M', '120.750000', '24.500000'); +INSERT INTO `yoshop_region` VALUES ('3533', '3532', '苗栗', '苗栗市', '中国,台湾省,苗栗县,苗栗市', '3', 'miaoli', '037', '360', 'M', '120.818869', '24.561472'); +INSERT INTO `yoshop_region` VALUES ('3534', '3532', '苑里', '苑里镇', '中国,台湾省,苗栗县,苑里镇', '3', 'yuanli', '037', '358', 'Y', '120.648907', '24.441750'); +INSERT INTO `yoshop_region` VALUES ('3535', '3532', '通霄', '通霄镇', '中国,台湾省,苗栗县,通霄镇', '3', 'tongxiao', '037', '357', 'T', '120.676700', '24.489087'); +INSERT INTO `yoshop_region` VALUES ('3536', '3532', '竹南', '竹南镇', '中国,台湾省,苗栗县,竹南镇', '3', 'zhunan', '037', '350', 'Z', '120.872641', '24.685513'); +INSERT INTO `yoshop_region` VALUES ('3537', '3532', '头份', '头份镇', '中国,台湾省,苗栗县,头份镇', '3', 'toufen', '037', '351', 'T', '120.895188', '24.687993'); +INSERT INTO `yoshop_region` VALUES ('3538', '3532', '后龙', '后龙镇', '中国,台湾省,苗栗县,后龙镇', '3', 'houlong', '037', '356', 'H', '120.786480', '24.612617'); +INSERT INTO `yoshop_region` VALUES ('3539', '3532', '卓兰', '卓兰镇', '中国,台湾省,苗栗县,卓兰镇', '3', 'zhuolan', '037', '369', 'Z', '120.823441', '24.309509'); +INSERT INTO `yoshop_region` VALUES ('3540', '3532', '大湖', '大湖乡', '中国,台湾省,苗栗县,大湖乡', '3', 'dahu', '037', '364', 'D', '120.863641', '24.422547'); +INSERT INTO `yoshop_region` VALUES ('3541', '3532', '公馆', '公馆乡', '中国,台湾省,苗栗县,公馆乡', '3', 'gongguan', '037', '363', 'G', '120.822983', '24.499108'); +INSERT INTO `yoshop_region` VALUES ('3542', '3532', '铜锣', '铜锣乡', '中国,台湾省,苗栗县,铜锣乡', '3', 'tongluo', '037', '366', 'T', '121.003418', '23.775291'); +INSERT INTO `yoshop_region` VALUES ('3543', '3532', '南庄', '南庄乡', '中国,台湾省,苗栗县,南庄乡', '3', 'nanzhuang', '037', '353', 'N', '120.994957', '24.596835'); +INSERT INTO `yoshop_region` VALUES ('3544', '3532', '头屋', '头屋乡', '中国,台湾省,苗栗县,头屋乡', '3', 'touwu', '037', '362', 'T', '120.846616', '24.574249'); +INSERT INTO `yoshop_region` VALUES ('3545', '3532', '三义', '三义乡', '中国,台湾省,苗栗县,三义乡', '3', 'sanyi', '037', '367', 'S', '120.742340', '24.350270'); +INSERT INTO `yoshop_region` VALUES ('3546', '3532', '西湖', '西湖乡', '中国,台湾省,苗栗县,西湖乡', '3', 'xihu', '037', '368', 'X', '121.003418', '23.775291'); +INSERT INTO `yoshop_region` VALUES ('3547', '3532', '造桥', '造桥乡', '中国,台湾省,苗栗县,造桥乡', '3', 'zaoqiao', '037', '361', 'Z', '120.862399', '24.637537'); +INSERT INTO `yoshop_region` VALUES ('3548', '3532', '三湾', '三湾乡', '中国,台湾省,苗栗县,三湾乡', '3', 'sanwan', '037', '352', 'S', '120.951484', '24.651051'); +INSERT INTO `yoshop_region` VALUES ('3549', '3532', '狮潭', '狮潭乡', '中国,台湾省,苗栗县,狮潭乡', '3', 'shitan', '037', '354', 'S', '120.918024', '24.540004'); +INSERT INTO `yoshop_region` VALUES ('3550', '3532', '泰安', '泰安乡', '中国,台湾省,苗栗县,泰安乡', '3', 'tai\'an', '037', '365', 'T', '120.904441', '24.442600'); +INSERT INTO `yoshop_region` VALUES ('3551', '3325', '彰化', '彰化县', '中国,台湾省,彰化县', '2', 'changhua', '04', '5', 'Z', '120.416000', '24.000000'); +INSERT INTO `yoshop_region` VALUES ('3552', '3551', '彰化市', '彰化市', '中国,台湾省,彰化县,彰化市', '3', 'zhanghuashi', '04', '500', 'Z', '120.542294', '24.080911'); +INSERT INTO `yoshop_region` VALUES ('3553', '3551', '鹿港', '鹿港镇', '中国,台湾省,彰化县,鹿港镇', '3', 'lugang', '04', '505', 'L', '120.435392', '24.056937'); +INSERT INTO `yoshop_region` VALUES ('3554', '3551', '和美', '和美镇', '中国,台湾省,彰化县,和美镇', '3', 'hemei', '04', '508', 'H', '120.500265', '24.110904'); +INSERT INTO `yoshop_region` VALUES ('3555', '3551', '线西', '线西乡', '中国,台湾省,彰化县,线西乡', '3', 'xianxi', '04', '507', 'X', '120.465921', '24.128653'); +INSERT INTO `yoshop_region` VALUES ('3556', '3551', '伸港', '伸港乡', '中国,台湾省,彰化县,伸港乡', '3', 'shengang', '04', '509', 'S', '120.484224', '24.146081'); +INSERT INTO `yoshop_region` VALUES ('3557', '3551', '福兴', '福兴乡', '中国,台湾省,彰化县,福兴乡', '3', 'fuxing', '04', '506', 'F', '120.443682', '24.047883'); +INSERT INTO `yoshop_region` VALUES ('3558', '3551', '秀水', '秀水乡', '中国,台湾省,彰化县,秀水乡', '3', 'xiushui', '04', '504', 'X', '120.502658', '24.035267'); +INSERT INTO `yoshop_region` VALUES ('3559', '3551', '花坛', '花坛乡', '中国,台湾省,彰化县,花坛乡', '3', 'huatan', '04', '503', 'H', '120.538403', '24.029399'); +INSERT INTO `yoshop_region` VALUES ('3560', '3551', '芬园', '芬园乡', '中国,台湾省,彰化县,芬园乡', '3', 'fenyuan', '04', '502', 'F', '120.629024', '24.013658'); +INSERT INTO `yoshop_region` VALUES ('3561', '3551', '员林', '员林镇', '中国,台湾省,彰化县,员林镇', '3', 'yuanlin', '04', '510', 'Y', '120.574625', '23.958999'); +INSERT INTO `yoshop_region` VALUES ('3562', '3551', '溪湖', '溪湖镇', '中国,台湾省,彰化县,溪湖镇', '3', 'xihu', '04', '514', 'X', '120.479144', '23.962315'); +INSERT INTO `yoshop_region` VALUES ('3563', '3551', '田中', '田中镇', '中国,台湾省,彰化县,田中镇', '3', 'tianzhong', '04', '520', 'T', '120.580629', '23.861718'); +INSERT INTO `yoshop_region` VALUES ('3564', '3551', '大村', '大村乡', '中国,台湾省,彰化县,大村乡', '3', 'dacun', '04', '515', 'D', '120.540713', '23.993726'); +INSERT INTO `yoshop_region` VALUES ('3565', '3551', '埔盐', '埔盐乡', '中国,台湾省,彰化县,埔盐乡', '3', 'puyan', '04', '516', 'P', '120.464044', '24.000346'); +INSERT INTO `yoshop_region` VALUES ('3566', '3551', '埔心', '埔心乡', '中国,台湾省,彰化县,埔心乡', '3', 'puxin', '04', '513', 'P', '120.543568', '23.953019'); +INSERT INTO `yoshop_region` VALUES ('3567', '3551', '永靖', '永靖乡', '中国,台湾省,彰化县,永靖乡', '3', 'yongjing', '04', '512', 'Y', '120.547775', '23.924703'); +INSERT INTO `yoshop_region` VALUES ('3568', '3551', '社头', '社头乡', '中国,台湾省,彰化县,社头乡', '3', 'shetou', '04', '511', 'S', '120.582681', '23.896686'); +INSERT INTO `yoshop_region` VALUES ('3569', '3551', '二水', '二水乡', '中国,台湾省,彰化县,二水乡', '3', 'ershui', '04', '530', 'E', '120.618788', '23.806995'); +INSERT INTO `yoshop_region` VALUES ('3570', '3551', '北斗', '北斗镇', '中国,台湾省,彰化县,北斗镇', '3', 'beidou', '04', '521', 'B', '120.520449', '23.870911'); +INSERT INTO `yoshop_region` VALUES ('3571', '3551', '二林', '二林镇', '中国,台湾省,彰化县,二林镇', '3', 'erlin', '04', '526', 'E', '120.374468', '23.899751'); +INSERT INTO `yoshop_region` VALUES ('3572', '3551', '田尾', '田尾乡', '中国,台湾省,彰化县,田尾乡', '3', 'tianwei', '04', '522', 'T', '120.524717', '23.890735'); +INSERT INTO `yoshop_region` VALUES ('3573', '3551', '埤头', '埤头乡', '中国,台湾省,彰化县,埤头乡', '3', 'pitou', '04', '523', null, '120.462599', '23.891324'); +INSERT INTO `yoshop_region` VALUES ('3574', '3551', '芳苑', '芳苑乡', '中国,台湾省,彰化县,芳苑乡', '3', 'fangyuan', '04', '528', 'F', '120.320329', '23.924222'); +INSERT INTO `yoshop_region` VALUES ('3575', '3551', '大城', '大城乡', '中国,台湾省,彰化县,大城乡', '3', 'dacheng', '04', '527', 'D', '120.320934', '23.852382'); +INSERT INTO `yoshop_region` VALUES ('3576', '3551', '竹塘', '竹塘乡', '中国,台湾省,彰化县,竹塘乡', '3', 'zhutang', '04', '525', 'Z', '120.427499', '23.860112'); +INSERT INTO `yoshop_region` VALUES ('3577', '3551', '溪州', '溪州乡', '中国,台湾省,彰化县,溪州乡', '3', 'xizhou', '04', '524', 'X', '120.498706', '23.851229'); +INSERT INTO `yoshop_region` VALUES ('3578', '3325', '南投', '南投县', '中国,台湾省,南投县', '2', 'nantou', '049', '5', 'N', '120.830000', '23.830000'); +INSERT INTO `yoshop_region` VALUES ('3579', '3578', '南投市', '南投市', '中国,台湾省,南投县,南投市', '3', 'nantoushi', '049', '540', 'N', '120.683706', '23.909956'); +INSERT INTO `yoshop_region` VALUES ('3580', '3578', '埔里', '埔里镇', '中国,台湾省,南投县,埔里镇', '3', 'puli', '049', '545', 'P', '120.964648', '23.964789'); +INSERT INTO `yoshop_region` VALUES ('3581', '3578', '草屯', '草屯镇', '中国,台湾省,南投县,草屯镇', '3', 'caotun', '049', '542', 'C', '120.680343', '23.973947'); +INSERT INTO `yoshop_region` VALUES ('3582', '3578', '竹山', '竹山镇', '中国,台湾省,南投县,竹山镇', '3', 'zhushan', '049', '557', 'Z', '120.672007', '23.757655'); +INSERT INTO `yoshop_region` VALUES ('3583', '3578', '集集', '集集镇', '中国,台湾省,南投县,集集镇', '3', 'jiji', '049', '552', 'J', '120.783673', '23.829013'); +INSERT INTO `yoshop_region` VALUES ('3584', '3578', '名间', '名间乡', '中国,台湾省,南投县,名间乡', '3', 'mingjian', '049', '551', 'M', '120.702797', '23.838427'); +INSERT INTO `yoshop_region` VALUES ('3585', '3578', '鹿谷', '鹿谷乡', '中国,台湾省,南投县,鹿谷乡', '3', 'lugu', '049', '558', 'L', '120.752796', '23.744471'); +INSERT INTO `yoshop_region` VALUES ('3586', '3578', '中寮', '中寮乡', '中国,台湾省,南投县,中寮乡', '3', 'zhongliao', '049', '541', 'Z', '120.766654', '23.878935'); +INSERT INTO `yoshop_region` VALUES ('3587', '3578', '鱼池', '鱼池乡', '中国,台湾省,南投县,鱼池乡', '3', 'yuchi', '049', '555', 'Y', '120.936060', '23.896356'); +INSERT INTO `yoshop_region` VALUES ('3588', '3578', '国姓', '国姓乡', '中国,台湾省,南投县,国姓乡', '3', 'guoxing', '049', '544', 'G', '120.858541', '24.042298'); +INSERT INTO `yoshop_region` VALUES ('3589', '3578', '水里', '水里乡', '中国,台湾省,南投县,水里乡', '3', 'shuili', '049', '553', 'S', '120.855912', '23.812086'); +INSERT INTO `yoshop_region` VALUES ('3590', '3578', '信义', '信义乡', '中国,台湾省,南投县,信义乡', '3', 'xinyi', '049', '556', 'X', '120.855257', '23.699922'); +INSERT INTO `yoshop_region` VALUES ('3591', '3578', '仁爱', '仁爱乡', '中国,台湾省,南投县,仁爱乡', '3', 'renai', '049', '546', 'R', '121.133543', '24.024429'); +INSERT INTO `yoshop_region` VALUES ('3592', '3325', '云林', '云林县', '中国,台湾省,云林县', '2', 'yunlin', '05', '6', 'Y', '120.250000', '23.750000'); +INSERT INTO `yoshop_region` VALUES ('3593', '3592', '斗六', '斗六市', '中国,台湾省,云林县,斗六市', '3', 'douliu', '05', '640', 'D', '120.527360', '23.697651'); +INSERT INTO `yoshop_region` VALUES ('3594', '3592', '斗南', '斗南镇', '中国,台湾省,云林县,斗南镇', '3', 'dounan', '05', '630', 'D', '120.479075', '23.679731'); +INSERT INTO `yoshop_region` VALUES ('3595', '3592', '虎尾', '虎尾镇', '中国,台湾省,云林县,虎尾镇', '3', 'huwei', '05', '632', 'H', '120.445339', '23.708182'); +INSERT INTO `yoshop_region` VALUES ('3596', '3592', '西螺', '西螺镇', '中国,台湾省,云林县,西螺镇', '3', 'xiluo', '05', '648', 'X', '120.466010', '23.797984'); +INSERT INTO `yoshop_region` VALUES ('3597', '3592', '土库', '土库镇', '中国,台湾省,云林县,土库镇', '3', 'tuku', '05', '633', 'T', '120.392572', '23.677822'); +INSERT INTO `yoshop_region` VALUES ('3598', '3592', '北港', '北港镇', '中国,台湾省,云林县,北港镇', '3', 'beigang', '05', '651', 'B', '120.302393', '23.575525'); +INSERT INTO `yoshop_region` VALUES ('3599', '3592', '古坑', '古坑乡', '中国,台湾省,云林县,古坑乡', '3', 'gukeng', '05', '646', 'G', '120.562043', '23.642568'); +INSERT INTO `yoshop_region` VALUES ('3600', '3592', '大埤', '大埤乡', '中国,台湾省,云林县,大埤乡', '3', 'dapi', '05', '631', 'D', '120.430516', '23.645908'); +INSERT INTO `yoshop_region` VALUES ('3601', '3592', '莿桐', '莿桐乡', '中国,台湾省,云林县,莿桐乡', '3', 'citong', '05', '647', null, '120.502374', '23.760784'); +INSERT INTO `yoshop_region` VALUES ('3602', '3592', '林内', '林内乡', '中国,台湾省,云林县,林内乡', '3', 'linna', '05', '643', 'L', '120.611365', '23.758712'); +INSERT INTO `yoshop_region` VALUES ('3603', '3592', '二仑', '二仑乡', '中国,台湾省,云林县,二仑乡', '3', 'erlun', '05', '649', 'E', '120.415077', '23.771273'); +INSERT INTO `yoshop_region` VALUES ('3604', '3592', '仑背', '仑背乡', '中国,台湾省,云林县,仑背乡', '3', 'lunbei', '05', '637', 'L', '120.353895', '23.758840'); +INSERT INTO `yoshop_region` VALUES ('3605', '3592', '麦寮', '麦寮乡', '中国,台湾省,云林县,麦寮乡', '3', 'mailiao', '05', '638', 'M', '120.252043', '23.753841'); +INSERT INTO `yoshop_region` VALUES ('3606', '3592', '东势', '东势乡', '中国,台湾省,云林县,东势乡', '3', 'dongshi', '05', '635', 'D', '120.252672', '23.674679'); +INSERT INTO `yoshop_region` VALUES ('3607', '3592', '褒忠', '褒忠乡', '中国,台湾省,云林县,褒忠乡', '3', 'baozhong', '05', '634', 'B', '120.310488', '23.694245'); +INSERT INTO `yoshop_region` VALUES ('3608', '3592', '台西', '台西乡', '中国,台湾省,云林县,台西乡', '3', 'taixi', '05', '636', 'T', '120.196141', '23.702819'); +INSERT INTO `yoshop_region` VALUES ('3609', '3592', '元长', '元长乡', '中国,台湾省,云林县,元长乡', '3', 'yuanchang', '05', '655', 'Y', '120.315124', '23.649458'); +INSERT INTO `yoshop_region` VALUES ('3610', '3592', '四湖', '四湖乡', '中国,台湾省,云林县,四湖乡', '3', 'sihu', '05', '654', 'S', '120.225741', '23.637740'); +INSERT INTO `yoshop_region` VALUES ('3611', '3592', '口湖', '口湖乡', '中国,台湾省,云林县,口湖乡', '3', 'kouhu', '05', '653', 'K', '120.185370', '23.582406'); +INSERT INTO `yoshop_region` VALUES ('3612', '3592', '水林', '水林乡', '中国,台湾省,云林县,水林乡', '3', 'shuilin', '05', '652', 'S', '120.245948', '23.572634'); +INSERT INTO `yoshop_region` VALUES ('3613', '3325', '嘉义', '嘉义县', '中国,台湾省,嘉义县', '2', 'chiayi', '05', '6', 'J', '120.300000', '23.500000'); +INSERT INTO `yoshop_region` VALUES ('3614', '3613', '太保', '太保市', '中国,台湾省,嘉义县,太保市', '3', 'taibao', '05', '612', 'T', '120.332876', '23.459647'); +INSERT INTO `yoshop_region` VALUES ('3615', '3613', '朴子', '朴子市', '中国,台湾省,嘉义县,朴子市', '3', 'puzi', '05', '613', 'P', '120.247014', '23.464961'); +INSERT INTO `yoshop_region` VALUES ('3616', '3613', '布袋', '布袋镇', '中国,台湾省,嘉义县,布袋镇', '3', 'budai', '05', '625', 'B', '120.166936', '23.377979'); +INSERT INTO `yoshop_region` VALUES ('3617', '3613', '大林', '大林镇', '中国,台湾省,嘉义县,大林镇', '3', 'dalin', '05', '622', 'D', '120.471336', '23.603815'); +INSERT INTO `yoshop_region` VALUES ('3618', '3613', '民雄', '民雄乡', '中国,台湾省,嘉义县,民雄乡', '3', 'minxiong', '05', '621', 'M', '120.428577', '23.551456'); +INSERT INTO `yoshop_region` VALUES ('3619', '3613', '溪口', '溪口乡', '中国,台湾省,嘉义县,溪口乡', '3', 'xikou', '05', '623', 'X', '120.393822', '23.602223'); +INSERT INTO `yoshop_region` VALUES ('3620', '3613', '新港', '新港乡', '中国,台湾省,嘉义县,新港乡', '3', 'xingang', '05', '616', 'X', '120.347647', '23.551806'); +INSERT INTO `yoshop_region` VALUES ('3621', '3613', '六脚', '六脚乡', '中国,台湾省,嘉义县,六脚乡', '3', 'liujiao', '05', '615', 'L', '120.291083', '23.493942'); +INSERT INTO `yoshop_region` VALUES ('3622', '3613', '东石', '东石乡', '中国,台湾省,嘉义县,东石乡', '3', 'dongshi', '05', '614', 'D', '120.153822', '23.459235'); +INSERT INTO `yoshop_region` VALUES ('3623', '3613', '义竹', '义竹乡', '中国,台湾省,嘉义县,义竹乡', '3', 'yizhu', '05', '624', 'Y', '120.243423', '23.336277'); +INSERT INTO `yoshop_region` VALUES ('3624', '3613', '鹿草', '鹿草乡', '中国,台湾省,嘉义县,鹿草乡', '3', 'lucao', '05', '611', 'L', '120.308370', '23.410784'); +INSERT INTO `yoshop_region` VALUES ('3625', '3613', '水上', '水上乡', '中国,台湾省,嘉义县,水上乡', '3', 'shuishang', '05', '608', 'S', '120.397936', '23.428104'); +INSERT INTO `yoshop_region` VALUES ('3626', '3613', '中埔', '中埔乡', '中国,台湾省,嘉义县,中埔乡', '3', 'zhongpu', '05', '606', 'Z', '120.522948', '23.425148'); +INSERT INTO `yoshop_region` VALUES ('3627', '3613', '竹崎', '竹崎乡', '中国,台湾省,嘉义县,竹崎乡', '3', 'zhuqi', '05', '604', 'Z', '120.551466', '23.523184'); +INSERT INTO `yoshop_region` VALUES ('3628', '3613', '梅山', '梅山乡', '中国,台湾省,嘉义县,梅山乡', '3', 'meishan', '05', '603', 'M', '120.557192', '23.584915'); +INSERT INTO `yoshop_region` VALUES ('3629', '3613', '番路', '番路乡', '中国,台湾省,嘉义县,番路乡', '3', 'fanlu', '05', '602', 'F', '120.555043', '23.465222'); +INSERT INTO `yoshop_region` VALUES ('3630', '3613', '大埔', '大埔乡', '中国,台湾省,嘉义县,大埔乡', '3', 'dapu', '05', '607', 'D', '120.593795', '23.296715'); +INSERT INTO `yoshop_region` VALUES ('3631', '3613', '阿里山', '阿里山乡', '中国,台湾省,嘉义县,阿里山乡', '3', 'alishan', '05', '605', 'A', '120.732520', '23.467950'); +INSERT INTO `yoshop_region` VALUES ('3632', '3325', '屏东', '屏东县', '中国,台湾省,屏东县', '2', 'pingtung', '08', '9', 'P', '120.487928', '22.682802'); +INSERT INTO `yoshop_region` VALUES ('3633', '3632', '屏东', '屏东市', '中国,台湾省,屏东县,屏东市', '3', 'pingdong', '08', '900', 'P', '120.488465', '22.669723'); +INSERT INTO `yoshop_region` VALUES ('3634', '3632', '潮州', '潮州镇', '中国,台湾省,屏东县,潮州镇', '3', 'chaozhou', '08', '920', 'C', '120.542854', '22.550536'); +INSERT INTO `yoshop_region` VALUES ('3635', '3632', '东港', '东港镇', '中国,台湾省,屏东县,东港镇', '3', 'donggang', '08', '928', 'D', '120.454489', '22.466626'); +INSERT INTO `yoshop_region` VALUES ('3636', '3632', '恒春', '恒春镇', '中国,台湾省,屏东县,恒春镇', '3', 'hengchun', '08', '946', 'H', '120.745451', '22.002373'); +INSERT INTO `yoshop_region` VALUES ('3637', '3632', '万丹', '万丹乡', '中国,台湾省,屏东县,万丹乡', '3', 'wandan', '08', '913', 'W', '120.484533', '22.589839'); +INSERT INTO `yoshop_region` VALUES ('3638', '3632', '长治', '长治乡', '中国,台湾省,屏东县,长治乡', '3', 'changzhi', '08', '908', 'C', '120.527614', '22.677062'); +INSERT INTO `yoshop_region` VALUES ('3639', '3632', '麟洛', '麟洛乡', '中国,台湾省,屏东县,麟洛乡', '3', 'linluo', '08', '909', null, '120.527283', '22.650604'); +INSERT INTO `yoshop_region` VALUES ('3640', '3632', '九如', '九如乡', '中国,台湾省,屏东县,九如乡', '3', 'jiuru', '08', '904', 'J', '120.490142', '22.739778'); +INSERT INTO `yoshop_region` VALUES ('3641', '3632', '里港', '里港乡', '中国,台湾省,屏东县,里港乡', '3', 'ligang', '08', '905', 'L', '120.494490', '22.779220'); +INSERT INTO `yoshop_region` VALUES ('3642', '3632', '盐埔', '盐埔乡', '中国,台湾省,屏东县,盐埔乡', '3', 'yanpu', '08', '907', 'Y', '120.572849', '22.754783'); +INSERT INTO `yoshop_region` VALUES ('3643', '3632', '高树', '高树乡', '中国,台湾省,屏东县,高树乡', '3', 'gaoshu', '08', '906', 'G', '120.600214', '22.826789'); +INSERT INTO `yoshop_region` VALUES ('3644', '3632', '万峦', '万峦乡', '中国,台湾省,屏东县,万峦乡', '3', 'wanluan', '08', '923', 'W', '120.566477', '22.571965'); +INSERT INTO `yoshop_region` VALUES ('3645', '3632', '内埔', '内埔乡', '中国,台湾省,屏东县,内埔乡', '3', 'napu', '08', '912', 'N', '120.566865', '22.611967'); +INSERT INTO `yoshop_region` VALUES ('3646', '3632', '竹田', '竹田乡', '中国,台湾省,屏东县,竹田乡', '3', 'zhutian', '08', '911', 'Z', '120.544038', '22.584678'); +INSERT INTO `yoshop_region` VALUES ('3647', '3632', '新埤', '新埤乡', '中国,台湾省,屏东县,新埤乡', '3', 'xinpi', '08', '925', 'X', '120.549546', '22.469976'); +INSERT INTO `yoshop_region` VALUES ('3648', '3632', '枋寮', '枋寮乡', '中国,台湾省,屏东县,枋寮乡', '3', 'fangliao', '08', '940', null, '120.593438', '22.365560'); +INSERT INTO `yoshop_region` VALUES ('3649', '3632', '新园', '新园乡', '中国,台湾省,屏东县,新园乡', '3', 'xinyuan', '08', '932', 'X', '120.461739', '22.543952'); +INSERT INTO `yoshop_region` VALUES ('3650', '3632', '崁顶', '崁顶乡', '中国,台湾省,屏东县,崁顶乡', '3', 'kanding', '08', '924', null, '120.514571', '22.514795'); +INSERT INTO `yoshop_region` VALUES ('3651', '3632', '林边', '林边乡', '中国,台湾省,屏东县,林边乡', '3', 'linbian', '08', '927', 'L', '120.515091', '22.434015'); +INSERT INTO `yoshop_region` VALUES ('3652', '3632', '南州', '南州乡', '中国,台湾省,屏东县,南州乡', '3', 'nanzhou', '08', '926', 'N', '120.509808', '22.490192'); +INSERT INTO `yoshop_region` VALUES ('3653', '3632', '佳冬', '佳冬乡', '中国,台湾省,屏东县,佳冬乡', '3', 'jiadong', '08', '931', 'J', '120.551544', '22.417653'); +INSERT INTO `yoshop_region` VALUES ('3654', '3632', '琉球', '琉球乡', '中国,台湾省,屏东县,琉球乡', '3', 'liuqiu', '08', '929', 'L', '120.369020', '22.342366'); +INSERT INTO `yoshop_region` VALUES ('3655', '3632', '车城', '车城乡', '中国,台湾省,屏东县,车城乡', '3', 'checheng', '08', '944', 'C', '120.710979', '22.072077'); +INSERT INTO `yoshop_region` VALUES ('3656', '3632', '满州', '满州乡', '中国,台湾省,屏东县,满州乡', '3', 'manzhou', '08', '947', 'M', '120.838843', '22.020853'); +INSERT INTO `yoshop_region` VALUES ('3657', '3632', '枋山', '枋山乡', '中国,台湾省,屏东县,枋山乡', '3', 'fangshan', '08', '941', null, '120.656356', '22.260338'); +INSERT INTO `yoshop_region` VALUES ('3658', '3632', '三地门', '三地门乡', '中国,台湾省,屏东县,三地门乡', '3', 'sandimen', '08', '901', 'S', '120.654486', '22.713877'); +INSERT INTO `yoshop_region` VALUES ('3659', '3632', '雾台', '雾台乡', '中国,台湾省,屏东县,雾台乡', '3', 'wutai', '08', '902', 'W', '120.732318', '22.744877'); +INSERT INTO `yoshop_region` VALUES ('3660', '3632', '玛家', '玛家乡', '中国,台湾省,屏东县,玛家乡', '3', 'majia', '08', '903', 'M', '120.644130', '22.706718'); +INSERT INTO `yoshop_region` VALUES ('3661', '3632', '泰武', '泰武乡', '中国,台湾省,屏东县,泰武乡', '3', 'taiwu', '08', '921', 'T', '120.632856', '22.591819'); +INSERT INTO `yoshop_region` VALUES ('3662', '3632', '来义', '来义乡', '中国,台湾省,屏东县,来义乡', '3', 'laiyi', '08', '922', 'L', '120.633601', '22.525866'); +INSERT INTO `yoshop_region` VALUES ('3663', '3632', '春日', '春日乡', '中国,台湾省,屏东县,春日乡', '3', 'chunri', '08', '942', 'C', '120.628793', '22.370672'); +INSERT INTO `yoshop_region` VALUES ('3664', '3632', '狮子', '狮子乡', '中国,台湾省,屏东县,狮子乡', '3', 'shizi', '08', '943', 'S', '120.704617', '22.201917'); +INSERT INTO `yoshop_region` VALUES ('3665', '3632', '牡丹', '牡丹乡', '中国,台湾省,屏东县,牡丹乡', '3', 'mudan', '08', '945', 'M', '120.770108', '22.125687'); +INSERT INTO `yoshop_region` VALUES ('3666', '3325', '台东', '台东县', '中国,台湾省,台东县', '2', 'taitung', '089', '9', 'T', '120.916000', '23.000000'); +INSERT INTO `yoshop_region` VALUES ('3667', '3666', '台东', '台东市', '中国,台湾省,台东县,台东市', '3', 'taidong', '089', '950', 'T', '121.145654', '22.756045'); +INSERT INTO `yoshop_region` VALUES ('3668', '3666', '成功', '成功镇', '中国,台湾省,台东县,成功镇', '3', 'chenggong', '089', '961', 'C', '121.379571', '23.100223'); +INSERT INTO `yoshop_region` VALUES ('3669', '3666', '关山', '关山镇', '中国,台湾省,台东县,关山镇', '3', 'guanshan', '089', '956', 'G', '121.163134', '23.047450'); +INSERT INTO `yoshop_region` VALUES ('3670', '3666', '卑南', '卑南乡', '中国,台湾省,台东县,卑南乡', '3', 'beinan', '089', '954', 'B', '121.083503', '22.786039'); +INSERT INTO `yoshop_region` VALUES ('3671', '3666', '鹿野', '鹿野乡', '中国,台湾省,台东县,鹿野乡', '3', 'luye', '089', '955', 'L', '121.135982', '22.913951'); +INSERT INTO `yoshop_region` VALUES ('3672', '3666', '池上', '池上乡', '中国,台湾省,台东县,池上乡', '3', 'chishang', '089', '958', 'C', '121.215139', '23.122393'); +INSERT INTO `yoshop_region` VALUES ('3673', '3666', '东河', '东河乡', '中国,台湾省,台东县,东河乡', '3', 'donghe', '089', '959', 'D', '121.300334', '22.969934'); +INSERT INTO `yoshop_region` VALUES ('3674', '3666', '长滨', '长滨乡', '中国,台湾省,台东县,长滨乡', '3', 'changbin', '089', '962', 'C', '121.451522', '23.315041'); +INSERT INTO `yoshop_region` VALUES ('3675', '3666', '太麻里', '太麻里乡', '中国,台湾省,台东县,太麻里乡', '3', 'taimali', '089', '963', 'T', '121.007394', '22.615383'); +INSERT INTO `yoshop_region` VALUES ('3676', '3666', '大武', '大武乡', '中国,台湾省,台东县,大武乡', '3', 'dawu', '089', '965', 'D', '120.889938', '22.339919'); +INSERT INTO `yoshop_region` VALUES ('3677', '3666', '绿岛', '绿岛乡', '中国,台湾省,台东县,绿岛乡', '3', 'lvdao', '089', '951', 'L', '121.492596', '22.661676'); +INSERT INTO `yoshop_region` VALUES ('3678', '3666', '海端', '海端乡', '中国,台湾省,台东县,海端乡', '3', 'haiduan', '089', '957', 'H', '121.172008', '23.101074'); +INSERT INTO `yoshop_region` VALUES ('3679', '3666', '延平', '延平乡', '中国,台湾省,台东县,延平乡', '3', 'yanping', '089', '953', 'Y', '121.084499', '22.902358'); +INSERT INTO `yoshop_region` VALUES ('3680', '3666', '金峰', '金峰乡', '中国,台湾省,台东县,金峰乡', '3', 'jinfeng', '089', '964', 'J', '120.971292', '22.595511'); +INSERT INTO `yoshop_region` VALUES ('3681', '3666', '达仁', '达仁乡', '中国,台湾省,台东县,达仁乡', '3', 'daren', '089', '966', 'D', '120.884131', '22.294869'); +INSERT INTO `yoshop_region` VALUES ('3682', '3666', '兰屿', '兰屿乡', '中国,台湾省,台东县,兰屿乡', '3', 'lanyu', '089', '952', 'L', '121.532473', '22.056736'); +INSERT INTO `yoshop_region` VALUES ('3683', '3325', '花莲', '花莲县', '中国,台湾省,花莲县', '2', 'hualien', '03', '9', 'H', '121.300000', '23.830000'); +INSERT INTO `yoshop_region` VALUES ('3684', '3683', '花莲', '花莲市', '中国,台湾省,花莲县,花莲市', '3', 'hualian', '03', '970', 'H', '121.606810', '23.982074'); +INSERT INTO `yoshop_region` VALUES ('3685', '3683', '凤林', '凤林镇', '中国,台湾省,花莲县,凤林镇', '3', 'fenglin', '03', '975', 'F', '121.451687', '23.744648'); +INSERT INTO `yoshop_region` VALUES ('3686', '3683', '玉里', '玉里镇', '中国,台湾省,花莲县,玉里镇', '3', 'yuli', '03', '981', 'Y', '121.316445', '23.336509'); +INSERT INTO `yoshop_region` VALUES ('3687', '3683', '新城', '新城乡', '中国,台湾省,花莲县,新城乡', '3', 'xincheng', '03', '971', 'X', '121.640512', '24.128133'); +INSERT INTO `yoshop_region` VALUES ('3688', '3683', '吉安', '吉安乡', '中国,台湾省,花莲县,吉安乡', '3', 'ji\'an', '03', '973', 'J', '121.568005', '23.961635'); +INSERT INTO `yoshop_region` VALUES ('3689', '3683', '寿丰', '寿丰乡', '中国,台湾省,花莲县,寿丰乡', '3', 'shoufeng', '03', '974', 'S', '121.508955', '23.870680'); +INSERT INTO `yoshop_region` VALUES ('3690', '3683', '光复', '光复乡', '中国,台湾省,花莲县,光复乡', '3', 'guangfu', '03', '976', 'G', '121.423496', '23.669084'); +INSERT INTO `yoshop_region` VALUES ('3691', '3683', '丰滨', '丰滨乡', '中国,台湾省,花莲县,丰滨乡', '3', 'fengbin', '03', '977', 'F', '121.518639', '23.597080'); +INSERT INTO `yoshop_region` VALUES ('3692', '3683', '瑞穗', '瑞穗乡', '中国,台湾省,花莲县,瑞穗乡', '3', 'ruisui', '03', '978', 'R', '121.375992', '23.496817'); +INSERT INTO `yoshop_region` VALUES ('3693', '3683', '富里', '富里乡', '中国,台湾省,花莲县,富里乡', '3', 'fuli', '03', '983', 'F', '121.250124', '23.179984'); +INSERT INTO `yoshop_region` VALUES ('3694', '3683', '秀林', '秀林乡', '中国,台湾省,花莲县,秀林乡', '3', 'xiulin', '03', '972', 'X', '121.620381', '24.116642'); +INSERT INTO `yoshop_region` VALUES ('3695', '3683', '万荣', '万荣乡', '中国,台湾省,花莲县,万荣乡', '3', 'wanrong', '03', '979', 'W', '121.407493', '23.715346'); +INSERT INTO `yoshop_region` VALUES ('3696', '3683', '卓溪', '卓溪乡', '中国,台湾省,花莲县,卓溪乡', '3', 'zhuoxi', '03', '982', 'Z', '121.303422', '23.346369'); +INSERT INTO `yoshop_region` VALUES ('3697', '3325', '澎湖', '澎湖县', '中国,台湾省,澎湖县', '2', 'penghu', '06', '8', 'P', '119.566417', '23.569733'); +INSERT INTO `yoshop_region` VALUES ('3698', '3697', '马公', '马公市', '中国,台湾省,澎湖县,马公市', '3', 'magong', '06', '880', 'M', '119.566499', '23.565845'); +INSERT INTO `yoshop_region` VALUES ('3699', '3697', '湖西', '湖西乡', '中国,台湾省,澎湖县,湖西乡', '3', 'huxi', '06', '885', 'H', '119.659666', '23.583358'); +INSERT INTO `yoshop_region` VALUES ('3700', '3697', '白沙', '白沙乡', '中国,台湾省,澎湖县,白沙乡', '3', 'baisha', '06', '884', 'B', '119.597919', '23.666060'); +INSERT INTO `yoshop_region` VALUES ('3701', '3697', '西屿', '西屿乡', '中国,台湾省,澎湖县,西屿乡', '3', 'xiyu', '06', '881', 'X', '119.506974', '23.600836'); +INSERT INTO `yoshop_region` VALUES ('3702', '3697', '望安', '望安乡', '中国,台湾省,澎湖县,望安乡', '3', 'wang\'an', '06', '882', 'W', '119.500538', '23.357531'); +INSERT INTO `yoshop_region` VALUES ('3703', '3697', '七美', '七美乡', '中国,台湾省,澎湖县,七美乡', '3', 'qimei', '06', '883', 'Q', '119.423929', '23.206018'); +INSERT INTO `yoshop_region` VALUES ('3704', '3325', '金门', '金门县', '中国,台湾省,金门县', '2', 'jinmen', '082', '8', 'J', '118.317089', '24.432706'); +INSERT INTO `yoshop_region` VALUES ('3705', '3704', '金城', '金城镇', '中国,台湾省,金门县,金城镇', '3', 'jincheng', '082', '893', 'J', '118.316667', '24.416667'); +INSERT INTO `yoshop_region` VALUES ('3706', '3704', '金湖', '金湖镇', '中国,台湾省,金门县,金湖镇', '3', 'jinhu', '082', '891', 'J', '118.419743', '24.438633'); +INSERT INTO `yoshop_region` VALUES ('3707', '3704', '金沙', '金沙镇', '中国,台湾省,金门县,金沙镇', '3', 'jinsha', '082', '890', 'J', '118.427993', '24.481109'); +INSERT INTO `yoshop_region` VALUES ('3708', '3704', '金宁', '金宁乡', '中国,台湾省,金门县,金宁乡', '3', 'jinning', '082', '892', 'J', '118.334506', '24.45672'); +INSERT INTO `yoshop_region` VALUES ('3709', '3704', '烈屿', '烈屿乡', '中国,台湾省,金门县,烈屿乡', '3', 'lieyu', '082', '894', 'L', '118.247255', '24.433102'); +INSERT INTO `yoshop_region` VALUES ('3710', '3704', '乌丘', '乌丘乡', '中国,台湾省,金门县,乌丘乡', '3', 'wuqiu', '082', '896', 'W', '118.319578', '24.435038'); +INSERT INTO `yoshop_region` VALUES ('3711', '3325', '连江', '连江县', '中国,台湾省,连江县', '2', 'lienchiang', '0836', '2', 'L', '119.539704', '26.197364'); +INSERT INTO `yoshop_region` VALUES ('3712', '3711', '南竿', '南竿乡', '中国,台湾省,连江县,南竿乡', '3', 'nangan', '0836', '209', 'N', '119.944267', '26.144035'); +INSERT INTO `yoshop_region` VALUES ('3713', '3711', '北竿', '北竿乡', '中国,台湾省,连江县,北竿乡', '3', 'beigan', '0836', '210', 'B', '120.000572', '26.221983'); +INSERT INTO `yoshop_region` VALUES ('3714', '3711', '莒光', '莒光乡', '中国,台湾省,连江县,莒光乡', '3', 'juguang', '0836', '211', null, '119.940405', '25.976256'); +INSERT INTO `yoshop_region` VALUES ('3715', '3711', '东引', '东引乡', '中国,台湾省,连江县,东引乡', '3', 'dongyin', '0836', '212', 'D', '120.493955', '26.366164'); +INSERT INTO `yoshop_region` VALUES ('3716', '0', '香港', '香港特别行政区', '中国,香港特别行政区', '1', 'hongkong', '', '', 'X', '114.173355', '22.320048'); +INSERT INTO `yoshop_region` VALUES ('3738', '0', '澳门', '澳门特别行政区', '中国,澳门特别行政区', '1', 'macau', '', '', 'A', '113.54909', '22.198951'); +INSERT INTO `yoshop_region` VALUES ('3739', '3738', '澳门半岛', '澳门半岛', '中国,澳门特别行政区,澳门半岛', '2', 'macaupeninsula', '00853', '999078', 'A', '113.549134', '22.198751'); +INSERT INTO `yoshop_region` VALUES ('3740', '3739', '花地玛堂区', '花地玛堂区', '中国,澳门特别行政区,澳门半岛,花地玛堂区', '3', 'nossasenhoradefatima', '00853', '999078', 'H', '113.552284', '22.208067'); +INSERT INTO `yoshop_region` VALUES ('3741', '3739', '圣安多尼堂区', '圣安多尼堂区', '中国,澳门特别行政区,澳门半岛,圣安多尼堂区', '3', 'santoantonio', '00853', '999078', 'S', '113.564301', '22.12381'); +INSERT INTO `yoshop_region` VALUES ('3742', '3739', '大堂', '大堂区', '中国,澳门特别行政区,澳门半岛,大堂区', '3', 'sé', '00853', '999078', 'D', '113.552971', '22.188359'); +INSERT INTO `yoshop_region` VALUES ('3743', '3739', '望德堂区', '望德堂区', '中国,澳门特别行政区,澳门半岛,望德堂区', '3', 'saolazaro', '00853', '999078', 'W', '113.550568', '22.194081'); +INSERT INTO `yoshop_region` VALUES ('3744', '3739', '风顺堂区', '风顺堂区', '中国,澳门特别行政区,澳门半岛,风顺堂区', '3', 'saolourenco', '00853', '999078', 'F', '113.541928', '22.187368'); +INSERT INTO `yoshop_region` VALUES ('3745', '3738', '氹仔岛', '氹仔岛', '中国,澳门特别行政区,氹仔岛', '2', 'taipa', '00853', '999078', null, '113.577669', '22.156838'); +INSERT INTO `yoshop_region` VALUES ('3746', '3745', '嘉模堂区', '嘉模堂区', '中国,澳门特别行政区,氹仔岛,嘉模堂区', '3', 'ourladyofcarmel\'sparish', '00853', '999078', 'J', '113.565303', '22.149029'); +INSERT INTO `yoshop_region` VALUES ('3747', '3738', '路环岛', '路环岛', '中国,澳门特别行政区,路环岛', '2', 'coloane', '00853', '999078', 'L', '113.564857', '22.116226'); +INSERT INTO `yoshop_region` VALUES ('3748', '3747', '圣方济各堂区', '圣方济各堂区', '中国,澳门特别行政区,路环岛,圣方济各堂区', '3', 'stfrancisxavier\'sparish', '00853', '999078', 'S', '113.559954', '22.123486'); +INSERT INTO `yoshop_region` VALUES ('3999', '3716', '香港', '香港特别行政区', '中国,香港特别行政区', '2', 'hongkong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4000', '3999', '中西区', '中西区', '中国,香港特别行政区,中西区', '3', 'zhongxin', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4001', '3999', '东区', '东区', '中国,香港特别行政区,东区', '3', 'dong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4002', '3999', '九龙城区', '九龙城区', '中国,香港特别行政区,九龙城区', '3', 'jiulong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4003', '3999', '观塘区', '观塘区', '中国,香港特别行政区,观塘区', '3', 'guantang', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4004', '3999', '南区', '南区', '中国,香港特别行政区,南区', '3', 'nan', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4005', '3999', '深水埗区', '深水埗区', '中国,香港特别行政区,深水埗区', '3', 'shenshuibu', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4006', '3999', '湾仔区', '湾仔区', '中国,香港特别行政区,湾仔区', '3', 'wanzi', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4007', '3999', '黄大仙区', '黄大仙区', '中国,香港特别行政区,黄大仙区', '3', 'huangdaxian', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4008', '3999', '油尖旺区', '油尖旺区', '中国,香港特别行政区,油尖旺区', '3', 'youjianwang', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4009', '3999', '离岛区', '离岛区', '中国,香港特别行政区,离岛区', '3', 'lidao', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4010', '3999', '葵青区', '葵青区', '中国,香港特别行政区,葵青区', '3', 'kuiqing', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4011', '3999', '北区', '北区', '中国,香港特别行政区,北区', '3', 'bei', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4012', '3999', '西贡区', '西贡区', '中国,香港特别行政区,西贡区', '3', 'xigong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4013', '3999', '沙田区', '沙田区', '中国,香港特别行政区,沙田区', '3', 'shatian', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4014', '3999', '屯门区', '屯门区', '中国,香港特别行政区,屯门区', '3', 'tunmen', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4015', '3999', '大埔区', '大埔区', '中国,香港特别行政区,大埔区', '3', 'dapu', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4016', '3999', '荃湾区', '荃湾区', '中国,香港特别行政区,荃湾区', '3', 'quanwan', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4017', '3999', '元朗区', '元朗区', '中国,香港特别行政区,元朗区', '3', 'yuanlang', null, null, null, null, null); + +CREATE TABLE `yoshop_setting` ( + `key` varchar(30) NOT NULL COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商城设置记录表'; + +INSERT INTO `yoshop_setting` VALUES ('storage', '上传设置', '{\"default\":\"local\",\"engine\":{\"qiniu\":{\"bucket\":\"\",\"access_key\":\"\",\"secret_key\":\"\",\"domain\":\"http:\\/\\/\"},\"aliyun\":{\"bucket\":\"\",\"access_key_id\":\"\",\"access_key_secret\":\"\",\"domain\":\"http:\\/\\/\"},\"qcloud\":{\"bucket\":\"\",\"region\":\"\",\"secret_id\":\"\",\"secret_key\":\"\",\"domain\":\"http:\\/\\/\"}}}', '10001', '1535945598'); + + +CREATE TABLE `yoshop_spec` ( + `spec_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '规格组id', + `spec_name` varchar(255) NOT NULL DEFAULT '' COMMENT '规格组名称', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL COMMENT '创建时间', + PRIMARY KEY (`spec_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品规格组记录表'; + + +CREATE TABLE `yoshop_spec_value` ( + `spec_value_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '规格值id', + `spec_value` varchar(255) NOT NULL COMMENT '规格值', + `spec_id` int(11) NOT NULL COMMENT '规格组id', + `wxapp_id` int(11) NOT NULL COMMENT '小程序id', + `create_time` int(11) NOT NULL COMMENT '创建时间', + PRIMARY KEY (`spec_value_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品规格值记录表'; + + +CREATE TABLE `yoshop_store_user` ( + `store_user_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名', + `password` varchar(255) NOT NULL DEFAULT '' COMMENT '登录密码', + `real_name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名', + `is_super` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否为超级管理员', + `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`store_user_id`), + KEY `user_name` (`user_name`) +) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='商家用户记录表'; + +INSERT INTO `yoshop_store_user` VALUES ('10001', 'admin', '9ae7b2e6f25c907a1fc81b503b16e25f', '管理员', '1', '0', '10001', '1529926348', '1531027042'); + + +CREATE TABLE `yoshop_upload_file` ( + `file_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '文件id', + `storage` varchar(20) NOT NULL DEFAULT '' COMMENT '存储方式', + `group_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文件分组id', + `file_url` varchar(255) NOT NULL DEFAULT '' COMMENT '存储域名', + `file_name` varchar(255) NOT NULL DEFAULT '' COMMENT '文件路径', + `file_size` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文件大小(字节)', + `file_type` varchar(20) NOT NULL DEFAULT '' COMMENT '文件类型', + `extension` varchar(20) NOT NULL DEFAULT '' COMMENT '文件扩展名', + `is_user` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否为c端用户上传', + `is_recycle` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否已回收', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`file_id`), + UNIQUE KEY `path_idx` (`file_name`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文件库记录表'; + + +CREATE TABLE `yoshop_upload_group` ( + `group_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '分类id', + `group_type` varchar(10) NOT NULL DEFAULT '' COMMENT '文件类型', + `group_name` varchar(30) NOT NULL DEFAULT '' COMMENT '分类名称', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分类排序(数字越小越靠前)', + `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`group_id`), + KEY `type_index` (`group_type`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文件库分组记录表'; + + +CREATE TABLE `yoshop_user` ( + `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id', + `open_id` varchar(255) NOT NULL DEFAULT '' COMMENT '微信openid(唯一标示)', + `nickName` varchar(255) NOT NULL DEFAULT '' COMMENT '微信昵称', + `avatarUrl` varchar(255) NOT NULL DEFAULT '' COMMENT '微信头像', + `gender` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '性别', + `country` varchar(50) NOT NULL DEFAULT '' COMMENT '国家', + `province` varchar(50) NOT NULL DEFAULT '' COMMENT '省份', + `city` varchar(50) NOT NULL DEFAULT '' COMMENT '城市', + `address_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '默认收货地址', + `balance` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '用户可用余额', + `points` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户可用积分', + `pay_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '用户总支付的金额', + `expend_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际消费的金额(不含退款)', + `grade_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '会员等级id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`user_id`), + KEY `openid` (`open_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户记录表'; + + +CREATE TABLE `yoshop_user_address` ( + `address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在区id', + `district` varchar(255) NULL DEFAULT '' COMMENT '新市辖区(该字段用于记录region表中没有的市辖区)', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`address_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户收货地址表'; + + +CREATE TABLE `yoshop_user_coupon` ( + `user_coupon_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `coupon_id` int(11) unsigned NOT NULL COMMENT '优惠券id', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '优惠券名称', + `color` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券颜色(10蓝 20红 30紫 40黄)', + `coupon_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '优惠券类型(10满减券 20折扣券)', + `reduce_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '满减券-减免金额', + `discount` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '折扣券-折扣率(0-100)', + `min_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '最低消费金额', + `expire_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '到期类型(10领取后生效 20固定时间)', + `expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数', + `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期开始时间', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期结束时间', + `apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品)', + `is_expire` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否过期(0未过期 1已过期)', + `is_use` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已使用(0未使用 1已使用)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`user_coupon_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户优惠券记录表'; + + +CREATE TABLE `yoshop_wxapp` ( + `wxapp_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '小程序id', + `app_id` varchar(50) NOT NULL DEFAULT '' COMMENT '小程序AppID', + `app_secret` varchar(50) NOT NULL DEFAULT '' COMMENT '小程序AppSecret', + `mchid` varchar(50) NOT NULL DEFAULT '' COMMENT '微信商户号id', + `apikey` varchar(255) NOT NULL DEFAULT '' COMMENT '微信支付密钥', + `cert_pem` longtext COMMENT '证书文件cert', + `key_pem` longtext COMMENT '证书文件key', + `is_recycle` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否回收', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`wxapp_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='微信小程序记录表'; + +INSERT INTO `yoshop_wxapp` VALUES ('10001', '', '', '', '', '', '', 0, 0, '1529926348', '1532766653'); + + +CREATE TABLE `yoshop_wxapp_help` ( + `help_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `title` varchar(255) NOT NULL DEFAULT '' COMMENT '帮助标题', + `content` text NOT NULL COMMENT '帮助内容', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`help_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8 COMMENT='微信小程序帮助'; + +INSERT INTO `yoshop_wxapp_help` VALUES ('10001', '关于小程序', '小程序本身无需下载,无需注册,不占用手机内存,可以跨平台使用,响应迅速,体验接近原生APP。', '100', '10001', '1535942481', '1535942481'); + + + +CREATE TABLE `yoshop_wxapp_page` ( + `page_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '页面id', + `page_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '页面类型(10首页 20自定义页)', + `page_name` varchar(255) NOT NULL DEFAULT '' COMMENT '页面名称', + `page_data` longtext NOT NULL COMMENT '页面数据', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '微信小程序id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`page_id`), + KEY `wxapp_id` (`wxapp_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10004 DEFAULT CHARSET=utf8 COMMENT='微信小程序diy页面表'; + +INSERT INTO `yoshop_wxapp_page` VALUES ('10001', '10', '小程序首页', '{\"items\":{\"page\":{\"id\":\"page\",\"type\":\"page\",\"name\":\"\\u9875\\u9762\\u8bbe\\u7f6e\",\"params\":{\"name\":\"\\u9875\\u9762\\u540d\\u79f0\",\"title\":\"\\u8424\\u706b\\u5c0f\\u7a0b\\u5e8f\\u5546\\u57ce\"},\"style\":{\"titleTextColor\":\"white\",\"titleBackgroundColor\":\"#ff8000\"}},\"n50214144672381\":{\"id\":\"n50214144672381\",\"type\":\"search\",\"name\":\"\\u641c\\u7d22\\u6846\",\"params\":{\"placeholder\":\"\\u8bf7\\u8f93\\u5165\\u5173\\u952e\\u5b57\\u8fdb\\u884c\\u641c\\u7d22\"},\"style\":{\"textAlign\":\"left\",\"searchStyle\":\"\"}},\"n33356112682143\":{\"id\":\"n33356112682143\",\"type\":\"coupon\",\"name\":\"\\u4f18\\u60e0\\u5238\\u7ec4\",\"style\":{\"paddingTop\":\"10\",\"background\":\"#ffffff\"},\"params\":{\"limit\":\"5\"},\"data\":{\"n214578430230592\":{\"color\":\"red\",\"reduce_price\":\"10\",\"min_price\":\"100.00\"},\"n818030369705776\":{\"color\":\"violet\",\"reduce_price\":\"10\",\"min_price\":\"100.00\"}}}}}', '10001', 0, '1536197290', '1536197290'); + + +# 微信小程序分类页模板表 +CREATE TABLE `yoshop_wxapp_category` ( + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `category_style` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '分类页样式(10一级分类[大图] 11一级分类[小图] 20二级分类)', + `share_title` varchar(100) NOT NULL DEFAULT '' COMMENT '分享标题', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信小程序分类页模板'; + +INSERT INTO `yoshop_wxapp_category` (`wxapp_id`, `category_style`, `share_title`, `create_time`, `update_time`) VALUES ('10001', '10', '', '1536373988', '1536375112'); + + +# 小程序prepay_id记录表 +CREATE TABLE `yoshop_wxapp_prepay_id` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT 10 COMMENT '订单类型(10商城订单 20拼团订单)', + `prepay_id` varchar(50) NOT NULL DEFAULT '' COMMENT '微信支付prepay_id', + `can_use_times` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '可使用次数', + `used_times` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '已使用次数', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '支付状态(1已支付)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `expiry_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `order_id` (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='小程序prepay_id记录'; + + +# 分销商申请记录表 +CREATE TABLE `yoshop_dealer_apply` ( + `apply_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `referee_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '推荐人用户id', + `apply_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '申请方式(10需后台审核 20无需审核)', + `apply_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '申请时间', + `apply_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '审核状态 (10待审核 20审核通过 30驳回)', + `audit_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '审核时间', + `reject_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '驳回原因', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`apply_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商申请记录表'; + + +# 分销商资金明细表 +CREATE TABLE `yoshop_dealer_capital` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `flow_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '资金流动类型 (10佣金收入 20提现支出)', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '金额', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商资金明细表'; + + +# 销商订单记录表 +CREATE TABLE `yoshop_dealer_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id (买家)', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号(废弃,勿用)', + `order_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单总金额(不含运费)', + `first_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(一级)', + `second_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(二级)', + `third_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(三级)', + `first_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `is_invalid` tinyint(3) NOT NULL DEFAULT 0 COMMENT '订单是否失效(0未失效 1已失效)', + `is_settled` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已结算佣金(0未结算 1已结算)', + `settle_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '结算时间', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `order_id` (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商订单记录表'; + + +# 分销商推荐关系表 +CREATE TABLE `yoshop_dealer_referee` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `dealer_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id(被推荐人)', + `level` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '推荐关系层级(1,2,3)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `dealer_id` (`dealer_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商推荐关系表'; + + +# 分销商设置表 +CREATE TABLE `yoshop_dealer_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分销商设置表'; + + +# 分销商用户记录表 +CREATE TABLE `yoshop_dealer_user` ( + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '当前可提现佣金', + `freeze_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '已冻结佣金', + `total_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '累积提现佣金', + `referee_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '推荐人用户id', + `first_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(一级)', + `second_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(二级)', + `third_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(三级)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分销商用户记录表'; + + +# 分销商提现明细表 +CREATE TABLE `yoshop_dealer_withdraw` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '提现金额', + `pay_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '打款方式 (10微信 20支付宝 30银行卡)', + `alipay_name` varchar(30) NOT NULL DEFAULT '' COMMENT '支付宝姓名', + `alipay_account` varchar(30) NOT NULL DEFAULT '' COMMENT '支付宝账号', + `bank_name` varchar(30) NOT NULL DEFAULT '' COMMENT '开户行名称', + `bank_account` varchar(30) NOT NULL DEFAULT '' COMMENT '银行开户名', + `bank_card` varchar(30) NOT NULL DEFAULT '' COMMENT '银行卡号', + `apply_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '申请状态 (10待审核 20审核通过 30驳回 40已打款)', + `audit_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '审核时间', + `reject_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '驳回原因', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商提现明细表'; + + +# 小程序form_id记录表 +CREATE TABLE `yoshop_wxapp_formid` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `form_id` varchar(50) NOT NULL DEFAULT '' COMMENT '小程序form_id', + `expiry_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间', + `is_used` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已使用', + `used_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '使用时间', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='小程序form_id记录表'; + + +# 超管用户记录表 +CREATE TABLE `yoshop_admin_user` ( + `admin_user_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名', + `password` varchar(255) NOT NULL DEFAULT '' COMMENT '登录密码', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`admin_user_id`), + KEY `user_name` (`user_name`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='超管用户记录表'; + +INSERT INTO `yoshop_admin_user` (`user_name`, `password`, `create_time`, `update_time`) VALUES ('admin', '9ae7b2e6f25c907a1fc81b503b16e25f', '1529926348', '1540194026'); + + +# 商家用户权限表 +CREATE TABLE `yoshop_store_access` ( + `access_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '权限名称', + `url` varchar(255) NOT NULL DEFAULT '' COMMENT '权限url', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级id', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '100' COMMENT '排序(数字越小越靠前)', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`access_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10114 DEFAULT CHARSET=utf8 COMMENT='商家用户权限表'; + +INSERT INTO `yoshop_store_access` VALUES ('10001', '首页', 'index/index', '0', '100', '1540628721', '1540781975'); +INSERT INTO `yoshop_store_access` VALUES ('10002', '管理员', 'store', '0', '105', '1540628721', '1552635011'); +INSERT INTO `yoshop_store_access` VALUES ('10003', '管理员管理', 'store.user', '10002', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10004', '管理员列表', 'store.user/index', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10005', '添加管理员', 'store.user/add', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10006', '编辑管理员', 'store.user/edit', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10007', '删除管理员', 'store.user/delete', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10008', '角色管理', 'store.role', '10002', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10009', '角色列表', 'store.role/index', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10010', '添加角色', 'store.role/add', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10011', '编辑角色', 'store.role/edit', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10012', '删除角色', 'store.role/delete', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10018', '商品管理', 'goods', '0', '110', '1540628721', '1552635017'); +INSERT INTO `yoshop_store_access` VALUES ('10019', '商品管理', 'goods', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10020', '商品列表', 'goods/index', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10021', '添加商品', 'goods/add', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10022', '编辑商品', 'goods/edit', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10023', '复制商品', 'goods/copy', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10024', '删除商品', 'goods/delete', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10025', '商品上下架', 'goods/state', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10026', '商品分类', 'goods.category', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10027', '分类列表', 'goods.category/index', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10028', '添加分类', 'goods.category/add', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10029', '编辑分类', 'goods.category/edit', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10030', '删除分类', 'goods.category/delete', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10031', '商品评价', 'goods.comment', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10032', '评价列表', 'goods.comment/index', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10033', '评价详情', 'goods.comment/detail', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10034', '删除评价', 'goods.comment/delete', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10035', '订单管理', 'order', '0', '115', '1540628721', '1552635035'); +INSERT INTO `yoshop_store_access` VALUES ('10036', '订单列表', '', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10037', '待发货', 'order/delivery_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10038', '待收货', 'order/receipt_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10039', '待付款', 'order/pay_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10040', '已完成', 'order/complete_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10041', '已取消', 'order/cancel_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10042', '全部订单', 'order/all_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10043', '订单详情', '', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10044', '详情信息', 'order/detail', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10045', '确认发货', 'order/delivery', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10046', '修改订单价格', 'order/updateprice', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10047', '订单导出', 'order.operate/export', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10048', '批量发货', 'order.operate/batchdelivery', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10049', '用户管理', 'user', '0', '120', '1540628721', '1552635042'); +INSERT INTO `yoshop_store_access` VALUES ('10050', '用户列表', 'user/index', '10049', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10051', '删除用户', 'user/delete', '10049', '105', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10052', '营销管理', 'market', '0', '135', '1540628721', '1552635080'); +INSERT INTO `yoshop_store_access` VALUES ('10053', '优惠券', 'coupon', '10052', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10054', '优惠券列表', 'market.coupon/index', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10055', '新增优惠券', 'market.coupon/add', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10056', '编辑优惠券', 'market.coupon/edit', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10057', '删除优惠券', 'market.coupon/delete', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10058', '领取记录', 'market.coupon/receive', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10059', '小程序', 'wxapp', '0', '140', '1540628721', '1552635087'); +INSERT INTO `yoshop_store_access` VALUES ('10060', '小程序设置', 'wxapp/setting', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10061', '页面管理', 'wxapp.page', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10062', '页面设计', '', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10063', '页面列表', 'wxapp.page/index', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10064', '新增页面', 'wxapp.page/add', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10065', '编辑页面', 'wxapp.page/edit', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10066', '设为首页', 'wxapp.page/sethome', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10067', '分类页模板', 'wxapp.page/category', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10068', '页面链接', 'wxapp.page/links', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10069', '帮助中心', 'wxapp.help', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10070', '帮助列表', 'wxapp.help/index', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10071', '新增帮助', 'wxapp.help/add', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10072', '编辑帮助', 'wxapp.help/edit', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10073', '删除帮助', 'wxapp.help/delete', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10074', '应用中心', 'apps', '0', '145', '1540628721', '1552635094'); +INSERT INTO `yoshop_store_access` VALUES ('10075', '分销中心', 'apps.dealer', '10074', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10076', '入驻申请', 'apps.dealer.apply', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10077', '申请列表', 'apps.dealer.apply/index', '10076', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10078', '分销商审核', 'apps.dealer.apply/submit', '10076', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10079', '分销商用户', 'apps.dealer.user', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10080', '分销商列表', 'apps.dealer.user/index', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10081', '删除分销商', 'apps.dealer.user/delete', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10082', '分销商二维码', 'apps.dealer.user/qrcode', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10083', '分销订单', 'apps.dealer.order/index', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10084', '提现申请', 'apps.dealer.withdraw', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10085', '申请列表', 'apps.dealer.withdraw/index', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10086', '提现审核', 'apps.dealer.withdraw/submit', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10087', '确认打款', 'apps.dealer.withdraw/money', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10088', '分销设置', 'apps.dealer.setting/index', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10089', '分销海报', 'apps.dealer.setting/qrcode', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10090', '设置', 'setting', '0', '150', '1540628721', '1552635100'); +INSERT INTO `yoshop_store_access` VALUES ('10091', '商城设置', 'setting/store', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10092', '交易设置', 'setting/trade', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10093', '运费模板', 'setting.delivery', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10094', '运费模板列表', 'setting.delivery/index', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10095', '新增运费模板', 'setting.delivery/add', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10096', '编辑运费模板', 'setting.delivery/edit', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10097', '删除运费模板', 'setting.delivery/delete', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10098', '物流公司', 'setting.express', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10099', '物流公司列表', 'setting.express/index', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10100', '新增物流公司', 'setting.express/add', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10101', '编辑物流公司', 'setting.express/edit', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10102', '删除物流公司', 'setting.express/delete', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10103', '短信通知', 'setting/sms', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10104', '模板消息', 'setting/tplmsg', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10105', '上传设置', 'setting/storage', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10106', '其他', '', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10107', '清理缓存', 'setting.cache/clear', '10106', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10108', '审核用户取消申请', 'order.operate/confirmcancel', '10043', '100', '1542163260', '1542163260'); +INSERT INTO `yoshop_store_access` VALUES ('10109', '售后管理', 'order.refund', '10035', '100', '1542161684', '1542161684'); +INSERT INTO `yoshop_store_access` VALUES ('10110', '售后列表', 'order.refund/index', '10109', '100', '1542161714', '1542161714'); +INSERT INTO `yoshop_store_access` VALUES ('10111', '售后详情', 'order.refund/detail', '10109', '100', '1542161736', '1542161736'); +INSERT INTO `yoshop_store_access` VALUES ('10112', '审核售后单', 'order.refund/audit', '10109', '100', '1542162196', '1542162196'); +INSERT INTO `yoshop_store_access` VALUES ('10113', '确认收货并退款', 'order.refund/receipt', '10109', '100', '1542162231', '1542162231'); +INSERT INTO `yoshop_store_access` VALUES ('10114', '退货地址管理', 'setting.address', '10090', '100', '1542609573', '1542609573'); +INSERT INTO `yoshop_store_access` VALUES ('10115', '退货地址列表', 'setting.address/index', '10114', '100', '1542609598', '1542609598'); +INSERT INTO `yoshop_store_access` VALUES ('10116', '新增退货地址', 'setting.address/add', '10114', '100', '1542609630', '1542609630'); +INSERT INTO `yoshop_store_access` VALUES ('10117', '编辑退货地址', 'setting.address/edit', '10114', '100', '1542609656', '1542609656'); +INSERT INTO `yoshop_store_access` VALUES ('10118', '删除退货地址', 'setting.address/delete', '10114', '100', '1542609680', '1542609680'); +INSERT INTO `yoshop_store_access` VALUES ('10300', '拼团管理', 'apps.sharing', '10074', '100', '1544601161', '1544601161'); +INSERT INTO `yoshop_store_access` VALUES ('10301', '商品分类', 'apps.sharing.category', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10302', '分类列表', 'apps.sharing.category/index', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10303', '添加分类', 'apps.sharing.category/add', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10304', '编辑分类', 'apps.sharing.category/edit', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10305', '删除分类', 'apps.sharing.category/delete', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10306', '商品管理', 'apps.sharing.goods', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10307', '商品列表', 'apps.sharing.goods/index', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10308', '添加商品', 'apps.sharing.goods/add', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10309', '编辑商品', 'apps.sharing.goods/edit', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10310', '复制商品', 'apps.sharing.goods/copy', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10311', '删除商品', 'apps.sharing.goods/delete', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10312', '商品上下架', 'apps.sharing.goods/state', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10313', '拼单管理', 'apps.sharing.active', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10314', '拼单列表', 'apps.sharing.active/index', '10313', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10315', '拼单成员列表', 'apps.sharing.active/users', '10313', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10316', '订单管理', 'apps.sharing.order', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10317', '订单列表', 'apps.sharing.order/index', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10318', '订单详情', '', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10319', '详情信息', 'apps.sharing.order/detail', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10320', '确认发货', 'apps.sharing.order/delivery', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10321', '修改订单价格', 'apps.sharing.order/updateprice', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10322', '审核用户取消订单', 'apps.sharing.order/confirmcancel', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10323', '拼团失败手动退款', 'apps.sharing.order/refund', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10324', '订单导出', 'apps.sharing.order.operate/export', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10325', '批量发货', 'apps.sharing.order.operate/batchdelivery', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10326', '售后管理', 'apps.sharing.order.refund', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10327', '售后列表', 'apps.sharing.order.refund/index', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10328', '售后详情', 'apps.sharing.order.refund/detail', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10329', '审核售后单', 'apps.sharing.order.refund/audit', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10330', '确认收货并退款', 'apps.sharing.order.refund/receipt', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10331', '商品评价', 'apps.sharing.comment', '10300', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10332', '评价列表', 'apps.sharing.comment/index', '10331', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10333', '评价详情', 'apps.sharing.comment/detail', '10331', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10334', '删除评价', 'apps.sharing.comment/delete', '10331', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10335', '拼团设置', 'apps.sharing.setting/index', '10300', '105', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10336', '下级用户列表', 'apps.dealer.user/fans', '10079', '100', '1545189676', '1545189676'); +INSERT INTO `yoshop_store_access` VALUES ('10337', '内容管理', 'content', '0', '130', '1547018818', '1552635075'); +INSERT INTO `yoshop_store_access` VALUES ('10338', '文章管理', 'content.article', '10337', '100', '1547018849', '1547018869'); +INSERT INTO `yoshop_store_access` VALUES ('10339', '文章列表', 'content.article/index', '10338', '100', '1547018885', '1547018885'); +INSERT INTO `yoshop_store_access` VALUES ('10340', '添加文章', 'content.article/add', '10338', '100', '1547018901', '1547018901'); +INSERT INTO `yoshop_store_access` VALUES ('10341', '编辑文章', 'content.article/edit', '10338', '100', '1547018922', '1547018922'); +INSERT INTO `yoshop_store_access` VALUES ('10342', '删除文章', 'content.article/delete', '10338', '100', '1547018937', '1547018937'); +INSERT INTO `yoshop_store_access` VALUES ('10343', '文章分类', 'content.article.category', '10337', '100', '1547018972', '1547018972'); +INSERT INTO `yoshop_store_access` VALUES ('10344', '分类列表', 'content.article.category/index', '10343', '100', '1547018992', '1547018992'); +INSERT INTO `yoshop_store_access` VALUES ('10345', '添加分类', 'content.article.category/add', '10343', '100', '1547019008', '1547019017'); +INSERT INTO `yoshop_store_access` VALUES ('10346', '编辑分类', 'content.article.category/edit', '10343', '100', '1547019008', '1547019017'); +INSERT INTO `yoshop_store_access` VALUES ('10347', '删除分类', 'content.article.category/delete', '10343', '100', '1547019008', '1547019017'); +INSERT INTO `yoshop_store_access` VALUES ('10348', '微信付款', 'apps.dealer.withdraw/wechat_pay', '10084', '100', '1548232045', '1548232045'); +INSERT INTO `yoshop_store_access` VALUES ('10349', '小票打印机', 'setting.printer', '10090', '100', '1548738285', '1548738285'); +INSERT INTO `yoshop_store_access` VALUES ('10350', '打印机管理', 'setting.printer', '10349', '100', '1548738718', '1548738718'); +INSERT INTO `yoshop_store_access` VALUES ('10351', '小票打印设置', 'setting/printer', '10349', '100', '1548738720', '1548738720'); +INSERT INTO `yoshop_store_access` VALUES ('10352', '小票打印机列表', 'setting.printer/index', '10350', '100', '1548738420', '1548738420'); +INSERT INTO `yoshop_store_access` VALUES ('10353', '新增小票打印机', 'setting.printer/add', '10350', '100', '1548738443', '1548738443'); +INSERT INTO `yoshop_store_access` VALUES ('10354', '编辑小票打印机', 'setting.printer/edit', '10350', '100', '1548738443', '1548738443'); +INSERT INTO `yoshop_store_access` VALUES ('10355', '删除小票打印机', 'setting.printer/delete', '10350', '100', '1548738443', '1548738443'); +INSERT INTO `yoshop_store_access` VALUES ('10356', '门店管理', 'shop', '0', '125', '1551504862', '1552635061'); +INSERT INTO `yoshop_store_access` VALUES ('10357', '门店管理', 'shop', '10356', '105', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10358', '门店列表', 'shop/index', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10359', '添加门店', 'shop/add', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10360', '编辑门店', 'shop/edit', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10361', '删除门店', 'shop/delete', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10362', '店员管理', 'shop.clerk', '10356', '110', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10363', '店员列表', 'shop.clerk/index', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10364', '添加店员', 'shop.clerk/add', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10365', '编辑店员', 'shop.clerk/edit', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10366', '删除店员', 'shop.clerk/delete', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` VALUES ('10367', '订单核销记录', 'shop.order/index', '10356', '115', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10368', '门店自提核销', 'order.operate/extract', '10043', '100', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10369', '门店自提核销', 'apps.sharing.order.operate/extract', '10318', '100', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10370', '满额包邮', 'market.basic/full_free', '10052', '125', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` VALUES ('10375', '文件库管理', 'content.files.group', '10337', '105', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10376', '文件分组', 'content.files.group', '10375', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10377', '分组列表', 'content.files.group/index', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10378', '添加分组', 'content.files.group/add', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10379', '编辑分组', 'content.files.group/edit', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10380', '删除分组', 'content.files.group/delete', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10381', '文件管理', 'content.files.group', '10375', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10382', '文件列表', 'content.files.group/index', '10381', '105', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10383', '回收站列表', 'content.files.group/recycle', '10381', '110', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10384', '移入回收站', 'content.files.group/add', '10381', '115', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10385', '回收站还原', 'content.files.group/edit', '10381', '120', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10386', '删除文件', 'content.files.group/delete', '10381', '125', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` VALUES ('10387', '余额记录', 'user.balance', '10049', '125', '1554685953', '1554685965'); +INSERT INTO `yoshop_store_access` VALUES ('10388', '充值记录', 'user.recharge/order', '10387', '100', '1554686010', '1554686010'); +INSERT INTO `yoshop_store_access` VALUES ('10389', '余额明细', 'user.balance/log', '10387', '105', '1554686031', '1554686031'); +INSERT INTO `yoshop_store_access` VALUES ('10390', '用户充值', 'market.recharge', '10052', '110', '1554686283', '1554686339'); +INSERT INTO `yoshop_store_access` VALUES ('10391', '充值套餐', 'market.recharge.plan', '10390', '100', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` VALUES ('10392', '套餐列表', 'market.recharge.plan/index', '10391', '100', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` VALUES ('10393', '添加套餐', 'market.recharge.plan/add', '10391', '105', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` VALUES ('10394', '编辑套餐', 'market.recharge.plan/edit', '10391', '110', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` VALUES ('10395', '删除套餐', 'market.recharge.plan/delete', '10391', '115', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` VALUES ('10396', '充值设置', 'market.recharge/setting', '10390', '105', '1554686647', '1554686647'); +INSERT INTO `yoshop_store_access` VALUES ('10400', '好物圈', 'apps.wow', '10074', '110', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10401', '商品收藏', 'apps.wow.shoping', '10400', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10402', '订单信息', 'apps.wow.order', '10400', '105', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10403', '基础设置', 'apps.wow.setting/index', '10400', '110', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10404', '商品收藏记录', 'apps.wow.shoping/index', '10401', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10405', '取消同步', 'apps.wow.shoping/delete', '10401', '105', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10406', '订单同步记录', 'apps.wow.order/index', '10402', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10407', '取消同步', 'apps.wow.order/delete', '10402', '105', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10408', '用户充值', 'user/recharge', '10049', '110', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` VALUES ('10411', '修改会员等级', 'user/grade', '10049', '115', '1558317213', '1558317226'); +INSERT INTO `yoshop_store_access` VALUES ('10412', '会员等级管理', 'user.grade', '10049', '120', '1558317440', '1558317440'); +INSERT INTO `yoshop_store_access` VALUES ('10413', '会员等级列表', 'user.grade/index', '10412', '100', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10414', '新增等级', 'user.grade/add', '10412', '105', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10415', '编辑等级', 'user.grade/edit', '10412', '110', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10416', '删除等级', 'user.grade/delete', '10412', '115', '1558317464', '1558317464'); + +INSERT INTO `yoshop_store_access` VALUES ('10426', '砍价活动', 'apps.bargain', '10074', '100', '1559615418', '1559615441'); +INSERT INTO `yoshop_store_access` VALUES ('10427', '砍价活动管理', 'apps.bargain.active', '10426', '100', '1559615566', '1559615566'); +INSERT INTO `yoshop_store_access` VALUES ('10428', '砍价活动列表', 'apps.bargain.active/index', '10427', '100', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10429', '新增砍价活动', 'apps.bargain.active/add', '10427', '105', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10430', '编辑砍价活动', 'apps.bargain.active/edit', '10427', '110', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10431', '删除砍价活动', 'apps.bargain.active/delete', '10427', '115', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10432', '砍价记录', 'apps.bargain.task', '10426', '105', '1559615788', '1559615788'); +INSERT INTO `yoshop_store_access` VALUES ('10433', '砍价记录列表', 'apps.bargain.task/index', '10432', '100', '1559615815', '1559615815'); +INSERT INTO `yoshop_store_access` VALUES ('10434', '砍价助力榜', 'apps.bargain.task/help', '10432', '105', '1559615850', '1559615850'); +INSERT INTO `yoshop_store_access` VALUES ('10435', '删除砍价记录', 'apps.bargain.task/delete', '10432', '110', '1559615878', '1559615878'); +INSERT INTO `yoshop_store_access` VALUES ('10436', '砍价设置', 'apps.bargain.setting/index', '10426', '110', '1559615946', '1559615979'); + +INSERT INTO `yoshop_store_access` VALUES ('10437', '复制主商城商品', 'apps.sharing.goods/copy_master', '10306', '112', '1559615946', '1559615979'); + + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10021'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10022'); +UPDATE `yoshop_store_access` SET `sort`='115' WHERE (`access_id`='10023'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10024'); +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10025'); + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10307'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10308'); +UPDATE `yoshop_store_access` SET `sort`='115' WHERE (`access_id`='10309'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10310'); +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10311'); + +INSERT INTO `yoshop_store_access` VALUES ('10443', '活跃用户', 'market.push/user', '10441', '105', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10442', '发送消息', 'market.push/send', '10441', '100', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10441', '消息推送', 'market.push', '10052', '120', '1561080292', '1561080292'); +INSERT INTO `yoshop_store_access` VALUES ('10440', '积分明细', 'market.points/log', '10438', '105', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10439', '积分设置', 'market.points/setting', '10438', '100', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10438', '积分管理', 'market.points', '10052', '115', '1561080292', '1561080292'); + +INSERT INTO `yoshop_store_access` VALUES ('11003', '数据统计', 'statistics.data/index', '0', '138', '1572507520', '1572507520'); + + + +# 商家用户角色表 +CREATE TABLE `yoshop_store_role` ( + `role_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '角色id', + `role_name` varchar(50) NOT NULL DEFAULT '' COMMENT '角色名称', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级角色id', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '100' COMMENT '排序(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`role_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色表'; + + +# 商家用户角色权限关系表 +CREATE TABLE `yoshop_store_role_access` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色id', + `access_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `role_id` (`role_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色权限关系表'; + + +# 商家用户角色记录表 +CREATE TABLE `yoshop_store_user_role` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `store_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '超管用户id', + `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `admin_user_id` (`store_user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色记录表'; + + +# 退货地址记录表 +CREATE TABLE `yoshop_return_address` ( + `address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '退货地址id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序 (数字越小越靠前)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`address_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='退货地址记录表'; + + +# 售后单记录表 +CREATE TABLE `yoshop_order_refund` ( + `order_refund_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '售后单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后类型(10退货退款 20换货)', + `apply_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '用户申请原因(说明)', + `is_agree` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家审核状态(0待审核 10已同意 20已拒绝)', + `refuse_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '商家拒绝原因(说明)', + `refund_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际退款金额', + `is_user_send` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '用户是否发货(0未发货 1已发货)', + `send_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户发货时间', + `express_id` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流公司id', + `express_no` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流单号', + `is_receipt` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家收货状态(0未收货 1已收货)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后单状态(0进行中 10已拒绝 20已完成 30已取消)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_refund_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单记录表'; + + +# 售后单退货地址记录表 +CREATE TABLE `yoshop_order_refund_address` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单退货地址记录表'; + + +# 售后单图片记录表 +CREATE TABLE `yoshop_order_refund_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单图片记录表'; + + +# 新增:拼团拼单记录表 +CREATE TABLE `yoshop_sharing_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '拼单id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `actual_people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '当前已拼人数', + `creator_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '团长用户id', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单结束时间', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '拼单状态(0未拼单 10拼单中 20拼单成功 30拼单失败)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团拼单记录表'; + + +# 新增:拼团拼单成员记录表 +CREATE TABLE `yoshop_sharing_active_users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_creator` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为创建者', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团拼单成员记录表'; + + +# 新增:拼团商品分类表 +CREATE TABLE `yoshop_sharing_category` ( + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品分类id', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上级分类id', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分类图片id', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品分类表'; + + +# 新增:拼团商品评价表 +CREATE TABLE `yoshop_sharing_comment` ( + `comment_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '评价id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `score` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '评分(10好评 20中评 30差评)', + `content` text NOT NULL COMMENT '评价内容', + `is_picture` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为图片评价', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '评价排序', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态(0隐藏 1显示)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`comment_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品评价表'; + + +# 新增:拼团评价图片记录表 +CREATE TABLE `yoshop_sharing_comment_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `comment_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '评价id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团评价图片记录表'; + + +# 新增:拼团商品记录表 +CREATE TABLE `yoshop_sharing_goods` ( + `goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '拼团商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品分类id', + `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点', + `people` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `group_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团有效时间(单位:小时)', + `is_alone` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否允许单买(0不允许 1允许)', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格(10单规格 20多规格)', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `content` longtext NOT NULL COMMENT '商品详情', + `sales_initial` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '初始销量', + `sales_actual` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际销量', + `goods_sort` int(11) unsigned NOT NULL DEFAULT '100' COMMENT '商品排序(数字越小越靠前)', + `delivery_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '配送模板id', + `is_points_gift` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否开启积分赠送(1开启 0关闭)', + `is_points_discount` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否允许使用积分抵扣(1允许 0不允许)', + `is_enable_grade` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否开启会员折扣(1开启 0关闭)', + `is_alone_grade` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '会员折扣设置(0默认等级折扣 1单独设置折扣)', + `alone_grade_equity` text COMMENT '单独设置折扣的配置', + `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)', + `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)', + `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `goods_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '商品状态(10上架 20下架)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_id`), + KEY `category_id` (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品记录表'; + + +# 新增:商品图片记录表 +CREATE TABLE `yoshop_sharing_goods_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `image_id` int(11) NOT NULL COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品图片记录表'; + + +# 新增:拼团商品规格表 +CREATE TABLE `yoshop_sharing_goods_sku` ( + `goods_sku_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品规格id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '商品sku记录索引(由规格id组成)', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格图片id', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `sharing_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '拼团价格', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格(单买价)', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `stock_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '当前库存数量', + `goods_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品销量', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_sku_id`), + UNIQUE KEY `sku_idx` (`goods_id`,`spec_sku_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品规格表'; + + +# 新增:拼团商品与规格值关系记录表 +CREATE TABLE `yoshop_sharing_goods_spec_rel` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格组id', + `spec_value_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格值id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品与规格值关系记录表'; + + +# 新增:拼团订单记录表 +CREATE TABLE `yoshop_sharing_order` ( + `order_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品总金额(不含优惠折扣)', + `order_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单金额(含优惠折扣)', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单类型(10单独购买 20拼团)', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单id', + `coupon_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '优惠券id', + `coupon_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券抵扣金额', + `points_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '积分抵扣金额', + `points_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '积分抵扣数量', + `pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款金额(包含运费、优惠)', + `update_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '后台修改的订单金额(差价)', + `buyer_remark` varchar(255) NOT NULL DEFAULT '' COMMENT '买家留言', + `pay_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '支付方式(10余额支付 20微信支付)', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '付款状态(10未付款 20已付款)', + `pay_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间', + `delivery_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '配送方式(10快递配送 20上门自提)', + `extract_shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '自提门店id', + `extract_clerk_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '核销店员id', + `express_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '运费金额', + `express_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '物流公司id', + `express_company` varchar(50) NOT NULL DEFAULT '' COMMENT '物流公司', + `express_no` varchar(50) NOT NULL DEFAULT '' COMMENT '物流单号', + `delivery_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '发货状态(10未发货 20已发货)', + `delivery_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '发货时间', + `receipt_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '收货状态(10未收货 20已收货)', + `receipt_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '收货时间', + `order_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单状态(10进行中 20已取消 21待取消 30已完成)', + `points_bonus` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '赠送的积分数量', + `is_settled` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单是否已结算(0未结算 1已结算)', + `is_refund` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '拼团未成功退款(0未退款 1已退款)', + `transaction_id` varchar(30) NOT NULL DEFAULT '' COMMENT '微信支付交易号', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_id`), + UNIQUE KEY `order_no` (`order_no`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单记录表'; + + +# 新增:拼团订单收货地址记录表 +CREATE TABLE `yoshop_sharing_order_address` ( + `order_address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '地址id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在区id', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_address_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团订单收货地址记录表'; + + +# 新增:拼团订单商品记录表 +CREATE TABLE `yoshop_sharing_order_goods` ( + `order_goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品封面图id', + `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点', + `people` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `group_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团有效时间(单位:小时)', + `is_alone` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否允许单买(0不允许 1允许)', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '规格类型(10单规格 20多规格)', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品sku标识', + `goods_sku_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格id', + `goods_attr` varchar(500) NOT NULL DEFAULT '' COMMENT '商品规格信息', + `content` longtext NOT NULL COMMENT '商品详情', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格(单价)', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `is_user_grade` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否存在会员等级折扣', + `grade_ratio` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '会员折扣比例(0-10)', + `grade_goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '会员折扣的商品单价', + `grade_total_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '会员折扣的总额差', + `coupon_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券折扣金额', + `points_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '积分金额', + `points_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '积分抵扣数量', + `points_bonus` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '赠送的积分数量', + `total_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '购买数量', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品总价(数量×单价)', + `total_pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款价(包含优惠、折扣)', + `is_ind_dealer` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否开启单独分销(0关闭 1开启)', + `dealer_money_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '分销佣金类型(10百分比 20固定金额)', + `first_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_goods_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='订单商品记录表'; + + +# 新增:拼团售后单记录表 +CREATE TABLE `yoshop_sharing_order_refund` ( + `order_refund_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '售后单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后类型(10退货退款 20换货)', + `apply_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '用户申请原因(说明)', + `is_agree` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家审核状态(0待审核 10已同意 20已拒绝)', + `refuse_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '商家拒绝原因(说明)', + `refund_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际退款金额', + `is_user_send` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '用户是否发货(0未发货 1已发货)', + `send_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户发货时间', + `express_id` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流公司id', + `express_no` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流单号', + `is_receipt` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家收货状态(0未收货 1已收货)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后单状态(0进行中 10已拒绝 20已完成 30已取消)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_refund_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单记录表'; + + +# 新增:拼团售后单退货地址记录表 +CREATE TABLE `yoshop_sharing_order_refund_address` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单退货地址记录表'; + + +# 新增:拼团售后单图片记录表 +CREATE TABLE `yoshop_sharing_order_refund_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单图片记录表'; + + +# 新增:拼团设置表 +CREATE TABLE `yoshop_sharing_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='拼团设置表'; + + +# 新增文章记录表 +CREATE TABLE `yoshop_article` ( + `article_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '文章id', + `article_title` varchar(300) NOT NULL DEFAULT '' COMMENT '文章标题', + `show_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '列表显示方式(10小图展示 20大图展示)', + `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文章分类id', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '封面图id', + `article_content` longtext NOT NULL COMMENT '文章内容', + `article_sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文章排序(数字越小越靠前)', + `article_status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '文章状态(0隐藏 1显示)', + `virtual_views` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '虚拟阅读量(仅用作展示)', + `actual_views` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际阅读量', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`article_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文章记录表'; + + +# 新增文章分类表 +CREATE TABLE `yoshop_article_category` ( + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品分类id', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文章分类表'; + + +# 小票打印机记录表 +CREATE TABLE `yoshop_printer` ( + `printer_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '打印机id', + `printer_name` varchar(255) NOT NULL DEFAULT '' COMMENT '打印机名称', + `printer_type` varchar(255) NOT NULL DEFAULT '' COMMENT '打印机类型', + `printer_config` text NOT NULL COMMENT '打印机配置', + `print_times` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '打印联数(次数)', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序 (数字越小越靠前)', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`printer_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='小票打印机记录表'; + + +# 商家门店记录表 +CREATE TABLE `yoshop_store_shop` ( + `shop_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '门店id', + `shop_name` varchar(255) NOT NULL DEFAULT '' COMMENT '门店名称', + `logo_image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '门店logo图片id', + `linkman` varchar(20) NOT NULL DEFAULT '' COMMENT '联系人', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `shop_hours` varchar(255) NOT NULL DEFAULT '' COMMENT '营业时间', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在辖区id', + `address` varchar(100) NOT NULL DEFAULT '' COMMENT '详细地址', + `longitude` varchar(50) NOT NULL DEFAULT '' COMMENT '门店坐标经度', + `latitude` varchar(50) NOT NULL DEFAULT '' COMMENT '门店坐标纬度', + `geohash` varchar(50) NOT NULL DEFAULT '' COMMENT 'geohash', + `summary` varchar(1000) NOT NULL DEFAULT '0' COMMENT '门店简介', + `sort` tinyint(3) NOT NULL DEFAULT 0 COMMENT '门店排序(数字越小越靠前)', + `is_check` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否支持自提核销(0否 1支持)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '门店状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`shop_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店记录表'; + + +# 商家门店店员表 +CREATE TABLE `yoshop_store_shop_clerk` ( + `clerk_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '店员id', + `shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所属门店id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '店员姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`clerk_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店店员表'; + + +#商家门店核销订单记录表 +CREATE TABLE `yoshop_store_shop_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)', + `shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '门店id', + `clerk_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '核销员id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店核销订单记录表'; + + +# 用户充值订单表 +CREATE TABLE `yoshop_recharge_order` ( + `order_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `recharge_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '充值方式(10自定义金额 20套餐充值)', + `plan_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '充值套餐id', + `pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '用户支付金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `actual_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际到账金额', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '支付状态(10待支付 20已支付)', + `pay_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间', + `transaction_id` varchar(30) NOT NULL DEFAULT '' COMMENT '微信支付交易号', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户充值订单表'; + +# 用户充值订单套餐快照表 +CREATE TABLE `yoshop_recharge_order_plan` ( + `order_plan_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `plan_id` int(11) unsigned NOT NULL COMMENT '主键id', + `plan_name` varchar(255) NOT NULL DEFAULT '' COMMENT '方案名称', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '充值金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_plan_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户充值订单套餐快照表'; + +# 余额充值套餐表 +CREATE TABLE `yoshop_recharge_plan` ( + `plan_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `plan_name` varchar(255) NOT NULL DEFAULT '' COMMENT '套餐名称', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '充值金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '排序(数字越小越靠前)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`plan_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='余额充值套餐表'; + +# 用户余额变动明细表 +CREATE TABLE `yoshop_user_balance_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `scene` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '余额变动场景(10用户充值 20用户消费 30管理员操作 40订单退款)', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变动金额', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述/说明', + `remark` varchar(500) NOT NULL DEFAULT '' COMMENT '管理员备注', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户余额变动明细表'; + +# 自提订单联系方式记录表 +CREATE TABLE `yoshop_order_extract` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `linkman` varchar(30) NOT NULL DEFAULT '' COMMENT '联系人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='自提订单联系方式记录表'; + +# 自提订单联系方式记录表 +CREATE TABLE `yoshop_sharing_order_extract` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `linkman` varchar(30) NOT NULL DEFAULT '' COMMENT '联系人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='自提订单联系方式记录表'; + + +# 好物圈设置表 +CREATE TABLE `yoshop_wow_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='好物圈设置表'; + + +# 好物圈商品收藏记录表 +CREATE TABLE `yoshop_wow_shoping` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='好物圈商品收藏记录表'; + + +# 好物圈订单同步记录表 +CREATE TABLE `yoshop_wow_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单状态(3支付完成 4已发货 5已退款 100已完成)', + `last_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '最后更新时间', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='好物圈订单同步记录表'; + + +# 用户会员等级表 +CREATE TABLE `yoshop_user_grade` ( + `grade_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '等级ID', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '等级名称', + `weight` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '等级权重(1-9999)', + `upgrade` text NOT NULL COMMENT '升级条件', + `equity` text NOT NULL COMMENT '等级权益(折扣率0-100)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态(1启用 0禁用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`grade_id`), + KEY `wxapp_id` (`wxapp_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户会员等级表'; + + +# 用户会员等级变更记录表 +CREATE TABLE `yoshop_user_grade_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `old_grade_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '变更前的等级id', + `new_grade_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '变更后的等级id', + `change_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '变更类型(10后台管理员设置 20自动升级)', + `remark` varchar(500) DEFAULT '' COMMENT '管理员备注', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户会员等级变更记录表'; + +# 砍价活动表 +CREATE TABLE `yoshop_bargain_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '砍价活动id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动开始时间', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动结束时间', + `expiryt_time` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '砍价有效期(单位:小时)', + `floor_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍价底价', + `peoples` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帮砍人数', + `is_self_cut` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '可自砍一刀(0禁止 1允许)', + `is_floor_buy` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '必须底价购买(0否 1是)', + `share_title` varchar(500) NOT NULL DEFAULT '' COMMENT '分享标题', + `prompt_words` varchar(500) NOT NULL DEFAULT '' COMMENT '砍价助力语', + `actual_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动销量(实际的)', + `initial_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '虚拟销量', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序(数字越小越靠前)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '活动状态(1启用 0禁用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价活动表'; + +# 砍价活动设置表 +CREATE TABLE `yoshop_bargain_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='砍价活动设置表'; + +# 砍价任务表 +CREATE TABLE `yoshop_bargain_task` ( + `task_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '砍价任务id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价活动id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id(发起人)', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品sku标识', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品原价', + `floor_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍价底价', + `peoples` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帮砍人数', + `cut_people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已砍人数', + `section` text NOT NULL COMMENT '砍价金额区间', + `cut_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '已砍金额', + `actual_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际购买金额', + `is_floor` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已砍到底价(0否 1是)', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '任务截止时间', + `is_buy` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否购买(0未购买 1已购买)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '任务状态 (0已结束 1砍价中)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`task_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价任务表'; + +# 砍价任务助力记录表 +CREATE TABLE `yoshop_bargain_task_help` ( + `help_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价活动id', + `task_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价任务id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_creater` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为发起人(0否 1是)', + `cut_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍掉的金额', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`help_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价任务助力记录表'; + +# 用户积分变动明细表 +CREATE TABLE `yoshop_user_points_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `value` int(11) NOT NULL DEFAULT '0.00' COMMENT '变动数量', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述/说明', + `remark` varchar(500) NOT NULL DEFAULT '' COMMENT '管理员备注', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户积分变动明细表'; + + +CREATE TABLE `yoshop_sharp_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '活动会场ID', + `active_date` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动日期', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '活动状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场表'; + + +CREATE TABLE `yoshop_sharp_active_goods` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动会场ID', + `active_time_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动场次ID', + `sharp_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀商品ID', + `sales_actual` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际销量', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场与商品关联表'; + + +CREATE TABLE `yoshop_sharp_active_time` ( + `active_time_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '场次ID', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动会场ID', + `active_time` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '场次时间(0点-23点)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '活动状态(0禁用 1启用)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_time_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场场次表'; + + +CREATE TABLE `yoshop_sharp_goods` ( + `sharp_goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '秒杀商品ID', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品ID', + `deduct_stock_type` tinyint(3) unsigned DEFAULT '10' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `limit_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '限购数量', + `seckill_stock` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品库存总量', + `total_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '累积销量', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品排序(数字越小越靠前)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '商品状态(0下架 1上架)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`sharp_goods_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-商品表'; + +CREATE TABLE `yoshop_sharp_goods_sku` ( + `goods_sku_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品规格id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '商品sku记录索引 (由规格id组成)', + `sharp_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀商品id', + `seckill_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格', + `seckill_stock` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀库存数量', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_sku_id`), + UNIQUE KEY `sku_idx` (`sharp_goods_id`,`spec_sku_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-秒杀商品sku信息表'; + +CREATE TABLE `yoshop_sharp_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='整点秒杀设置表'; + + + + +ALTER TABLE `yoshop_order_goods` +ADD COLUMN `goods_source_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '来源记录id' AFTER `user_id`; + + +ALTER TABLE `yoshop_order` +MODIFY COLUMN `order_source` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '订单来源(10普通订单 20砍价订单 30秒杀订单)' AFTER `is_comment`; + + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10300'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10426'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10400'); + + + +INSERT INTO `yoshop_store_access` VALUES ('10444', '整点秒杀', 'apps.sharp', '10074', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10445', '秒杀商品', 'apps.sharp.goods', '10444', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10446', '商品列表', 'apps.sharp.goods/index', '10445', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10447', '新增商品', 'apps.sharp.goods/add', '10445', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10448', '编辑商品', 'apps.sharp.goods/edit', '10445', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10449', '删除商品', 'apps.sharp.goods/delete', '10445', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10450', '活动会场', 'apps.sharp.active', '10444', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10451', '会场列表', 'apps.sharp.active/index', '10450', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10452', '新增会场', 'apps.sharp.active/add', '10450', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10453', '修改活动状态', 'apps.sharp.active/state', '10450', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10454', '删除会场', 'apps.sharp.active/delete', '10450', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10455', '场次管理', 'apps.sharp.active_time', '10450', '120', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10456', '场次列表', 'apps.sharp.active_time/index', '10455', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10457', '新增场次', 'apps.sharp.active_time/add', '10455', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10458', '编辑场次', 'apps.sharp.active_time/edit', '10455', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10459', '修改活动状态', 'apps.sharp.active_time/state', '10455', '115', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10460', '删除场次', 'apps.sharp.active_time/delete', '10455', '120', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10461', '基础设置', 'apps.sharp.setting/index', '10444', '125', '1564449650', '1564449650'); + + diff --git a/doc/database/upgrade/v1.1.0.sql b/doc/database/upgrade/v1.1.0.sql new file mode 100644 index 0000000..f59a1ab --- /dev/null +++ b/doc/database/upgrade/v1.1.0.sql @@ -0,0 +1,31 @@ + +# 微信小程序diy页面表:页面标题改为页面名称 +ALTER TABLE `yoshop_wxapp_page` +CHANGE COLUMN `page_title` `page_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '页面名称' AFTER `page_type`; + + +# 微信小程序diy页面表:新增字段 软删除 +ALTER TABLE `yoshop_wxapp_page` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '软删除' AFTER `wxapp_id`; + + +# 微信小程序diy页面表:添加页面名称 +UPDATE `yoshop_wxapp_page` SET `page_name` = '小程序首页' WHERE page_id = 10001; + + +# 订单记录表:新增字段 后台修改的订单金额(差价) +ALTER TABLE `yoshop_order` +ADD COLUMN `update_price` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '后台修改的订单金额(差价)' AFTER `pay_price`; + + +# 微信小程序分类页模板表 +CREATE TABLE `yoshop_wxapp_category` ( + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `category_style` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '分类页样式(10一级分类[大图] 11一级分类[小图] 20二级分类)', + `share_title` varchar(10) NOT NULL DEFAULT '' COMMENT '分享标题', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='微信小程序分类页模板'; + +INSERT INTO `yoshop_wxapp_category` (`wxapp_id`, `category_style`, `share_title`, `create_time`, `update_time`) VALUES ('10001', '10', '', '1536373988', '1536375112'); diff --git a/doc/database/upgrade/v1.1.1.sql b/doc/database/upgrade/v1.1.1.sql new file mode 100644 index 0000000..4a2251f --- /dev/null +++ b/doc/database/upgrade/v1.1.1.sql @@ -0,0 +1,22 @@ + +# 订单记录表:买家留言 +ALTER TABLE `yoshop_order` +ADD COLUMN `buyer_remark` varchar(255) NOT NULL DEFAULT '' COMMENT '买家留言' AFTER `update_price`; + + +# 小程序prepay_id记录表 +CREATE TABLE `yoshop_wxapp_prepay_id` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `prepay_id` varchar(50) NOT NULL DEFAULT '' COMMENT '微信支付prepay_id', + `can_use_times` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '可使用次数', + `used_times` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '已使用次数', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '支付状态(1已支付)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `expiry_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `order_id` (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='小程序prepay_id记录'; diff --git a/doc/database/upgrade/v1.1.12.sql b/doc/database/upgrade/v1.1.12.sql new file mode 100644 index 0000000..1bba6c2 --- /dev/null +++ b/doc/database/upgrade/v1.1.12.sql @@ -0,0 +1,43 @@ + + +# 新增权限url:分销中心下级用户列表 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10336', '下级用户列表', 'apps.dealer.user/fans', '10079', '100', '1545189676', '1545189676'); + + +# 微信小程序分类页模板:分享标题字段增加 varchar长度 +ALTER TABLE `yoshop_wxapp_category` +MODIFY COLUMN `share_title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '分享标题' AFTER `category_style`; + + + +-- 主商品 -- +ALTER TABLE `yoshop_goods` ADD COLUMN `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)' AFTER `delivery_id`, + ADD COLUMN `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)' AFTER `is_ind_dealer`, + ADD COLUMN `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)' AFTER `dealer_money_type`, + ADD COLUMN `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)' AFTER `first_money`, + ADD COLUMN `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)' AFTER `second_money`; + + +ALTER TABLE `yoshop_order_goods` ADD COLUMN `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)' AFTER `total_pay_price`, + ADD COLUMN `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)' AFTER `is_ind_dealer`, + ADD COLUMN `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)' AFTER `dealer_money_type`, + ADD COLUMN `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)' AFTER `first_money`, + ADD COLUMN `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)' AFTER `second_money`; + + + + -- 拼团 -- + + ALTER TABLE `yoshop_sharing_goods` ADD COLUMN `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)' AFTER `delivery_id`, + ADD COLUMN `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)' AFTER `is_ind_dealer`, + ADD COLUMN `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)' AFTER `dealer_money_type`, + ADD COLUMN `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)' AFTER `first_money`, + ADD COLUMN `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)' AFTER `second_money`; + + +ALTER TABLE `yoshop_sharing_order_goods` ADD COLUMN `is_ind_dealer` TINYINT (3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否开启单独分销(0关闭 1开启)' AFTER `total_pay_price`, + ADD COLUMN `dealer_money_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '分销佣金类型(10百分比 20固定金额)' AFTER `is_ind_dealer`, + ADD COLUMN `first_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)' AFTER `dealer_money_type`, + ADD COLUMN `second_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)' AFTER `first_money`, + ADD COLUMN `third_money` DECIMAL (10, 2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)' AFTER `second_money`; + diff --git a/doc/database/upgrade/v1.1.13.sql b/doc/database/upgrade/v1.1.13.sql new file mode 100644 index 0000000..5d0d6dd --- /dev/null +++ b/doc/database/upgrade/v1.1.13.sql @@ -0,0 +1,48 @@ + + +# 新增文章记录表 +CREATE TABLE `yoshop_article` ( + `article_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '文章id', + `article_title` varchar(300) NOT NULL DEFAULT '' COMMENT '文章标题', + `show_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '列表显示方式(10小图展示 20大图展示)', + `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文章分类id', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '封面图id', + `article_content` longtext NOT NULL COMMENT '文章内容', + `article_sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '文章排序(数字越小越靠前)', + `article_status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '文章状态(0隐藏 1显示)', + `virtual_views` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '虚拟阅读量(仅用作展示)', + `actual_views` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际阅读量', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`article_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文章记录表'; + + +# 新增文章分类表 +CREATE TABLE `yoshop_article_category` ( + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品分类id', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='文章分类表'; + + +# 新增权限url:后台文章管理 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10337', '内容管理', 'content', '0', '100', '1547018818', '1547018818'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10338', '文章管理', 'content.article', '10337', '100', '1547018849', '1547018869'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10339', '文章列表', 'content.article/index', '10338', '100', '1547018885', '1547018885'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10340', '添加文章', 'content.article/add', '10338', '100', '1547018901', '1547018901'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10341', '编辑文章', 'content.article/edit', '10338', '100', '1547018922', '1547018922'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10342', '删除文章', 'content.article/delete', '10338', '100', '1547018937', '1547018937'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10343', '文章分类', 'content.article.category', '10337', '100', '1547018972', '1547018972'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10344', '分类列表', 'content.article.category/index', '10343', '100', '1547018992', '1547018992'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10345', '添加分类', 'content.article.category/add', '10343', '100', '1547019008', '1547019017'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10346', '编辑分类', 'content.article.category/edit', '10343', '100', '1547019008', '1547019017'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10347', '删除分类', 'content.article.category/delete', '10343', '100', '1547019008', '1547019017'); + + diff --git a/doc/database/upgrade/v1.1.16.sql b/doc/database/upgrade/v1.1.16.sql new file mode 100644 index 0000000..60c1845 --- /dev/null +++ b/doc/database/upgrade/v1.1.16.sql @@ -0,0 +1,33 @@ + + +# 小票打印机记录表 +CREATE TABLE `yoshop_printer` ( + `printer_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '打印机id', + `printer_name` varchar(255) NOT NULL DEFAULT '' COMMENT '打印机名称', + `printer_type` varchar(255) NOT NULL DEFAULT '' COMMENT '打印机类型', + `printer_config` text NOT NULL COMMENT '打印机配置', + `print_times` smallint(6) unsigned NOT NULL DEFAULT '0' COMMENT '打印联数(次数)', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序 (数字越小越靠前)', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`printer_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='小票打印机记录表'; + + + +# 新增权限url:分销商提现微信付款 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10348', '微信付款', 'apps.dealer.withdraw/wechat_pay', '10084', '100', '1548232045', '1548232045'); + + +# 新增权限url:小票打印机管理 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10349', '小票打印机', 'setting.printer', '10090', '100', '1548738285', '1548738285'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10350', '打印机管理', 'setting.printer', '10349', '100', '1548738718', '1548738718'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10351', '小票打印设置', 'setting/printer', '10349', '100', '1548738720', '1548738720'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10352', '小票打印机列表', 'setting.printer/index', '10350', '100', '1548738420', '1548738420'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10353', '新增小票打印机', 'setting.printer/add', '10350', '100', '1548738443', '1548738443'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10354', '编辑小票打印机', 'setting.printer/edit', '10350', '100', '1548738443', '1548738443'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10355', '删除小票打印机', 'setting.printer/delete', '10350', '100', '1548738443', '1548738443'); + + diff --git a/doc/database/upgrade/v1.1.17.sql b/doc/database/upgrade/v1.1.17.sql new file mode 100644 index 0000000..69c962b --- /dev/null +++ b/doc/database/upgrade/v1.1.17.sql @@ -0,0 +1,87 @@ + + +ALTER TABLE `yoshop_order` ADD COLUMN `delivery_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '配送方式(10快递配送 20上门自提)' AFTER `pay_time`; +ALTER TABLE `yoshop_order` ADD COLUMN `extract_shop_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '自提门店id' AFTER `delivery_type`; +ALTER TABLE `yoshop_order` ADD COLUMN `extract_clerk_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '核销店员id' AFTER `extract_shop_id`; + +ALTER TABLE `yoshop_sharing_order` ADD COLUMN `delivery_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '配送方式(10快递配送 20上门自提)' AFTER `pay_time`; +ALTER TABLE `yoshop_sharing_order` ADD COLUMN `extract_shop_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '自提门店id' AFTER `delivery_type`; +ALTER TABLE `yoshop_sharing_order` ADD COLUMN `extract_clerk_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '核销店员id' AFTER `extract_shop_id`; + + +# 商家门店记录表 +CREATE TABLE `yoshop_store_shop` ( + `shop_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '门店id', + `shop_name` varchar(255) NOT NULL DEFAULT '' COMMENT '门店名称', + `logo_image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '门店logo图片id', + `linkman` varchar(20) NOT NULL DEFAULT '' COMMENT '联系人', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `shop_hours` varchar(255) NOT NULL DEFAULT '' COMMENT '营业时间', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在辖区id', + `address` varchar(100) NOT NULL DEFAULT '' COMMENT '详细地址', + `longitude` varchar(50) NOT NULL DEFAULT '' COMMENT '门店坐标经度', + `latitude` varchar(50) NOT NULL DEFAULT '' COMMENT '门店坐标纬度', + `geohash` varchar(50) NOT NULL DEFAULT '' COMMENT 'geohash', + `summary` varchar(1000) NOT NULL DEFAULT '0' COMMENT '门店简介', + `is_check` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '是否支持自提核销(0否 1支持)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '门店状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`shop_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店记录表'; + + +# 商家门店店员表 +CREATE TABLE `yoshop_store_shop_clerk` ( + `clerk_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '店员id', + `shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所属门店id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '店员姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`clerk_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店店员表'; + + +#商家门店核销订单记录表 +CREATE TABLE `yoshop_store_shop_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)', + `shop_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '门店id', + `clerk_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '核销员id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家门店核销订单记录表'; + + + +# 新增权限url:门店管理 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10356', '门店管理', 'shop', '0', '100', '1551504862', '1551504862'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10357', '门店管理', 'shop', '10356', '105', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10358', '门店列表', 'shop/index', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10359', '添加门店', 'shop/add', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10360', '编辑门店', 'shop/edit', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10361', '删除门店', 'shop/delete', '10357', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10362', '店员管理', 'shop.clerk', '10356', '110', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10363', '店员列表', 'shop.clerk/index', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10364', '添加店员', 'shop.clerk/add', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10365', '编辑店员', 'shop.clerk/edit', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10366', '删除店员', 'shop.clerk/delete', '10362', '100', '1551505032', '1551505048'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10367', '订单核销记录', 'shop.order/index', '10356', '115', '1551505016', '1551505016'); + +# 新增权限url:订单核销 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10368', '门店自提核销', 'order.operate/extract', '10043', '100', '1551505016', '1551505016'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10369', '门店自提核销', 'apps.sharing.order.operate/extract', '10318', '100', '1551505016', '1551505016'); + +# 新增权限url:满额包邮 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10370', '满额包邮', 'market.basic/full_free', '10052', '100', '1551505016', '1551505016'); diff --git a/doc/database/upgrade/v1.1.18.sql b/doc/database/upgrade/v1.1.18.sql new file mode 100644 index 0000000..311b595 --- /dev/null +++ b/doc/database/upgrade/v1.1.18.sql @@ -0,0 +1,11 @@ + + +# 分销商订单记录表:新增 "订单是否失效" 字段 +ALTER TABLE `yoshop_dealer_order` +ADD COLUMN `is_invalid` tinyint(3) NOT NULL DEFAULT 0 COMMENT '订单是否失效 (0未失效 1已失效)' AFTER `third_money`; + + +# 门店记录表:新增 "排序" 字段 +ALTER TABLE `yoshop_store_shop` +ADD COLUMN `sort` tinyint(3) NOT NULL DEFAULT 0 COMMENT '门店排序(数字越小越靠前)' AFTER `summary`; + diff --git a/doc/database/upgrade/v1.1.19.sql b/doc/database/upgrade/v1.1.19.sql new file mode 100644 index 0000000..8e9c6a9 --- /dev/null +++ b/doc/database/upgrade/v1.1.19.sql @@ -0,0 +1,29 @@ + + +# 文件库记录表:新增 "是否已回收" 字段 +ALTER TABLE `yoshop_upload_file` +ADD COLUMN `is_recycle` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否已回收' AFTER `is_user`; + + +# 文件库分组记录表:新增 "是否删除" 字段 +ALTER TABLE `yoshop_upload_group` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `sort`; + + +# 新增权限url:后台文件库管理 + +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10375', '文件库管理', 'content.files.group', '10337', '105', '1552634170', '1552634170'); + +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10376', '文件分组', 'content.files.group', '10375', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10377', '分组列表', 'content.files.group/index', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10378', '添加分组', 'content.files.group/add', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10379', '编辑分组', 'content.files.group/edit', '10376', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10380', '删除分组', 'content.files.group/delete', '10376', '100', '1552634170', '1552634170'); + +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10381', '文件管理', 'content.files.group', '10375', '100', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10382', '文件列表', 'content.files.group/index', '10381', '105', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10383', '回收站列表', 'content.files.group/recycle', '10381', '110', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10384', '移入回收站', 'content.files.group/add', '10381', '115', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10385', '回收站还原', 'content.files.group/edit', '10381', '120', '1552634170', '1552634170'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10386', '删除文件', 'content.files.group/delete', '10381', '125', '1552634170', '1552634170'); + diff --git a/doc/database/upgrade/v1.1.21.sql b/doc/database/upgrade/v1.1.21.sql new file mode 100644 index 0000000..4c93f38 --- /dev/null +++ b/doc/database/upgrade/v1.1.21.sql @@ -0,0 +1,79 @@ + +ALTER TABLE `yoshop_user` ADD COLUMN `balance` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '用户可用余额' AFTER `address_id`; +ALTER TABLE `yoshop_order` ADD COLUMN `pay_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 20 COMMENT '支付方式(10余额支付 20微信支付)' AFTER `buyer_remark`; +ALTER TABLE `yoshop_sharing_order` ADD COLUMN `pay_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 20 COMMENT '支付方式(10余额支付 20微信支付)' AFTER `buyer_remark`; + + +CREATE TABLE `yoshop_recharge_order` ( + `order_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `recharge_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '充值方式(10自定义金额 20套餐充值)', + `plan_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '充值套餐id', + `pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '用户支付金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `actual_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际到账金额', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '支付状态(10待支付 20已支付)', + `pay_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间', + `transaction_id` varchar(30) NOT NULL DEFAULT '' COMMENT '微信支付交易号', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户充值订单表'; + + +CREATE TABLE `yoshop_recharge_order_plan` ( + `order_plan_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `plan_id` int(11) unsigned NOT NULL COMMENT '主键id', + `plan_name` varchar(255) NOT NULL DEFAULT '' COMMENT '方案名称', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '充值金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_plan_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户充值订单套餐快照表'; + + +CREATE TABLE `yoshop_recharge_plan` ( + `plan_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `plan_name` varchar(255) NOT NULL DEFAULT '' COMMENT '套餐名称', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '充值金额', + `gift_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '赠送金额', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '排序(数字越小越靠前)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`plan_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='余额充值套餐表'; + + +CREATE TABLE `yoshop_user_balance_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `scene` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '余额变动场景(10用户充值 20用户消费 30管理员操作 40订单退款)', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变动金额', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述/说明', + `remark` varchar(500) NOT NULL DEFAULT '' COMMENT '管理员备注', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户余额变动明细表'; + + + +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id` = '10370'); + +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10387', '余额记录', 'user.balance', '10049', '105', '1554685953', '1554685965'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10388', '充值记录', 'user.recharge/order', '10387', '100', '1554686010', '1554686010'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10389', '余额明细', 'user.balance/log', '10387', '105', '1554686031', '1554686031'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10390', '用户充值', 'market.recharge', '10052', '110', '1554686283', '1554686339'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10391', '充值套餐', 'market.recharge.plan', '10390', '100', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10392', '套餐列表', 'market.recharge.plan/index', '10391', '100', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10393', '添加套餐', 'market.recharge.plan/add', '10391', '105', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10394', '编辑套餐', 'market.recharge.plan/edit', '10391', '110', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10395', '删除套餐', 'market.recharge.plan/delete', '10391', '115', '1554686316', '1554686316'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10396', '充值设置', 'market.recharge/setting', '10390', '105', '1554686647', '1554686647'); diff --git a/doc/database/upgrade/v1.1.22.sql b/doc/database/upgrade/v1.1.22.sql new file mode 100644 index 0000000..76d87a9 --- /dev/null +++ b/doc/database/upgrade/v1.1.22.sql @@ -0,0 +1,27 @@ + +UPDATE `yoshop_store_access` SET `name`='运费模板' WHERE (`access_id`='10093'); + + +CREATE TABLE `yoshop_order_extract` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `linkman` varchar(30) NOT NULL DEFAULT '' COMMENT '联系人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=utf8 COMMENT='自提订单联系方式记录表'; + + +CREATE TABLE `yoshop_sharing_order_extract` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `linkman` varchar(30) NOT NULL DEFAULT '' COMMENT '联系人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='自提订单联系方式记录表'; + diff --git a/doc/database/upgrade/v1.1.23.sql b/doc/database/upgrade/v1.1.23.sql new file mode 100644 index 0000000..bcb5ed1 --- /dev/null +++ b/doc/database/upgrade/v1.1.23.sql @@ -0,0 +1,9 @@ + +UPDATE `yoshop_store_access` SET `name`='营销管理' WHERE (`access_id`='10052') + +ALTER TABLE `yoshop_order` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `user_id`; + +ALTER TABLE `yoshop_sharing_order` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `user_id`; + diff --git a/doc/database/upgrade/v1.1.24.sql b/doc/database/upgrade/v1.1.24.sql new file mode 100644 index 0000000..2cbce78 --- /dev/null +++ b/doc/database/upgrade/v1.1.24.sql @@ -0,0 +1,100 @@ + +START TRANSACTION; + + +# 好物圈设置表 +CREATE TABLE `yoshop_wow_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='好物圈设置表'; + + +# 好物圈商品收藏记录表 +CREATE TABLE `yoshop_wow_shoping` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='好物圈商品收藏记录表'; + + +# 好物圈订单同步记录表 +CREATE TABLE `yoshop_wow_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单状态(3支付完成 4已发货 5已退款 100已完成)', + `last_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '最后更新时间', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='好物圈订单同步记录表'; + + +# 更新权限排序 +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10335'); +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10051'); +UPDATE `yoshop_store_access` SET `sort`='115' WHERE (`access_id`='10387'); + + +# 新增好物圈管理管理 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10400', '好物圈', 'apps.wow', '10074', '110', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10401', '商品收藏', 'apps.wow.shoping', '10400', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10402', '订单信息', 'apps.wow.order', '10400', '105', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10403', '基础设置', 'apps.wow.setting/index', '10400', '110', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10404', '商品收藏记录', 'apps.wow.shoping/index', '10401', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10405', '取消同步', 'apps.wow.shoping/delete', '10401', '105', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10406', '订单同步记录', 'apps.wow.order/index', '10402', '100', '1557037952', '1557037952'); +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10407', '取消同步', 'apps.wow.order/delete', '10402', '105', '1557037952', '1557037952'); + + +# 新增用户充值权限 +INSERT INTO `yoshop_store_access` (`access_id`, `name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('10408', '用户充值', 'user/recharge', '10049', '110', '1557037952', '1557037952'); + + +# 删除冗余的权限 +DELETE FROM `yoshop_store_access` WHERE (`access_id`='10013'); +DELETE FROM `yoshop_store_access` WHERE (`access_id`='10014'); +DELETE FROM `yoshop_store_access` WHERE (`access_id`='10015'); +DELETE FROM `yoshop_store_access` WHERE (`access_id`='10016'); +DELETE FROM `yoshop_store_access` WHERE (`access_id`='10017'); + + +# 整理地区表 +UPDATE `yoshop_region` SET `name`='香港岛(废弃)' WHERE (`id`='3717'); +UPDATE `yoshop_region` SET `name`='九龙(废弃)' WHERE (`id`='3722'); +UPDATE `yoshop_region` SET `name`='新界(废弃)' WHERE (`id`='3728'); + +INSERT INTO `yoshop_region` VALUES ('3999', '3716', '香港', '香港特别行政区', '中国,香港特别行政区', '2', 'hongkong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4000', '3999', '中西区', '中西区', '中国,香港特别行政区,中西区', '3', 'zhongxin', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4001', '3999', '东区', '东区', '中国,香港特别行政区,东区', '3', 'dong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4002', '3999', '九龙城区', '九龙城区', '中国,香港特别行政区,九龙城区', '3', 'jiulong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4003', '3999', '观塘区', '观塘区', '中国,香港特别行政区,观塘区', '3', 'guantang', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4004', '3999', '南区', '南区', '中国,香港特别行政区,南区', '3', 'nan', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4005', '3999', '深水埗区', '深水埗区', '中国,香港特别行政区,深水埗区', '3', 'shenshuibu', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4006', '3999', '湾仔区', '湾仔区', '中国,香港特别行政区,湾仔区', '3', 'wanzi', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4007', '3999', '黄大仙区', '黄大仙区', '中国,香港特别行政区,黄大仙区', '3', 'huangdaxian', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4008', '3999', '油尖旺区', '油尖旺区', '中国,香港特别行政区,油尖旺区', '3', 'youjianwang', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4009', '3999', '离岛区', '离岛区', '中国,香港特别行政区,离岛区', '3', 'lidao', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4010', '3999', '葵青区', '葵青区', '中国,香港特别行政区,葵青区', '3', 'kuiqing', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4011', '3999', '北区', '北区', '中国,香港特别行政区,北区', '3', 'bei', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4012', '3999', '西贡区', '西贡区', '中国,香港特别行政区,西贡区', '3', 'xigong', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4013', '3999', '沙田区', '沙田区', '中国,香港特别行政区,沙田区', '3', 'shatian', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4014', '3999', '屯门区', '屯门区', '中国,香港特别行政区,屯门区', '3', 'tunmen', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4015', '3999', '大埔区', '大埔区', '中国,香港特别行政区,大埔区', '3', 'dapu', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4016', '3999', '荃湾区', '荃湾区', '中国,香港特别行政区,荃湾区', '3', 'quanwan', null, null, null, null, null); +INSERT INTO `yoshop_region` VALUES ('4017', '3999', '元朗区', '元朗区', '中国,香港特别行政区,元朗区', '3', 'yuanlang', null, null, null, null, null); + +ALTER TABLE `yoshop_region` AUTO_INCREMENT=50001; + +COMMIT; \ No newline at end of file diff --git a/doc/database/upgrade/v1.1.25.sql b/doc/database/upgrade/v1.1.25.sql new file mode 100644 index 0000000..9ba1089 --- /dev/null +++ b/doc/database/upgrade/v1.1.25.sql @@ -0,0 +1,168 @@ + +START TRANSACTION; + +# 修改字段:用户表 - 用户总支付的金额 +ALTER TABLE `yoshop_user` +CHANGE COLUMN `money` `pay_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '用户总支付的金额' AFTER `balance`; + +# 新增字段:用户表 - 实际消费的金额 +ALTER TABLE `yoshop_user` +ADD COLUMN `expend_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '实际消费的金额(不含退款)' AFTER `pay_money`; + +# 新增字段:用户表 - 会员等级id +ALTER TABLE `yoshop_user` +ADD COLUMN `grade_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员等级id' AFTER `expend_money`; + + + +# 新增字段:商品表 - 是否开启会员折扣 +ALTER TABLE `yoshop_goods` +ADD COLUMN `is_enable_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否开启会员折扣(1开启 0关闭)' AFTER `delivery_id`; + +# 新增字段:商品表 - 会员折扣设置 +ALTER TABLE `yoshop_goods` +ADD COLUMN `is_alone_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣设置(0默认等级折扣 1单独设置折扣)' AFTER `is_enable_grade`; + +# 新增字段:商品表 - 单独设置折扣的配置 +ALTER TABLE `yoshop_goods` +ADD COLUMN `alone_grade_equity` text NULL COMMENT '单独设置折扣的配置' AFTER `is_alone_grade`; + + +# 新增字段:商品表 - 是否开启会员折扣 +ALTER TABLE `yoshop_sharing_goods` +ADD COLUMN `is_enable_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否开启会员折扣(1开启 0关闭)' AFTER `delivery_id`; + +# 新增字段:商品表 - 会员折扣设置 +ALTER TABLE `yoshop_sharing_goods` +ADD COLUMN `is_alone_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣设置(0默认等级折扣 1单独设置折扣)' AFTER `is_enable_grade`; + +# 新增字段:商品表 - 单独设置折扣的配置 +ALTER TABLE `yoshop_sharing_goods` +ADD COLUMN `alone_grade_equity` text NULL COMMENT '单独设置折扣的配置' AFTER `is_alone_grade`; + + +# 新增字段:订单表 - 标识:累积用户实际消费金额 +ALTER TABLE `yoshop_order` +ADD COLUMN `is_user_expend` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '标识:累积用户实际消费金额' AFTER `user_id`; + +# 修改字段:订单表 - 优惠券抵扣金额 +ALTER TABLE `yoshop_order` +CHANGE COLUMN `coupon_price` `coupon_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '优惠券抵扣金额' AFTER `coupon_id`; + + +# 新增字段:拼团订单表 - 标识:累积用户实际消费金额 +ALTER TABLE `yoshop_sharing_order` +ADD COLUMN `is_user_expend` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '标识:累积用户实际消费金额' AFTER `user_id`; + +# 修改字段:拼团订单表 - 优惠券抵扣金额 +ALTER TABLE `yoshop_sharing_order` +CHANGE COLUMN `coupon_price` `coupon_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '优惠券抵扣金额' AFTER `coupon_id`; + + + + + + +# 新增字段:订单商品记录表 - 会员等级折扣金额 + 优惠券折扣金额 + +ALTER TABLE `yoshop_order` +MODIFY COLUMN `total_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '商品总金额(不含优惠折扣)' AFTER `order_no`, +ADD COLUMN `order_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '订单金额(含优惠折扣)' AFTER `total_price`; + +ALTER TABLE `yoshop_order_goods` +ADD COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员等级折扣金额' AFTER `goods_weight`, +ADD COLUMN `coupon_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠券折扣金额' AFTER `grade_total_money`; + +# 新增字段:订单商品记录表 - 是否存在会员等级折扣 +ALTER TABLE `yoshop_order_goods` +ADD COLUMN `is_user_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否存在会员等级折扣' AFTER `goods_weight`; + +# 新增字段:订单商品记录表 - 会员折扣比例(0-10) +ALTER TABLE `yoshop_order_goods` +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员等级折扣金额(总)' AFTER `is_user_grade`, +ADD COLUMN `grade_ratio` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣比例(0-10)' AFTER `is_user_grade`; + +ALTER TABLE `yoshop_order_goods` +MODIFY COLUMN `goods_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '商品价格(单价)' AFTER `goods_no`, +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员折扣总金额' AFTER `grade_ratio`, +ADD COLUMN `grade_goods_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣的商品单价' AFTER `grade_ratio`; + +ALTER TABLE `yoshop_order_goods` +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员折扣的总额差' AFTER `grade_goods_price`; + + + +# 新增字段:订单商品记录表 - 会员等级折扣金额 + 优惠券折扣金额 + +ALTER TABLE `yoshop_sharing_order` +MODIFY COLUMN `total_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '商品总金额(不含优惠折扣)' AFTER `order_no`, +ADD COLUMN `order_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '订单金额(含优惠折扣)' AFTER `total_price`; + +ALTER TABLE `yoshop_sharing_order_goods` +ADD COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员等级折扣金额' AFTER `goods_weight`, +ADD COLUMN `coupon_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '优惠券折扣金额' AFTER `grade_total_money`; + +# 新增字段:订单商品记录表 - 是否存在会员等级折扣 +ALTER TABLE `yoshop_sharing_order_goods` +ADD COLUMN `is_user_grade` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否存在会员等级折扣' AFTER `goods_weight`; + +# 新增字段:订单商品记录表 - 会员折扣比例(0-10) +ALTER TABLE `yoshop_sharing_order_goods` +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员等级折扣金额(总)' AFTER `is_user_grade`, +ADD COLUMN `grade_ratio` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣比例(0-10)' AFTER `is_user_grade`; + +ALTER TABLE `yoshop_sharing_order_goods` +MODIFY COLUMN `goods_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '商品价格(单价)' AFTER `goods_no`, +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员折扣总金额' AFTER `grade_ratio`, +ADD COLUMN `grade_goods_price` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员折扣的商品单价' AFTER `grade_ratio`; + +ALTER TABLE `yoshop_sharing_order_goods` +MODIFY COLUMN `grade_total_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '会员折扣的总额差' AFTER `grade_goods_price`; + + + + + +# 新增表:用户会员等级表 +CREATE TABLE `yoshop_user_grade` ( + `grade_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '等级ID', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '等级名称', + `weight` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '等级权重(1-9999)', + `upgrade` text NOT NULL COMMENT '升级条件', + `equity` text NOT NULL COMMENT '等级权益(折扣率0-100)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态(1启用 0禁用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`grade_id`), + KEY `wxapp_id` (`wxapp_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户会员等级表'; + + +# 新增表:用户会员等级变更记录表 +CREATE TABLE `yoshop_user_grade_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `old_grade_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '变更前的等级id', + `new_grade_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '变更后的等级id', + `change_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '变更类型(10后台管理员设置 20自动升级)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户会员等级变更记录表'; + + + +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10387'); + +INSERT INTO `yoshop_store_access` VALUES ('10411', '修改会员等级', 'user/grade', '10049', '115', '1558317213', '1558317226'); +INSERT INTO `yoshop_store_access` VALUES ('10412', '会员等级管理', 'user.grade', '10049', '120', '1558317440', '1558317440'); +INSERT INTO `yoshop_store_access` VALUES ('10413', '会员等级列表', 'user.grade/index', '10412', '100', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10414', '新增等级', 'user.grade/add', '10412', '105', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10415', '编辑等级', 'user.grade/edit', '10412', '110', '1558317464', '1558317464'); +INSERT INTO `yoshop_store_access` VALUES ('10416', '删除等级', 'user.grade/delete', '10412', '115', '1558317464', '1558317464'); + + + +COMMIT; \ No newline at end of file diff --git a/doc/database/upgrade/v1.1.26.sql b/doc/database/upgrade/v1.1.26.sql new file mode 100644 index 0000000..420e21d --- /dev/null +++ b/doc/database/upgrade/v1.1.26.sql @@ -0,0 +1,6 @@ + +START TRANSACTION; + +ALTER TABLE `yoshop_user_grade_log` ADD COLUMN `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '管理员备注' AFTER `change_type`; + +COMMIT; \ No newline at end of file diff --git a/doc/database/upgrade/v1.1.27.sql b/doc/database/upgrade/v1.1.27.sql new file mode 100644 index 0000000..8b9ee6f --- /dev/null +++ b/doc/database/upgrade/v1.1.27.sql @@ -0,0 +1,117 @@ + +START TRANSACTION; + +CREATE TABLE `yoshop_bargain_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '砍价活动id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动开始时间', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动结束时间', + `expiryt_time` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '砍价有效期(单位:小时)', + `floor_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍价底价', + `peoples` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帮砍人数', + `is_self_cut` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '可自砍一刀(0禁止 1允许)', + `is_floor_buy` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '必须底价购买(0否 1是)', + `share_title` varchar(500) NOT NULL DEFAULT '' COMMENT '分享标题', + `prompt_words` varchar(500) NOT NULL DEFAULT '' COMMENT '砍价助力语', + `actual_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动销量(实际的)', + `initial_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '虚拟销量', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序(数字越小越靠前)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '活动状态(1启用 0禁用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价活动表'; + + +CREATE TABLE `yoshop_bargain_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='砍价活动设置表'; + + +CREATE TABLE `yoshop_bargain_task` ( + `task_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '砍价任务id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价活动id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id(发起人)', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品sku标识', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品原价', + `floor_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍价底价', + `peoples` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '帮砍人数', + `cut_people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已砍人数', + `section` text NOT NULL COMMENT '砍价金额区间', + `cut_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '已砍金额', + `actual_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际购买金额', + `is_floor` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已砍到底价(0否 1是)', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '任务截止时间', + `is_buy` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否购买(0未购买 1已购买)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '任务状态 (0已结束 1砍价中)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`task_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价任务表'; + + +CREATE TABLE `yoshop_bargain_task_help` ( + `help_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价活动id', + `task_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '砍价任务id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_creater` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为发起人(0否 1是)', + `cut_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '砍掉的金额', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`help_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='砍价任务助力记录表'; + + +ALTER TABLE `yoshop_order` +ADD COLUMN `order_source` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '订单来源(10普通订单 20砍价订单)' AFTER `is_comment`; + + +ALTER TABLE `yoshop_order` +ADD COLUMN `order_source_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '来源记录id' AFTER `order_source`; + + +INSERT INTO `yoshop_store_access` VALUES ('10426', '砍价活动', 'apps.bargain', '10074', '100', '1559615418', '1559615441'); +INSERT INTO `yoshop_store_access` VALUES ('10427', '砍价活动管理', 'apps.bargain.active', '10426', '100', '1559615566', '1559615566'); +INSERT INTO `yoshop_store_access` VALUES ('10428', '砍价活动列表', 'apps.bargain.active/index', '10427', '100', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10429', '新增砍价活动', 'apps.bargain.active/add', '10427', '105', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10430', '编辑砍价活动', 'apps.bargain.active/edit', '10427', '110', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10431', '删除砍价活动', 'apps.bargain.active/delete', '10427', '115', '1559615601', '1559615601'); +INSERT INTO `yoshop_store_access` VALUES ('10432', '砍价记录', 'apps.bargain.task', '10426', '105', '1559615788', '1559615788'); +INSERT INTO `yoshop_store_access` VALUES ('10433', '砍价记录列表', 'apps.bargain.task/index', '10432', '100', '1559615815', '1559615815'); +INSERT INTO `yoshop_store_access` VALUES ('10434', '砍价助力榜', 'apps.bargain.task/help', '10432', '105', '1559615850', '1559615850'); +INSERT INTO `yoshop_store_access` VALUES ('10435', '删除砍价记录', 'apps.bargain.task/delete', '10432', '110', '1559615878', '1559615878'); +INSERT INTO `yoshop_store_access` VALUES ('10436', '砍价设置', 'apps.bargain.setting/index', '10426', '110', '1559615946', '1559615979'); + +INSERT INTO `yoshop_store_access` VALUES ('10437', '复制主商城商品', 'apps.sharing.goods/copy_master', '10306', '112', '1559615946', '1559615979'); + + + + + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10021'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10022'); +UPDATE `yoshop_store_access` SET `sort`='115' WHERE (`access_id`='10023'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10024'); +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10025'); + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10307'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10308'); +UPDATE `yoshop_store_access` SET `sort`='115' WHERE (`access_id`='10309'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10310'); +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10311'); + + + + +COMMIT; \ No newline at end of file diff --git a/doc/database/upgrade/v1.1.29.sql b/doc/database/upgrade/v1.1.29.sql new file mode 100644 index 0000000..ac71d37 --- /dev/null +++ b/doc/database/upgrade/v1.1.29.sql @@ -0,0 +1,83 @@ + +ALTER TABLE `yoshop_user` +ADD COLUMN `points` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户可用积分' AFTER `balance`; + + + +CREATE TABLE `yoshop_user_points_log` ( + `log_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `value` int(11) NOT NULL DEFAULT '0.00' COMMENT '变动数量', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述/说明', + `remark` varchar(500) NOT NULL DEFAULT '' COMMENT '管理员备注', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序商城id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`log_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='用户积分变动明细表'; + + + +ALTER TABLE `yoshop_order` +ADD COLUMN `points_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣金额' AFTER `coupon_money`; + +ALTER TABLE `yoshop_order` +ADD COLUMN `points_num` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣数量' AFTER `points_money`; + +ALTER TABLE `yoshop_order` +ADD COLUMN `points_bonus` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '赠送的积分数量' AFTER `order_status`; + +ALTER TABLE `yoshop_order` +CHANGE COLUMN `is_user_expend` `is_settled` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单是否已结算(0未结算 1已结算)' AFTER `points_bonus`; + + + +ALTER TABLE `yoshop_sharing_order` +ADD COLUMN `points_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣金额' AFTER `coupon_money`; + +ALTER TABLE `yoshop_sharing_order` +ADD COLUMN `points_num` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣数量' AFTER `points_money`; + +ALTER TABLE `yoshop_sharing_order` +ADD COLUMN `points_bonus` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '赠送的积分数量' AFTER `order_status`; + +ALTER TABLE `yoshop_sharing_order` +CHANGE COLUMN `is_user_expend` `is_settled` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单是否已结算(0未结算 1已结算)' AFTER `points_bonus`; + + + +ALTER TABLE `yoshop_goods` +ADD COLUMN `is_points_gift` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否开启积分赠送(1开启 0关闭)' AFTER `delivery_id`; + +ALTER TABLE `yoshop_goods` +ADD COLUMN `is_points_discount` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否允许使用积分抵扣(1允许 0不允许)' AFTER `is_points_gift`; + +ALTER TABLE `yoshop_order_goods` +ADD COLUMN `points_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '积分金额' AFTER `coupon_money`, +ADD COLUMN `points_num` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣数量' AFTER `points_money`, +ADD COLUMN `points_bonus` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '赠送的积分数量' AFTER `points_num`; + + + +ALTER TABLE `yoshop_sharing_goods` +ADD COLUMN `is_points_gift` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否开启积分赠送(1开启 0关闭)' AFTER `delivery_id`; + +ALTER TABLE `yoshop_sharing_goods` +ADD COLUMN `is_points_discount` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否允许使用积分抵扣(1允许 0不允许)' AFTER `is_points_gift`; + +ALTER TABLE `yoshop_sharing_order_goods` +ADD COLUMN `points_money` decimal(10,2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '积分金额' AFTER `coupon_money`, +ADD COLUMN `points_num` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '积分抵扣数量' AFTER `points_money`, +ADD COLUMN `points_bonus` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '赠送的积分数量' AFTER `points_num`; + + +UPDATE `yoshop_store_access` SET `sort`='125' WHERE (`access_id`='10370'); + + +INSERT INTO `yoshop_store_access` VALUES ('10443', '活跃用户', 'market.push/user', '10441', '105', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10442', '发送消息', 'market.push/send', '10441', '100', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10441', '消息推送', 'market.push', '10052', '120', '1561080292', '1561080292'); +INSERT INTO `yoshop_store_access` VALUES ('10440', '积分明细', 'market.points/log', '10438', '105', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10439', '积分设置', 'market.points/setting', '10438', '100', '1561080384', '1561080384'); +INSERT INTO `yoshop_store_access` VALUES ('10438', '积分管理', 'market.points', '10052', '115', '1561080292', '1561080292'); + + diff --git a/doc/database/upgrade/v1.1.3.sql b/doc/database/upgrade/v1.1.3.sql new file mode 100644 index 0000000..3950159 --- /dev/null +++ b/doc/database/upgrade/v1.1.3.sql @@ -0,0 +1,148 @@ + +# 用户记录表:新增用户总消费金额 +ALTER TABLE `yoshop_user` +ADD COLUMN `money` decimal(10,2) unsigned NOT NULL DEFAULT 0 COMMENT '用户总消费金额' AFTER `address_id`; + + +# 订单商品记录表:新增实际付款价 +ALTER TABLE `yoshop_order_goods` +MODIFY COLUMN `total_price` decimal(10,2) unsigned NOT NULL DEFAULT 0.00 COMMENT '商品总价(数量×单价)' AFTER `total_num`, +ADD COLUMN `total_pay_price` decimal(10,2) unsigned NOT NULL DEFAULT 0.00 COMMENT '实际付款价(折扣和优惠后)' AFTER `total_price`; + + +# 分销商申请记录表 +CREATE TABLE `yoshop_dealer_apply` ( + `apply_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `referee_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '推荐人用户id', + `apply_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '申请方式(10需后台审核 20无需审核)', + `apply_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '申请时间', + `apply_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '审核状态 (10待审核 20审核通过 30驳回)', + `audit_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '审核时间', + `reject_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '驳回原因', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`apply_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商申请记录表'; + + +# 分销商资金明细表 +CREATE TABLE `yoshop_dealer_capital` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `flow_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '资金流动类型 (10佣金收入 20提现支出)', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '金额', + `describe` varchar(500) NOT NULL DEFAULT '' COMMENT '描述', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商资金明细表'; + + +# 销商订单记录表 +CREATE TABLE `yoshop_dealer_order` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id (买家)', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号(废弃,勿用)', + `order_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单总金额(不含运费)', + `first_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(一级)', + `second_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(二级)', + `third_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id(三级)', + `first_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(一级)', + `second_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(二级)', + `third_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '分销佣金(三级)', + `is_settled` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已结算佣金 (0未结算 1已结算)', + `settle_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '结算时间', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `order_id` (`order_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商订单记录表'; + + +# 分销商推荐关系表 +CREATE TABLE `yoshop_dealer_referee` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `dealer_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id(被推荐人)', + `level` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '推荐关系层级(1,2,3)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `dealer_id` (`dealer_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商推荐关系表'; + + +# 分销商设置表 +CREATE TABLE `yoshop_dealer_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分销商设置表'; + + +# 分销商用户记录表 +CREATE TABLE `yoshop_dealer_user` ( + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `real_name` varchar(30) NOT NULL DEFAULT '' COMMENT '姓名', + `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '当前可提现佣金', + `freeze_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '已冻结佣金', + `total_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '累积提现佣金', + `referee_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '推荐人用户id', + `first_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(一级)', + `second_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(二级)', + `third_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成员数量(三级)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分销商用户记录表'; + + +# 分销商提现明细表 +CREATE TABLE `yoshop_dealer_withdraw` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分销商用户id', + `money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '提现金额', + `pay_type` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '打款方式 (10微信 20支付宝 30银行卡)', + `alipay_name` varchar(30) NOT NULL DEFAULT '' COMMENT '支付宝姓名', + `alipay_account` varchar(30) NOT NULL DEFAULT '' COMMENT '支付宝账号', + `bank_name` varchar(30) NOT NULL DEFAULT '' COMMENT '开户行名称', + `bank_account` varchar(30) NOT NULL DEFAULT '' COMMENT '银行开户名', + `bank_card` varchar(30) NOT NULL DEFAULT '' COMMENT '银行卡号', + `apply_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '申请状态 (10待审核 20审核通过 30驳回 40已打款)', + `audit_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '审核时间', + `reject_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '驳回原因', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='分销商提现明细表'; + + +# 小程序form_id记录表 +CREATE TABLE `yoshop_wxapp_formid` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `form_id` varchar(50) NOT NULL DEFAULT '' COMMENT '小程序form_id', + `expiry_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间', + `is_used` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已使用', + `used_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '使用时间', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='小程序form_id记录表'; \ No newline at end of file diff --git a/doc/database/upgrade/v1.1.32.sql b/doc/database/upgrade/v1.1.32.sql new file mode 100644 index 0000000..d79a9ce --- /dev/null +++ b/doc/database/upgrade/v1.1.32.sql @@ -0,0 +1,119 @@ + + + +ALTER TABLE `yoshop_order_goods` +ADD COLUMN `goods_source_id` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '来源记录id' AFTER `user_id`; + + +ALTER TABLE `yoshop_order` +MODIFY COLUMN `order_source` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '订单来源(10普通订单 20砍价订单 30秒杀订单)' AFTER `is_comment`; + + +UPDATE `yoshop_store_access` SET `sort`='105' WHERE (`access_id`='10300'); +UPDATE `yoshop_store_access` SET `sort`='110' WHERE (`access_id`='10426'); +UPDATE `yoshop_store_access` SET `sort`='120' WHERE (`access_id`='10400'); + + + +INSERT INTO `yoshop_store_access` VALUES ('10444', '整点秒杀', 'apps.sharp', '10074', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10445', '秒杀商品', 'apps.sharp.goods', '10444', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10446', '商品列表', 'apps.sharp.goods/index', '10445', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10447', '新增商品', 'apps.sharp.goods/add', '10445', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10448', '编辑商品', 'apps.sharp.goods/edit', '10445', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10449', '删除商品', 'apps.sharp.goods/delete', '10445', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10450', '活动会场', 'apps.sharp.active', '10444', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10451', '会场列表', 'apps.sharp.active/index', '10450', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10452', '新增会场', 'apps.sharp.active/add', '10450', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10453', '修改活动状态', 'apps.sharp.active/state', '10450', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10454', '删除会场', 'apps.sharp.active/delete', '10450', '115', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10455', '场次管理', 'apps.sharp.active_time', '10450', '120', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10456', '场次列表', 'apps.sharp.active_time/index', '10455', '100', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10457', '新增场次', 'apps.sharp.active_time/add', '10455', '105', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10458', '编辑场次', 'apps.sharp.active_time/edit', '10455', '110', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10459', '修改活动状态', 'apps.sharp.active_time/state', '10455', '115', '1564449650', '1564449650'); +INSERT INTO `yoshop_store_access` VALUES ('10460', '删除场次', 'apps.sharp.active_time/delete', '10455', '120', '1564449650', '1564449650'); + +INSERT INTO `yoshop_store_access` VALUES ('10461', '基础设置', 'apps.sharp.setting/index', '10444', '125', '1564449650', '1564449650'); + + + +CREATE TABLE `yoshop_sharp_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '活动会场ID', + `active_date` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动日期', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '活动状态(0禁用 1启用)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场表'; + + +CREATE TABLE `yoshop_sharp_active_goods` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动会场ID', + `active_time_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动场次ID', + `sharp_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀商品ID', + `sales_actual` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际销量', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场与商品关联表'; + + +CREATE TABLE `yoshop_sharp_active_time` ( + `active_time_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '场次ID', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '活动会场ID', + `active_time` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '场次时间(0点-23点)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '活动状态(0禁用 1启用)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_time_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-活动会场场次表'; + + +CREATE TABLE `yoshop_sharp_goods` ( + `sharp_goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '秒杀商品ID', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品ID', + `deduct_stock_type` tinyint(3) unsigned DEFAULT '10' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `limit_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '限购数量', + `seckill_stock` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品库存总量', + `total_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '累积销量', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品排序(数字越小越靠前)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '商品状态(0下架 1上架)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`sharp_goods_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-商品表'; + +CREATE TABLE `yoshop_sharp_goods_sku` ( + `goods_sku_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品规格id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '商品sku记录索引 (由规格id组成)', + `sharp_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀商品id', + `seckill_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格', + `seckill_stock` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '秒杀库存数量', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_sku_id`), + UNIQUE KEY `sku_idx` (`sharp_goods_id`,`spec_sku_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='整点秒杀-秒杀商品sku信息表'; + +CREATE TABLE `yoshop_sharp_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='整点秒杀设置表'; + + + diff --git a/doc/database/upgrade/v1.1.35.sql b/doc/database/upgrade/v1.1.35.sql new file mode 100644 index 0000000..817a5df --- /dev/null +++ b/doc/database/upgrade/v1.1.35.sql @@ -0,0 +1,66 @@ + + +UPDATE `yoshop_region` SET `name`='省直辖县级行政区划',`merger_name`='中国,湖北省,省直辖县级行政区划' WHERE (`id`='1822'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,省直辖县级行政区划,仙桃市' WHERE (`id`='1823'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,省直辖县级行政区划,潜江市' WHERE (`id`='1824'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,省直辖县级行政区划,天门市' WHERE (`id`='1825'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,省直辖县级行政区划,神农架林区' WHERE (`id`='1826'); + + + + +UPDATE `yoshop_region` SET `shortname`='县', `name`='县', `merger_name`='中国,重庆,县', `pinyin`='xian' WHERE (`id`='2363'); +UPDATE `yoshop_region` SET `id`='2325', `pid`='2324', `shortname`='万州', `name`='万州区', `merger_name`='中国,重庆,重庆市,万州区', `level`='3', `pinyin`='wanzhou', `code`='023', `zip_code`='404000', `first`='W', `lng`='108.40869', `lat`='30.80788' WHERE (`id`='2325'); +UPDATE `yoshop_region` SET `id`='2326', `pid`='2324', `shortname`='涪陵', `name`='涪陵区', `merger_name`='中国,重庆,重庆市,涪陵区', `level`='3', `pinyin`='fuling', `code`='023', `zip_code`='408000', `first`='F', `lng`='107.39007', `lat`='29.70292' WHERE (`id`='2326'); +UPDATE `yoshop_region` SET `id`='2327', `pid`='2324', `shortname`='渝中', `name`='渝中区', `merger_name`='中国,重庆,重庆市,渝中区', `level`='3', `pinyin`='yuzhong', `code`='023', `zip_code`='400010', `first`='Y', `lng`='106.56901', `lat`='29.55279' WHERE (`id`='2327'); +UPDATE `yoshop_region` SET `id`='2328', `pid`='2324', `shortname`='大渡口', `name`='大渡口区', `merger_name`='中国,重庆,重庆市,大渡口区', `level`='3', `pinyin`='dadukou', `code`='023', `zip_code`='400080', `first`='D', `lng`='106.48262', `lat`='29.48447' WHERE (`id`='2328'); +UPDATE `yoshop_region` SET `id`='2329', `pid`='2324', `shortname`='江北', `name`='江北区', `merger_name`='中国,重庆,重庆市,江北区', `level`='3', `pinyin`='jiangbei', `code`='023', `zip_code`='400020', `first`='J', `lng`='106.57434', `lat`='29.60658' WHERE (`id`='2329'); +UPDATE `yoshop_region` SET `id`='2330', `pid`='2324', `shortname`='沙坪坝', `name`='沙坪坝区', `merger_name`='中国,重庆,重庆市,沙坪坝区', `level`='3', `pinyin`='shapingba', `code`='023', `zip_code`='400030', `first`='S', `lng`='106.45752', `lat`='29.54113' WHERE (`id`='2330'); +UPDATE `yoshop_region` SET `id`='2331', `pid`='2324', `shortname`='九龙坡', `name`='九龙坡区', `merger_name`='中国,重庆,重庆市,九龙坡区', `level`='3', `pinyin`='jiulongpo', `code`='023', `zip_code`='400050', `first`='J', `lng`='106.51107', `lat`='29.50197' WHERE (`id`='2331'); +UPDATE `yoshop_region` SET `id`='2332', `pid`='2324', `shortname`='南岸', `name`='南岸区', `merger_name`='中国,重庆,重庆市,南岸区', `level`='3', `pinyin`='nan\'an', `code`='023', `zip_code`='400064', `first`='N', `lng`='106.56347', `lat`='29.52311' WHERE (`id`='2332'); +UPDATE `yoshop_region` SET `id`='2333', `pid`='2324', `shortname`='北碚', `name`='北碚区', `merger_name`='中国,重庆,重庆市,北碚区', `level`='3', `pinyin`='beibei', `code`='023', `zip_code`='400700', `first`='B', `lng`='106.39614', `lat`='29.80574' WHERE (`id`='2333'); +UPDATE `yoshop_region` SET `id`='2334', `pid`='2324', `shortname`='綦江', `name`='綦江区', `merger_name`='中国,重庆,重庆市,綦江区', `level`='3', `pinyin`='qijiang', `code`='023', `zip_code`='400800', `first`=NULL, `lng`='106.926779', `lat`='28.960656' WHERE (`id`='2334'); +UPDATE `yoshop_region` SET `id`='2335', `pid`='2324', `shortname`='大足', `name`='大足区', `merger_name`='中国,重庆,重庆市,大足区', `level`='3', `pinyin`='dazu', `code`='023', `zip_code`='400900', `first`='D', `lng`='105.768121', `lat`='29.484025' WHERE (`id`='2335'); +UPDATE `yoshop_region` SET `id`='2336', `pid`='2324', `shortname`='渝北', `name`='渝北区', `merger_name`='中国,重庆,重庆市,渝北区', `level`='3', `pinyin`='yubei', `code`='023', `zip_code`='401120', `first`='Y', `lng`='106.6307', `lat`='29.7182' WHERE (`id`='2336'); +UPDATE `yoshop_region` SET `id`='2337', `pid`='2324', `shortname`='巴南', `name`='巴南区', `merger_name`='中国,重庆,重庆市,巴南区', `level`='3', `pinyin`='banan', `code`='023', `zip_code`='401320', `first`='B', `lng`='106.52365', `lat`='29.38311' WHERE (`id`='2337'); +UPDATE `yoshop_region` SET `id`='2338', `pid`='2324', `shortname`='黔江', `name`='黔江区', `merger_name`='中国,重庆,重庆市,黔江区', `level`='3', `pinyin`='qianjiang', `code`='023', `zip_code`='409700', `first`='Q', `lng`='108.7709', `lat`='29.5332' WHERE (`id`='2338'); +UPDATE `yoshop_region` SET `id`='2339', `pid`='2324', `shortname`='长寿', `name`='长寿区', `merger_name`='中国,重庆,重庆市,长寿区', `level`='3', `pinyin`='changshou', `code`='023', `zip_code`='401220', `first`='C', `lng`='107.08166', `lat`='29.85359' WHERE (`id`='2339'); +UPDATE `yoshop_region` SET `id`='2340', `pid`='2324', `shortname`='江津', `name`='江津区', `merger_name`='中国,重庆,重庆市,江津区', `level`='3', `pinyin`='jiangjin', `code`='023', `zip_code`='402260', `first`='J', `lng`='106.25912', `lat`='29.29008' WHERE (`id`='2340'); +UPDATE `yoshop_region` SET `id`='2341', `pid`='2324', `shortname`='合川', `name`='合川区', `merger_name`='中国,重庆,重庆市,合川区', `level`='3', `pinyin`='hechuan', `code`='023', `zip_code`='401520', `first`='H', `lng`='106.27633', `lat`='29.97227' WHERE (`id`='2341'); +UPDATE `yoshop_region` SET `id`='2342', `pid`='2324', `shortname`='永川', `name`='永川区', `merger_name`='中国,重庆,重庆市,永川区', `level`='3', `pinyin`='yongchuan', `code`='023', `zip_code`='402160', `first`='Y', `lng`='105.927', `lat`='29.35593' WHERE (`id`='2342'); +UPDATE `yoshop_region` SET `id`='2343', `pid`='2324', `shortname`='南川', `name`='南川区', `merger_name`='中国,重庆,重庆市,南川区', `level`='3', `pinyin`='nanchuan', `code`='023', `zip_code`='408400', `first`='N', `lng`='107.09936', `lat`='29.15751' WHERE (`id`='2343'); +UPDATE `yoshop_region` SET `id`='2344', `pid`='2324', `shortname`='璧山', `name`='璧山区', `merger_name`='中国,重庆,重庆市,璧山区', `level`='3', `pinyin`='bishan', `code`='023', `zip_code`='402760', `first`=NULL, `lng`='106.231126', `lat`='29.593581' WHERE (`id`='2344'); +UPDATE `yoshop_region` SET `id`='2345', `pid`='2324', `shortname`='铜梁', `name`='铜梁区', `merger_name`='中国,重庆,重庆市,铜梁区', `level`='3', `pinyin`='tongliang', `code`='023', `zip_code`='402560', `first`='T', `lng`='106.054948', `lat`='29.839944' WHERE (`id`='2345'); +UPDATE `yoshop_region` SET `id`='2346', `pid`='2324', `shortname`='潼南', `name`='潼南区', `merger_name`='中国,重庆,重庆市,潼南区', `level`='3', `pinyin`='tongnan', `code`='023', `zip_code`='402660', `first`=NULL, `lng`='105.84005', `lat`='30.1912' WHERE (`id`='2346'); +UPDATE `yoshop_region` SET `id`='2347', `pid`='2324', `shortname`='荣昌', `name`='荣昌区', `merger_name`='中国,重庆,重庆市,荣昌区', `level`='3', `pinyin`='rongchang', `code`='023', `zip_code`='402460', `first`='R', `lng`='105.59442', `lat`='29.40488' WHERE (`id`='2347'); +UPDATE `yoshop_region` SET `id`='2348', `pid`='2324', `shortname`='梁平', `name`='梁平区', `merger_name`='中国,重庆,重庆市,梁平区', `level`='3', `pinyin`='liangping', `code`='023', `zip_code`='405200', `first`='L', `lng`='107.79998', `lat`='30.67545' WHERE (`id`='2348'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='城口', `name`='城口县', `merger_name`='中国,重庆,重庆市,城口县', `level`='3', `pinyin`='chengkou', `code`='023', `zip_code`='405900', `first`='C', `lng`='108.66513', `lat`='31.94801' WHERE (`id`='2349'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='丰都', `name`='丰都县', `merger_name`='中国,重庆,重庆市,丰都县', `level`='3', `pinyin`='fengdu', `code`='023', `zip_code`='408200', `first`='F', `lng`='107.73098', `lat`='29.86348' WHERE (`id`='2350'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='垫江', `name`='垫江县', `merger_name`='中国,重庆,重庆市,垫江县', `level`='3', `pinyin`='dianjiang', `code`='023', `zip_code`='408300', `first`='D', `lng`='107.35446', `lat`='30.33359' WHERE (`id`='2351'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='武隆', `name`='武隆县', `merger_name`='中国,重庆,重庆市,武隆县', `level`='3', `pinyin`='wulong', `code`='023', `zip_code`='408500', `first`='W', `lng`='107.7601', `lat`='29.32548' WHERE (`id`='2352'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='忠县', `name`='忠县', `merger_name`='中国,重庆,重庆市,忠县', `level`='3', `pinyin`='zhongxian', `code`='023', `zip_code`='404300', `first`='Z', `lng`='108.03689', `lat`='30.28898' WHERE (`id`='2353'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='开县', `name`='开县', `merger_name`='中国,重庆,重庆市,开县', `level`='3', `pinyin`='kaixian', `code`='023', `zip_code`='405400', `first`='K', `lng`='108.39306', `lat`='31.16095' WHERE (`id`='2354'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='云阳', `name`='云阳县', `merger_name`='中国,重庆,重庆市,云阳县', `level`='3', `pinyin`='yunyang', `code`='023', `zip_code`='404500', `first`='Y', `lng`='108.69726', `lat`='30.93062' WHERE (`id`='2355'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='奉节', `name`='奉节县', `merger_name`='中国,重庆,重庆市,奉节县', `level`='3', `pinyin`='fengjie', `code`='023', `zip_code`='404600', `first`='F', `lng`='109.46478', `lat`='31.01825' WHERE (`id`='2356'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='巫山', `name`='巫山县', `merger_name`='中国,重庆,重庆市,巫山县', `level`='3', `pinyin`='wushan', `code`='023', `zip_code`='404700', `first`='W', `lng`='109.87814', `lat`='31.07458' WHERE (`id`='2357'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='巫溪', `name`='巫溪县', `merger_name`='中国,重庆,重庆市,巫溪县', `level`='3', `pinyin`='wuxi', `code`='023', `zip_code`='405800', `first`='W', `lng`='109.63128', `lat`='31.39756' WHERE (`id`='2358'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='石柱', `name`='石柱土家族自治县', `merger_name`='中国,重庆,重庆市,石柱土家族自治县', `level`='3', `pinyin`='shizhu', `code`='023', `zip_code`='409100', `first`='S', `lng`='108.11389', `lat`='30.00054' WHERE (`id`='2359'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='秀山', `name`='秀山土家族苗族自治县', `merger_name`='中国,重庆,重庆市,秀山土家族苗族自治县', `level`='3', `pinyin`='xiushan', `code`='023', `zip_code`='409900', `first`='X', `lng`='108.98861', `lat`='28.45062' WHERE (`id`='2360'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='酉阳', `name`='酉阳土家族苗族自治县', `merger_name`='中国,重庆,重庆市,酉阳土家族苗族自治县', `level`='3', `pinyin`='youyang', `code`='023', `zip_code`='409800', `first`='Y', `lng`='108.77212', `lat`='28.8446' WHERE (`id`='2361'); +UPDATE `yoshop_region` SET `pid`='2363', `shortname`='彭水', `name`='彭水苗族土家族自治县', `merger_name`='中国,重庆,重庆市,彭水苗族土家族自治县', `level`='3', `pinyin`='pengshui', `code`='023', `zip_code`='409600', `first`='P', `lng`='108.16638', `lat`='29.29516' WHERE (`id`='2362'); +DELETE FROM `yoshop_region` WHERE (`id`='2364'); +DELETE FROM `yoshop_region` WHERE (`id`='2365'); +DELETE FROM `yoshop_region` WHERE (`id`='2366'); + + + + +ALTER TABLE `yoshop_goods_sku` +MODIFY COLUMN `goods_sales` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '商品销量(废弃)' AFTER `stock_num`; + +ALTER TABLE `yoshop_sharing_goods_sku` +MODIFY COLUMN `goods_sales` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '商品销量(废弃)' AFTER `stock_num`; + + + + diff --git a/doc/database/upgrade/v1.1.36.sql b/doc/database/upgrade/v1.1.36.sql new file mode 100644 index 0000000..3947674 --- /dev/null +++ b/doc/database/upgrade/v1.1.36.sql @@ -0,0 +1,15 @@ + + + +UPDATE `yoshop_region` SET `name`='直辖县级',`merger_name`='中国,湖北省,直辖县级' WHERE (`id`='1822'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,直辖县级,仙桃市' WHERE (`id`='1823'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,直辖县级,潜江市' WHERE (`id`='1824'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,直辖县级,天门市' WHERE (`id`='1825'); +UPDATE `yoshop_region` SET `merger_name`='中国,湖北省,直辖县级,神农架林区' WHERE (`id`='1826'); + + +UPDATE `yoshop_region` SET `name`='吐鲁番市', `merger_name`='中国,新疆维吾尔自治区,吐鲁番市' WHERE (`id`='3221'); +UPDATE `yoshop_region` SET `name`='哈密市', `merger_name`='中国,新疆维吾尔自治区,哈密市' WHERE (`id`='3225'); + + + diff --git a/doc/database/upgrade/v1.1.37.sql b/doc/database/upgrade/v1.1.37.sql new file mode 100644 index 0000000..231a108 --- /dev/null +++ b/doc/database/upgrade/v1.1.37.sql @@ -0,0 +1,4 @@ + + + +INSERT INTO `yoshop_store_access` VALUES ('11003', '数据统计', 'statistics.data/index', '0', '138', '1572507520', '1572507520'); diff --git a/doc/database/upgrade/v1.1.4.sql b/doc/database/upgrade/v1.1.4.sql new file mode 100644 index 0000000..03291f7 --- /dev/null +++ b/doc/database/upgrade/v1.1.4.sql @@ -0,0 +1,5 @@ + +# 用户记录表:新增伪删除字段 +ALTER TABLE `yoshop_user` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `money`; + diff --git a/doc/database/upgrade/v1.1.6.sql b/doc/database/upgrade/v1.1.6.sql new file mode 100644 index 0000000..c66bbca --- /dev/null +++ b/doc/database/upgrade/v1.1.6.sql @@ -0,0 +1,219 @@ + +# 微信小程序记录表:删除冗余字段 +ALTER TABLE `yoshop_wxapp` +DROP COLUMN `app_name`, +DROP COLUMN `is_service`, +DROP COLUMN `service_image_id`, +DROP COLUMN `is_phone`, +DROP COLUMN `phone_no`, +DROP COLUMN `phone_image_id`; + + +# 微信小程序记录表:新增是否回收字段 +ALTER TABLE `yoshop_wxapp` +ADD COLUMN `is_recycle` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否回收' AFTER `apikey`; + + +# 微信小程序记录表:新增伪删除字段 +ALTER TABLE `yoshop_wxapp` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `is_recycle`; + + +# 超管用户记录表 +DROP TABLE IF EXISTS `yoshop_admin_user`; +CREATE TABLE `yoshop_admin_user` ( + `admin_user_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `user_name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名', + `password` varchar(255) NOT NULL DEFAULT '' COMMENT '登录密码', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`admin_user_id`), + KEY `user_name` (`user_name`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='超管用户记录表'; + +INSERT INTO `yoshop_admin_user` (`user_name`, `password`, `create_time`, `update_time`) VALUES ('admin', '9ae7b2e6f25c907a1fc81b503b16e25f', '1529926348', '1540194026'); + + +# 商家用户记录表:新增是否为超级管理员字段 +ALTER TABLE `yoshop_store_user` +ADD COLUMN `is_super` tinyint(3) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否为超级管理员' AFTER `password`; + + +# 商家用户记录表:新增伪删除字段 +ALTER TABLE `yoshop_store_user` +ADD COLUMN `is_delete` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除' AFTER `is_super`; + + +# 商家用户记录表:新增姓名字段 +ALTER TABLE `yoshop_store_user` +ADD COLUMN `real_name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名' AFTER `password`; + + +# 商家用户记录表:设置默认姓名 +UPDATE `yoshop_store_user` SET `real_name` = '管理员' WHERE 1; + + +# 商家用户记录表:删除用户名唯一索引 +ALTER TABLE `yoshop_store_user` +DROP INDEX `user_name` , +ADD INDEX `user_name` (`user_name`); + + +# 商家用户权限表 +CREATE TABLE `yoshop_store_access` ( + `access_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT '权限名称', + `url` varchar(255) NOT NULL DEFAULT '' COMMENT '权限url', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级id', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '100' COMMENT '排序(数字越小越靠前)', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`access_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10108 DEFAULT CHARSET=utf8 COMMENT='商家用户权限表'; + + +INSERT INTO `yoshop_store_access` VALUES ('10001', '首页', 'index/index', '0', '100', '1540628721', '1540781975'); +INSERT INTO `yoshop_store_access` VALUES ('10002', '管理员', 'store', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10003', '管理员管理', 'store.user', '10002', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10004', '管理员列表', 'store.user/index', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10005', '添加管理员', 'store.user/add', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10006', '编辑管理员', 'store.user/edit', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10007', '删除管理员', 'store.user/delete', '10003', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10008', '角色管理', 'store.role', '10002', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10009', '角色列表', 'store.role/index', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10010', '添加角色', 'store.role/add', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10011', '编辑角色', 'store.role/edit', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10012', '删除角色', 'store.role/delete', '10008', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10013', '权限管理', 'store.access', '10002', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10014', '权限列表', 'store.access/index', '10013', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10015', '添加权限', 'store.access/add', '10013', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10016', '编辑权限', 'store.access/edit', '10013', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10017', '删除权限', 'store.access/delete', '10013', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10018', '商品管理', 'goods', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10019', '商品管理', 'goods', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10020', '商品列表', 'goods/index', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10021', '添加商品', 'goods/add', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10022', '编辑商品', 'goods/edit', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10023', '复制商品', 'goods/copy', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10024', '删除商品', 'goods/delete', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10025', '商品上下架', 'goods/state', '10019', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10026', '商品分类', 'goods.category', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10027', '分类列表', 'goods.category/index', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10028', '添加分类', 'goods.category/add', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10029', '编辑分类', 'goods.category/edit', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10030', '删除分类', 'goods.category/delete', '10026', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10031', '商品评价', 'goods.comment', '10018', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10032', '评价列表', 'goods.comment/index', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10033', '评价详情', 'goods.comment/detail', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10034', '删除评价', 'goods.comment/delete', '10031', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10035', '订单管理', 'order', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10036', '订单列表', '', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10037', '待发货', 'order/delivery_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10038', '待收货', 'order/receipt_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10039', '待付款', 'order/pay_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10040', '已完成', 'order/complete_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10041', '已取消', 'order/cancel_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10042', '全部订单', 'order/all_list', '10036', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10043', '订单详情', '', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10044', '详情信息', 'order/detail', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10045', '确认发货', 'order/delivery', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10046', '修改订单价格', 'order/updateprice', '10043', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10047', '订单导出', 'order.operate/export', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10048', '批量发货', 'order.operate/batchdelivery', '10035', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10049', '用户管理', 'user', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10050', '用户列表', 'user/index', '10049', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10051', '删除用户', 'user/delete', '10049', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10052', '营销设置', 'market', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10053', '优惠券', 'coupon', '10052', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10054', '优惠券列表', 'market.coupon/index', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10055', '新增优惠券', 'market.coupon/add', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10056', '编辑优惠券', 'market.coupon/edit', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10057', '删除优惠券', 'market.coupon/delete', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10058', '领取记录', 'market.coupon/receive', '10053', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10059', '小程序', 'wxapp', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10060', '小程序设置', 'wxapp/setting', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10061', '页面管理', 'wxapp.page', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10062', '页面设计', '', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10063', '页面列表', 'wxapp.page/index', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10064', '新增页面', 'wxapp.page/add', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10065', '编辑页面', 'wxapp.page/edit', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10066', '设为首页', 'wxapp.page/sethome', '10062', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10067', '分类页模板', 'wxapp.page/category', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10068', '页面链接', 'wxapp.page/links', '10061', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10069', '帮助中心', 'wxapp.help', '10059', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10070', '帮助列表', 'wxapp.help/index', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10071', '新增帮助', 'wxapp.help/add', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10072', '编辑帮助', 'wxapp.help/edit', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10073', '删除帮助', 'wxapp.help/delete', '10069', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10074', '应用中心', 'apps', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10075', '分销中心', 'apps.dealer', '10074', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10076', '入驻申请', 'apps.dealer.apply', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10077', '申请列表', 'apps.dealer.apply/index', '10076', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10078', '分销商审核', 'apps.dealer.apply/submit', '10076', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10079', '分销商用户', 'apps.dealer.user', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10080', '分销商列表', 'apps.dealer.user/index', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10081', '删除分销商', 'apps.dealer.user/delete', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10082', '分销商二维码', 'apps.dealer.user/qrcode', '10079', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10083', '分销订单', 'apps.dealer.order/index', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10084', '提现申请', 'apps.dealer.withdraw', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10085', '申请列表', 'apps.dealer.withdraw/index', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10086', '提现审核', 'apps.dealer.withdraw/submit', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10087', '确认打款', 'apps.dealer.withdraw/money', '10084', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10088', '分销设置', 'apps.dealer.setting/index', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10089', '分销海报', 'apps.dealer.setting/qrcode', '10075', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10090', '设置', 'setting', '0', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10091', '商城设置', 'setting/store', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10092', '交易设置', 'setting/trade', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10093', '配送设置', 'setting.delivery', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10094', '运费模板列表', 'setting.delivery/index', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10095', '新增运费模板', 'setting.delivery/add', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10096', '编辑运费模板', 'setting.delivery/edit', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10097', '删除运费模板', 'setting.delivery/delete', '10093', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10098', '物流公司', 'setting.express', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10099', '物流公司列表', 'setting.express/index', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10100', '新增物流公司', 'setting.express/add', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10101', '编辑物流公司', 'setting.express/edit', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10102', '删除物流公司', 'setting.express/delete', '10098', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10103', '短信通知', 'setting/sms', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10104', '模板消息', 'setting/tplmsg', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10105', '上传设置', 'setting/storage', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10106', '其他', '', '10090', '100', '1540628721', '1540628721'); +INSERT INTO `yoshop_store_access` VALUES ('10107', '清理缓存', 'setting.cache/clear', '10106', '100', '1540628721', '1540628721'); + + +# 商家用户角色表 +CREATE TABLE `yoshop_store_role` ( + `role_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '角色id', + `role_name` varchar(50) NOT NULL DEFAULT '' COMMENT '角色名称', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父级角色id', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '100' COMMENT '排序(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`role_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色表'; + + +# 商家用户角色权限关系表 +CREATE TABLE `yoshop_store_role_access` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色id', + `access_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '权限id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `role_id` (`role_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色权限关系表'; + + +# 商家用户角色记录表 +CREATE TABLE `yoshop_store_user_role` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `store_user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '超管用户id', + `role_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '角色id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `admin_user_id` (`store_user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商家用户角色记录表'; + diff --git a/doc/database/upgrade/v1.1.7.sql b/doc/database/upgrade/v1.1.7.sql new file mode 100644 index 0000000..ce2d626 --- /dev/null +++ b/doc/database/upgrade/v1.1.7.sql @@ -0,0 +1,83 @@ + + +# 微信小程序记录表:新增证书文件字段 +ALTER TABLE `yoshop_wxapp` +ADD COLUMN `cert_pem` longtext COMMENT '证书文件cert' AFTER `apikey`, +ADD COLUMN `key_pem` longtext COMMENT '证书文件key' AFTER `cert_pem`; + + +# 订单记录表:订单状态 新增21待取消状态 +ALTER TABLE `yoshop_order` +MODIFY COLUMN `order_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '订单状态(10进行中 20取消 21待取消 30已完成)' AFTER `receipt_time`; + + +# 退货地址记录表 +CREATE TABLE `yoshop_return_address` ( + `address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '退货地址id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序 (数字越小越靠前)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`address_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='退货地址记录表'; + + +# 售后单记录表 +CREATE TABLE `yoshop_order_refund` ( + `order_refund_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '售后单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后类型(10退货退款 20换货)', + `apply_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '用户申请原因(说明)', + `is_agree` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家审核状态(0待审核 10已同意 20已拒绝)', + `refuse_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '商家拒绝原因(说明)', + `refund_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际退款金额', + `is_user_send` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '用户是否发货(0未发货 1已发货)', + `send_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户发货时间', + `express_id` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流公司id', + `express_no` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流单号', + `is_receipt` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家收货状态(0未收货 1已收货)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后单状态(0进行中 10已拒绝 20已完成 30已取消)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_refund_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单记录表'; + + +# 售后单退货地址记录表 +CREATE TABLE `yoshop_order_refund_address` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单退货地址记录表'; + + +# 售后单图片记录表 +CREATE TABLE `yoshop_order_refund_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='售后单图片记录表'; + + +# 新增权限记录 +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('审核用户取消申请', 'order.operate/confirmcancel', '10043', '100', '1542163260', '1542163260'); +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('售后管理', 'order.refund', '10035', '100', '1542161684', '1542161684'); +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('售后列表', 'order.refund/index', '10108', '100', '1542161714', '1542161714'); +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('售后详情', 'order.refund/detail', '10108', '100', '1542161736', '1542161736'); +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('审核售后单', 'order.refund/audit', '10108', '100', '1542162196', '1542162196'); +INSERT INTO `yoshop_store_access` (`name`, `url`, `parent_id`, `sort`, `create_time`, `update_time`) VALUES ('确认收货并退款', 'order.refund/receipt', '10108', '100', '1542162231', '1542162231'); diff --git a/doc/database/upgrade/v1.1.8.sql b/doc/database/upgrade/v1.1.8.sql new file mode 100644 index 0000000..898aa65 --- /dev/null +++ b/doc/database/upgrade/v1.1.8.sql @@ -0,0 +1,11 @@ + +# 商品规格表:规格图片 +ALTER TABLE `yoshop_goods_sku` +ADD COLUMN `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格图片id' AFTER `spec_sku_id`; + + +# 用户收货地址表:新市辖区 +ALTER TABLE `yoshop_user_address` +ADD COLUMN `district` varchar(255) NULL DEFAULT '' COMMENT '新市辖区(该字段用于记录region表中没有的市辖区)' AFTER `region_id`; + + diff --git a/doc/database/upgrade/v1.1.9.sql b/doc/database/upgrade/v1.1.9.sql new file mode 100644 index 0000000..ad4bbe4 --- /dev/null +++ b/doc/database/upgrade/v1.1.9.sql @@ -0,0 +1,370 @@ + +#商品记录表:新增商品卖点字段 +ALTER TABLE `yoshop_goods` +ADD COLUMN `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点' AFTER `category_id`; + + +# 小程序prepay_id记录表:新增订单类型 +ALTER TABLE `yoshop_wxapp_prepay_id` +ADD COLUMN `order_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '订单类型(10商城订单 20拼团订单)' AFTER `order_id`; + + +# 分销商订单记录表:新增订单类型 +ALTER TABLE `yoshop_dealer_order` +ADD COLUMN `order_type` TINYINT (3) UNSIGNED NOT NULL DEFAULT '10' COMMENT '订单类型(10商城订单 20拼团订单)' AFTER `order_id`; + + +# 新增:拼团拼单记录表 +CREATE TABLE `yoshop_sharing_active` ( + `active_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '拼单id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `actual_people` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '当前已拼人数', + `creator_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '团长用户id', + `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单结束时间', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '拼单状态(0未拼单 10拼单中 20拼单成功 30拼单失败)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`active_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团拼单记录表'; + + +# 新增:拼团拼单成员记录表 +CREATE TABLE `yoshop_sharing_active_users` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `is_creator` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为创建者', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团拼单成员记录表'; + + +# 新增:拼团商品分类表 +CREATE TABLE `yoshop_sharing_category` ( + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品分类id', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称', + `parent_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '上级分类id', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分类图片id', + `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品分类表'; + + +# 新增:拼团商品评价表 +CREATE TABLE `yoshop_sharing_comment` ( + `comment_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '评价id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `score` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '评分(10好评 20中评 30差评)', + `content` text NOT NULL COMMENT '评价内容', + `is_picture` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为图片评价', + `sort` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '评价排序', + `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态(0隐藏 1显示)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `is_delete` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '软删除', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`comment_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品评价表'; + + +# 新增:拼团评价图片记录表 +CREATE TABLE `yoshop_sharing_comment_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `comment_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '评价id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团评价图片记录表'; + + +# 新增:拼团商品记录表 +CREATE TABLE `yoshop_sharing_goods` ( + `goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '拼团商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品分类id', + `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点', + `people` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `group_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团有效时间(单位:小时)', + `is_alone` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否允许单买(0不允许 1允许)', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格(10单规格 20多规格)', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `content` longtext NOT NULL COMMENT '商品详情', + `sales_initial` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '初始销量', + `sales_actual` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '实际销量', + `goods_sort` int(11) unsigned NOT NULL DEFAULT '100' COMMENT '商品排序(数字越小越靠前)', + `delivery_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '配送模板id', + `goods_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '商品状态(10上架 20下架)', + `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_id`), + KEY `category_id` (`category_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品记录表'; + + +# 新增:商品图片记录表 +CREATE TABLE `yoshop_sharing_goods_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `image_id` int(11) NOT NULL COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='商品图片记录表'; + + +# 新增:拼团商品规格表 +CREATE TABLE `yoshop_sharing_goods_sku` ( + `goods_sku_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '商品规格id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '0' COMMENT '商品sku记录索引(由规格id组成)', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格图片id', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `sharing_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '拼团价格', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格(单买价)', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `stock_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '当前库存数量', + `goods_sales` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品销量', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`goods_sku_id`), + UNIQUE KEY `sku_idx` (`goods_id`,`spec_sku_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品规格表'; + + +# 新增:拼团商品与规格值关系记录表 +CREATE TABLE `yoshop_sharing_goods_spec_rel` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id', + `spec_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格组id', + `spec_value_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '规格值id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团商品与规格值关系记录表'; + + +# 新增:拼团订单记录表 +CREATE TABLE `yoshop_sharing_order` ( + `order_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单id', + `order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号', + `order_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '订单类型(10单独购买 20拼团)', + `active_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼单id', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单金额(不含运费)', + `coupon_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '优惠券id', + `coupon_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券抵扣金额', + `pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款金额(包含运费、优惠)', + `update_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '后台修改的订单金额(差价)', + `buyer_remark` varchar(255) NOT NULL DEFAULT '' COMMENT '买家留言', + `pay_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '付款状态(10未付款 20已付款)', + `pay_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间', + `express_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '运费金额', + `express_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '物流公司id', + `express_company` varchar(50) NOT NULL DEFAULT '' COMMENT '物流公司', + `express_no` varchar(50) NOT NULL DEFAULT '' COMMENT '物流单号', + `delivery_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '发货状态(10未发货 20已发货)', + `delivery_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '发货时间', + `receipt_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '收货状态(10未收货 20已收货)', + `receipt_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '收货时间', + `order_status` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '订单状态(10进行中 20已取消 21待取消 30已完成)', + `is_refund` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '拼团未成功退款(0未退款 1已退款)', + `transaction_id` varchar(30) NOT NULL DEFAULT '' COMMENT '微信支付交易号', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_id`), + UNIQUE KEY `order_no` (`order_no`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团订单记录表'; + + +# 新增:拼团订单收货地址记录表 +CREATE TABLE `yoshop_sharing_order_address` ( + `order_address_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '地址id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `province_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在省份id', + `city_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在城市id', + `region_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '所在区id', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_address_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团订单收货地址记录表'; + + +# 新增:拼团订单商品记录表 +CREATE TABLE `yoshop_sharing_order_goods` ( + `order_goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团商品id', + `goods_name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', + `image_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品封面图id', + `selling_point` varchar(500) NOT NULL DEFAULT '' COMMENT '商品卖点', + `people` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '成团人数', + `group_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '成团有效时间(单位:小时)', + `is_alone` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否允许单买(0不允许 1允许)', + `deduct_stock_type` tinyint(3) unsigned NOT NULL DEFAULT '20' COMMENT '库存计算方式(10下单减库存 20付款减库存)', + `spec_type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '规格类型(10单规格 20多规格)', + `spec_sku_id` varchar(255) NOT NULL DEFAULT '' COMMENT '商品sku标识', + `goods_sku_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品规格id', + `goods_attr` varchar(500) NOT NULL DEFAULT '' COMMENT '商品规格信息', + `content` longtext NOT NULL COMMENT '商品详情', + `goods_no` varchar(100) NOT NULL DEFAULT '' COMMENT '商品编码', + `goods_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格', + `line_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品划线价', + `goods_weight` double unsigned NOT NULL DEFAULT '0' COMMENT '商品重量(Kg)', + `total_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '购买数量', + `total_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品总价(数量×单价)', + `total_pay_price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际付款价(包含优惠、折扣)', + `is_comment` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '是否已评价(0否 1是)', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`order_goods_id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团订单商品记录表'; + + +# 新增:拼团售后单记录表 +CREATE TABLE `yoshop_sharing_order_refund` ( + `order_refund_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '售后单id', + `order_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '拼团订单id', + `order_goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单商品id', + `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', + `type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后类型(10退货退款 20换货)', + `apply_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '用户申请原因(说明)', + `is_agree` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家审核状态(0待审核 10已同意 20已拒绝)', + `refuse_desc` varchar(1000) NOT NULL DEFAULT '' COMMENT '商家拒绝原因(说明)', + `refund_money` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '实际退款金额', + `is_user_send` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '用户是否发货(0未发货 1已发货)', + `send_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户发货时间', + `express_id` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流公司id', + `express_no` varchar(32) NOT NULL DEFAULT '' COMMENT '用户发货物流单号', + `is_receipt` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '商家收货状态(0未收货 1已收货)', + `status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '售后单状态(0进行中 10已拒绝 20已完成 30已取消)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + PRIMARY KEY (`order_refund_id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单记录表'; + + +# 新增:拼团售后单退货地址记录表 +CREATE TABLE `yoshop_sharing_order_refund_address` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `name` varchar(30) NOT NULL DEFAULT '' COMMENT '收货人姓名', + `phone` varchar(20) NOT NULL DEFAULT '' COMMENT '联系电话', + `detail` varchar(255) NOT NULL DEFAULT '' COMMENT '详细地址', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单退货地址记录表'; + + +# 新增:拼团售后单图片记录表 +CREATE TABLE `yoshop_sharing_order_refund_image` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', + `order_refund_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '售后单id', + `image_id` int(11) NOT NULL DEFAULT '0' COMMENT '图片id(关联文件记录表)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='拼团售后单图片记录表'; + + +# 新增:拼团设置表 +CREATE TABLE `yoshop_sharing_setting` ( + `key` varchar(30) NOT NULL DEFAULT '' COMMENT '设置项标示', + `describe` varchar(255) NOT NULL DEFAULT '' COMMENT '设置项描述', + `values` mediumtext NOT NULL COMMENT '设置内容(json格式)', + `wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id', + `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', + UNIQUE KEY `unique_key` (`key`,`wxapp_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='拼团设置表'; + + + +# 拼团管理 +INSERT INTO `yoshop_store_access` VALUES ('10300', '拼团管理', 'apps.sharing', '10074', '100', '1544601161', '1544601161'); + + +# 商品分类 +INSERT INTO `yoshop_store_access` VALUES ('10301', '商品分类', 'apps.sharing.category', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10302', '分类列表', 'apps.sharing.category/index', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10303', '添加分类', 'apps.sharing.category/add', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10304', '编辑分类', 'apps.sharing.category/edit', '10301', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10305', '删除分类', 'apps.sharing.category/delete', '10301', '100', '1544601378', '1544601378'); + + +# 拼团商品管理 +INSERT INTO `yoshop_store_access` VALUES ('10306', '商品管理', 'apps.sharing.goods', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10307', '商品列表', 'apps.sharing.goods/index', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10308', '添加商品', 'apps.sharing.goods/add', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10309', '编辑商品', 'apps.sharing.goods/edit', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10310', '复制商品', 'apps.sharing.goods/copy', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10311', '删除商品', 'apps.sharing.goods/delete', '10306', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10312', '商品上下架', 'apps.sharing.goods/state', '10306', '100', '1544601378', '1544601378'); + + +# 拼单管理 +INSERT INTO `yoshop_store_access` VALUES ('10313', '拼单管理', 'apps.sharing.active', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10314', '拼单列表', 'apps.sharing.active/index', '10313', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10315', '拼单成员列表', 'apps.sharing.active/users', '10313', '100', '1544601378', '1544601378'); + + +# 拼团订单 +INSERT INTO `yoshop_store_access` VALUES ('10316', '订单管理', 'apps.sharing.order', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10317', '订单列表', 'apps.sharing.order/index', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10318', '订单详情', '', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10319', '详情信息', 'apps.sharing.order/detail', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10320', '确认发货', 'apps.sharing.order/delivery', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10321', '修改订单价格', 'apps.sharing.order/updateprice', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10322', '审核用户取消订单', 'apps.sharing.order/confirmcancel', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10323', '拼团失败手动退款', 'apps.sharing.order/refund', '10318', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10324', '订单导出', 'apps.sharing.order.operate/export', '10316', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10325', '批量发货', 'apps.sharing.order.operate/batchdelivery', '10316', '100', '1544601378', '1544601378'); + + +# 售后管理 +INSERT INTO `yoshop_store_access` VALUES ('10326', '售后管理', 'apps.sharing.order.refund', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10327', '售后列表', 'apps.sharing.order.refund/index', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10328', '售后详情', 'apps.sharing.order.refund/detail', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10329', '审核售后单', 'apps.sharing.order.refund/audit', '10326', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10330', '确认收货并退款', 'apps.sharing.order.refund/receipt', '10326', '100', '1544601378', '1544601378'); + + +# 商品评价 +INSERT INTO `yoshop_store_access` VALUES ('10331', '商品评价', 'apps.sharing.comment', '10300', '100', '1544601378', '1544601378'); + +INSERT INTO `yoshop_store_access` VALUES ('10332', '评价列表', 'apps.sharing.comment/index', '10331', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10333', '评价详情', 'apps.sharing.comment/detail', '10331', '100', '1544601378', '1544601378'); +INSERT INTO `yoshop_store_access` VALUES ('10334', '删除评价', 'apps.sharing.comment/delete', '10331', '100', '1544601378', '1544601378'); + + +# 拼团设置 +INSERT INTO `yoshop_store_access` VALUES ('10335', '拼团设置', 'apps.sharing.setting/index', '10300', '100', '1544601378', '1544601378'); + + diff --git a/doc/更新日志.txt b/doc/更新日志.txt new file mode 100644 index 0000000..6baa1aa --- /dev/null +++ b/doc/更新日志.txt @@ -0,0 +1,598 @@ + +### v1.1.38 更新日志 ### + +修复:订单确认页收货地址提示 +修复:后台拼团商品列表状态筛选 +修复:秒杀订单并发库存问题 +修复:后台编辑门店后坐标不准确 +新增:后台分销中心编辑分销商用户 +优化:删除分销商清空推荐关系 +优化:删除用户时删除上级推荐关系 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.37 更新日志 ### + +优化:下单运费模板计算方式 +优化:已付款拼团订单不允许取消 +优化:后台砍价活动时间选择 +优化:权限管理新增数据统计项 +新增:首页DIY公众号关注组件 +修复:用户充值自动匹配套餐 +修复:后台新增管理员提示已存在 +修复:图片库列表不显示回收站的文件 +修复:订单详情页不显示详细地址 +修复:秒杀会场页进入商品报错 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.36 更新日志 ### + +优化:订单发货后即可申请售后 +优化:图片库删除图片进入回收站 +新增:后台数据统计模块 +修复:删除会员等级提示存在会员 +修复:小程序端 下单提示收货地址不存在 +修复:小程序端 秒杀商品详情页价格联动 +修复:小程序端 订单确认页has_error问题 +修复:小程序端 拼团拼单页暂无现货按钮样式 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.35 更新日志 ### + +优化:调整新授权登录方式 +修复:后台门店坐标无法选择 +修复:砍价会场页面报错(个别环境) +修复:不存在的收货城市不允许下单 +修复:验证运费模板是否被商品使用 +修复:复制参与活动的商品规格问题 +修复:小程序端 文章详情页空格不解析 +修复:小程序端 diy秒杀组件报错 +修复:小程序端 秒杀商品页倒计时错误 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.34 更新日志 ### + +优化:后台订单导出显示自提联系人 +修复:后台门店坐标地图加载无效 +修复:拼团订单结算错误 +修复:秒杀进度数字精确度问题 +修复:砍价商品正在砍价人数不正确 +修复:砍价任务并发问题 +修复:小程序端图片橱窗组样式错误 +修复:小程序端富文本空格不生效 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.33 更新日志 ### + +修复:砍价主商品下架详情页错误 +修复:php7环境秒杀首页报错 +修复:秒杀商品商品海报错误 +优化:余额支付订单支持小程序模板消息 +修复:购买产品成为分销商报错 +新增:diy组件-秒杀商品组 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.32 更新日志 ### + +优化:实际消费金额规则判断存在售后 +优化:订单存在退款的商品不赠送积分 +新增:新增营销模块整点秒杀 +修复:超管后台商城列表不显示分页 +修复:后台会员管理操作权限不正确 +修复:收货地址不在配送区报错 +修复:取消订单恢复sku库存的bug +修复:后台更新管理员时检测用户名重复 +修复:小程序端 更新收货地址时提示失败 +修复:小程序端 砍价商品详情页商品评价数量不正确 +修复:小程序端 门店详情页转发链接错误 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.31 更新日志 ### + +修复:用户可用积分不足无法抵扣问题 +修复:后台编辑商品compact方法报错 +修复:拼团订单新增分销记录类型错误 +新增:订单导出显示优惠抵扣后台改价等信息 +新增:小票打印显示上门自提点及联系信息 +优化:小程序端 砍价按钮重复点击问题 +优化:小程序端 商品详情页规格过多时支持滚动 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.30 更新日志 ### + +新增:小程序端积分明细页 +新增:页面设计砍价商品组件 +修复:购物车同商品多sku数据错误 +修复:默认购买商品积分赠送数量 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.29 更新日志 ### + +优化:小程序端 商品列表api +优化:小程序端 记忆购物车选择的商品 +新增:用户积分功能(支持积分赠送、积分抵扣) +新增:后台设置是否允许用户充值(隐藏充值入口) +修复:砍价商品海报二维码链接 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.28 更新日志 ### + +优化:监听砍价任务结束 +修复:订单结算优惠券价格计算问题 +修复:后台砍价活动状态设置 +修复:砍价页面分享链接不正确 +修复:订单确认页delivery报错 +修复:后台编辑运费模版报错 +新增:小程序端个人中心我的砍价 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.27 更新日志 ### + +优化:后台优惠券管理折扣率精确度 +优化:会员折扣比例精确小数点2位数 +优化:小程序端验证输入的商品购买数量 +优化:用户首次进入充值中心提示授权 +新增:新增营销模块砍价活动 +修复:后台拼团复制主商城商品提示无权限 +修复:后台导出订单收货地址地级市错误 +修复:未付款订单商品SKU不存在时提示 +修复:小程序端在线客服图标显示 +修复:记忆用户自提订单联系方式 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.26 更新日志 ### + +修复:后台修改会员等级bug +修复:拼团商品生成海报图片错误 +修复:订单自动关闭时回退商品库存、用户优惠券 +修复:拼团商品下单减库存无效 +优化:记忆用户自提订单联系方式 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.25 更新日志 ### + +优化:用户取消订单时回退优惠券 +优化:小程序端 订单详情页快捷导航组件 +优化:后台选择用户添加筛选条件 +优化:后台选择商品添加筛选条件 +新增:会员等级功能 +修复:后台编辑一级分类bug +修复:拼团商品无法使用优惠券 +修复:运费金额过大时计算错误 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.24 更新日志 ### + +优化:小程序端授权登录错误提示 +优化:收货地址的电话支持17x手机号 +新增:微信好物圈同步功能 +修复:后台会员管理充值权限记录 +修复:小程序端余额充值状态提示 +修复:小程序端商品列表单行隐藏问题 +修复:后台文件库列表不显示所属分组 +修复:下单收货地址选择香港出错 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.23 更新日志 ### + +修复:订单管理查询条件情况下导出报错 +新增:diy商品组件支持列表显示 +新增:后台营销管理活跃用户列表 +新增:后台营销管理发送推送消息 +优化:超管后台环境监测临时文件目录 +优化:订单表增加is_delete条件 +优化:小程序端 商品列表显示卖点和销量 +优化:小程序端 商品列表页记忆显示方式 +优化:小程序端 商品购买数量支持自定义输入 +优化:小程序端 拼单详情页面添加快捷导航组件 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.22 更新日志 ### + +修复:拼团订单退款到用户余额 +修复:充值自定义金额时报错 +修复:拼团订单余额支付失败 +新增:后台订单导出显示支付方式 +新增:后台商城设置支持的配送方式 +新增:自提订单需填写联系信息 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.21 更新日志 ### + +修复:后台拼团订单管理搜索查询失效 +修复:后台订单管理按时间筛选报错 +修复:后台设置角色权限不显示菜单问题 +新增:用户余额管理(充值套餐、余额支付) +新增:后台查看指定用户的订单记录 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.20 更新日志 ### + +优化:阿里云OSS支持https域名 +修复:取消订单时回退商品库存 +修复:分销商下级团队列表已删除的用户 +修复:图片上传到本地服务器时报错 +新增:订单管理筛选条件配送方式、自提门店、用户昵称 +新增:订单导出显示商品规格、配送方式、自提门店 +优化:小程序端选择门店时无自提门店提示 +优化:小程序端收货地址手机号验证规则 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.19 更新日志 ### + +修复:小程序端 购物车添加不同规格的商品 +修复:后台拼团订单导出时报错 +新增:后台文件库管理 +修复:后台首页统计已取消订单 +优化:图片库弹窗显示完整的缩略图 +优化:页面设计商品组件支持单列显示 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.18 更新日志 ### + +优化:门店列表支持排序设置 +优化:分销订单自动结算标记是否失效 +优化:分销商订单列表数据获取方式 +优化:token令牌的生成规则 +优化:小程序端导航条加载动画 +优化:订单售后页面显示实付款金额 +优化:查看门店位置显示名称和地址 +新增:DIY页面线下门店列表组件 +修复:小程序端购物车报错(计算运费) +修复:保存小程序码文件报错 +修复:后台文章列表分页 +修复:小程序端商品详情购物车数量 +修复:后台门店店员列表报错 +修复:拼团商品无法选择收货地址 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.17 更新日志 ### + +修复:服务端php7.3环境下报错问题 +修复:新增商城时默认diy页面数据 +修复:后台运费模版页选择区域bug +新增:订单满额包邮功能 +新增:门店自提核销功能 +修复:小程序端 分享自定义专题页打开后是首页 +优化:小程序端 分销商协议过长滚动 +-------------------------------- +注:本次更新须重新发布小程序 + + +### v1.1.16 更新日志 ### + +修复:后台页面设计无法删除默认数据 +修复:页面设计图片橱窗组件报错 +修复:拼团订单发放分销佣金出错 +修复:后台编辑分类无法删除图片 +修复:拼团商品列表价格排序问题 +修复:后台配送模板设置错误 +新增:分销商提现支持微信支付企业付款 +新增:小票打印机管理 +优化:小程序端文章详情页样式 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.15 更新日志 ### + +修复:后台页面设计旧数据兼容性问题 +修复:小程序端文章页分享错误 +修复:小程序端文章列表标题超出未隐藏 +修复:小程序端商品标题超出隐藏兼容性 +新增:页面设计头条快报组件 +优化:升级thinkphp版本5.0.24 +优化:轮播图组件支持设置切换时间 +优化:商品组件支持显示划线价格 +优化:富文本文字默认边距 +优化:小程序端富文本图片加载时尺寸 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.14 更新日志 ### + +修复:后台编辑小程序页面报错 +修复:文章板块设置虚拟阅读量无效 +修复:自定义页面api数据格式不正确 +新增:页面设计在线客服组件 +修复:小程序端 个人中心菜单formid收集 +优化:小程序端 快捷导航组件样式 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.13 更新日志 ### + +优化:小程序DIY视频组件支持自动播放 +优化:小程序端购物车商品总数量显示 +优化:重构后台小程序页面设计 +新增:后台富文本编辑器支持文件库图片 +新增:后台富文本编辑器支持添加视频 +新增:文章资讯板块 +修复:多规格商品默认价格不正确问题 +修复:拼团商品与商城商品id一致生成海报错误 +修复:获取拼团失败的订单过滤未付款订单 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.12 更新日志 ### + +修复:小程序端购物车is_delete报错问题 +修复:拼团DIY组件商品分类数据不正确 +修复:分类页模板分享标题长度 +修复:购物车结算商品数量问题 +修复:超管后台删除权限url报错 +修复:小程序端客服图标不能点击问题 +修复:小程序端拼单详情更多拼团跳转 +优化:小程序端商品详情增加快捷导航 +优化:小程序端个人中心页面UI改版 +优化:后台登录页面兼容适配 +优化:待付款订单中不显示已取消订单 +新增:商品管理支持单独分销设置 +新增:支持分销商内购返一级佣金 +新增:后台分销商列表查看下级用户 +新增:支持购买指定商品成为分销商 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.11 更新日志 ### + +修复:DIY商品组件手动选择排序问题 +修复:待付款订单不显示订单状态 +修复:订单退款失败问题 +修复:小程序端 更多拼团商品价格不正确 +新增:拼团设置支持开启分销 +新增:拼团拼单状态模板消息通知 +优化:小程序端 订单列表分页加载 +优化:小程序端 拼团首页分页加载 +优化:小程序端 拼单详情页支持跳转更多拼团 +优化:小程序端 拼团商品页面收集formId + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.10 更新日志 ### + +优化:小程序端立即购买时关闭确认弹框 +优化:小程序端拼团首页支持转发 +优化:小程序端拼单详情页标签样式 +修复:编辑微信支付证书文件缓存问题 +修复:小程序端拼团商品分享链接错误 +新增:后台小程序页面链接拼团页面 +新增:小程序端拼团商品页显示进行中的拼团 +新增:后台拼团管理支持复制主商城商品 +修复:拼团订单列表发起支付错误 +修复:小程序端领劵中心文字样式 +修复:IOS手机倒计时兼容性问题 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.9 更新日志 ### + +修复:thinkphp路由getshell漏洞(重要) +修复:后台首页统计中包含已删除的会员 +修复:用户申请退货时未填写运单信息可提交 +修复:小程序端生成二维码海报图缓存问题 +修复:已领取的优惠券不自动过期问题 +新增:商品管理商品卖点简述 +新增:后台商品评价管理查看订单详情 +新增:应用中心拼团管理模块 +优化:后台列表页面兼容性问题 +优化:小程序端显示用户已取消的订单 +优化:环境检测中新增微信支付证书目录 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.8 更新日志 ### + +修复:取消已付款订单时更改订单状态 +修复:申请售后不上传图片时凭证报错 +修复:后台售后单列表筛选错误 +优化:发放分销订单佣金时重新计算佣金 +优化:订单商品存在售后退款则不计算分销佣金 +优化:删除umeditor清空文档按钮 +优化:收货地址兼容最新的市辖区 +优化:小程序端售后详情显示已退款金额 +新增:后台分销设置佣金结算天数 +新增:用户售后单状态通知模板消息 +新增:多规格商品支持单独封面图片 +新增:后台设置已完成订单n天内允许申请售后 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.7 更新日志 ### + +新增:商户后台退货地址管理 +新增:后台设置微信支付双向认证文件 +新增:已付款订单用户申请取消并原路退回 +新增:售后订单管理模块(退货退款、换货) +优化:兼容非mysqlnd环境全等改为等于 +优化:小程序端 改版商品详情页面SKU选择 +优化:小程序端 操作按钮样式 +优化:小程序端 删除保存图片分享朋友圈提示语 +修复:多商家后台图片库文件未分离 +修复:新增商城创建默认分类页模板记录 +修复:富文本编辑器p标签间距 +修复:订单批量发货报错问题 +修复:商户后台添加管理员用户名重复问题 +修复:小程序端 富文本域名中带code解析异常 +修复:小程序端 订单列表不显示评论按钮(个别环境) + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.6 更新日志 ### + +新增:超管后台模块 (支持多开小程序商城) +新增:商家后台管理员模板 (支持角色管理和权限管理) +优化:商家后台订单列表添加时间筛选 +优化:商家后台小程序设置敏感项星号隐藏 +优化:自动删除购物车中失效的商品 +修复:小程序端商品列表价格排序箭头顺序 + +注:本次更新无须重新发布小程序 +-------------------------------- + + +### v1.1.5 更新日志 ### + +优化:小程序端富文本内容兼容性 +优化:小程序端商品海报图默认高度 +优化:保存商品海报图显示loading +修复:小程序端扫码进入获取不到场景值 +修复:小程序端DIY富文本组件样式不生效问题 +修复:小程序端loading窗口不关闭的问题 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.4 更新日志 ### + +新增:后台小程序分销中心链接 +新增:后台订单导出功能 +新增:后台订单批量发货功能 +新增:小程序页面DIY公告组 +新增:小程序页面DIY富文本组 +新增:后台删除会员功能 +新增:小程序端商品分享(海报二维码+转发) +优化:后台分销海报设置页面兼容性 +优化:小程序端最近搜索不记录重复值 +优化:小程序端商品列表价格筛选箭头 +优化:小程序端商品详情富文本兼容性 +修复:小程序端banner组件图片高度兼容 +修复:小程序端视频组件底部空白 +修复:小程序端操作成功提示框失效问题 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.3 更新日志 ### + +修复:删除分类时提示存在商品 +修复:后台订单管理不显示改价按钮 +修复:后台编辑DIY页面名称不生效 +优化:后台订单改价不能为0元 +优化:后台商品详情编辑器尺寸 +新增:分销商中心功能 +新增:后台商品列表一键上下架 +新增:后台一键复制商品 +新增:后台用户列表显示累积消费金额 +修复:小程序端商品评价页错乱问题 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.2 更新日志 ### + +修复:订单发货模板消息不显示物流公司 +修复:后台首页统计下单用户数不正确 +修复:后台首页商城统计遮挡左侧菜单 +修复:小程序端 二级分类排版错乱问题 +优化:兼容系统中不存在的地区 +优化:发送消息通知时记录日志 +优化:小程序端 编辑收货地址默认地区选择 +优化:小程序端 图片轮播高度自适应 +优化:小程序端 支持链接跳转tabBar页面 +新增:后台会员列表页条件查询 +新增:后台商品列表页条件查询 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.1 更新日志 ### + +修复:优惠券最多优惠到0.01元 +修复:优惠券有效期日期格式 +修复:小程序端图片橱窗DIY组件链接 +新增:订单提交买家留言 +新增:新版后台首页 +新增:小程序主动更新机制 +新增:小程序模板消息通知功能 +新增:小程序端购物车页可多选 +优化:删除商品改为软删除 + +注:本次更新须重新发布小程序 +-------------------------------- + + +### v1.1.0 更新日志 ### + +新增:小程序分类页面模板 +新增:后台小程序页面设计-自定义页 +新增:后台修改订单价格功能 +新增:小程序端一键获取微信地址 +修复:后台操作成功url跳转问题 +修复:优惠券抵扣金额计算问题 +修复:后台商品评论管理隐藏状态 +修复:小程序端DIY商品组件控制显示内容 +修复:小程序端分类页无数据时错误提示 +优化:后台操作提示等待时间 +优化:后台页面设计兼容性问题 +优化:商品分类排序问题 +优化:小程序端二级分类图片大小 +优化:商品详情页支持点击主图预览 +优化:商品详情页支持右上角分享 + +注:本次更新须重新发布小程序 +-------------------------------- diff --git a/icon.jpg b/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6238359e99190324f40e37b7b0002ccfde685498 GIT binary patch literal 26385 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 literal 0 HcmV?d00001 diff --git a/source/application/admin/config.php b/source/application/admin/config.php new file mode 100644 index 0000000..42bafc0 --- /dev/null +++ b/source/application/admin/config.php @@ -0,0 +1,28 @@ + 'html', + + 'template' => [ + // layout布局 + 'layout_on' => true, + 'layout_name' => 'layouts/layout', + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'think', + // 模板路径 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'php', + // 模板文件名分隔符 + 'view_depr' => DS, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}}', + // 标签库标签开始标记 + 'taglib_begin' => '{{', + // 标签库标签结束标记 + 'taglib_end' => '}}', + ], +]; diff --git a/source/application/admin/controller/Controller.php b/source/application/admin/controller/Controller.php new file mode 100644 index 0000000..5e9a4d9 --- /dev/null +++ b/source/application/admin/controller/Controller.php @@ -0,0 +1,187 @@ +admin = Session::get('yoshop_admin'); + // 当前路由信息 + $this->getRouteinfo(); + // 验证登录 + $this->checkLogin(); + // 全局layout + $this->layout(); + } + + /** + * 全局layout模板输出 + * @throws \Exception + */ + private function layout() + { + // 验证当前请求是否在白名单 + if (!in_array($this->routeUri, $this->notLayoutAction)) { + // 输出到view + $this->assign([ + 'base_url' => base_url(), // 当前域名 + 'admin_url' => url('/admin'), // 后台模块url + 'group' => $this->group, + 'menus' => $this->menus(), // 后台菜单 + 'admin' => $this->admin, // 商家登录信息 + 'version' => get_version(), // 当前系统版本号 + 'request' => Request::instance() // Request对象 + ]); + } + } + + /** + * 解析当前路由参数 (分组名称、控制器名称、方法名) + */ + protected function getRouteinfo() + { + // 控制器名称 + $this->controller = toUnderScore($this->request->controller()); + // 方法名称 + $this->action = $this->request->action(); + // 控制器分组 (用于定义所属模块) + $groupstr = strstr($this->controller, '.', true); + $this->group = $groupstr !== false ? $groupstr : $this->controller; + // 当前uri + $this->routeUri = $this->controller . '/' . $this->action; + } + + /** + * 后台菜单配置 + * @return array + */ + private function menus() + { + foreach ($data = Config::get('menus') as $group => $first) { + $data[$group]['active'] = $group === $this->group; + // 遍历:二级菜单 + if (isset($first['submenu'])) { + foreach ($first['submenu'] as $secondKey => $second) { + // 二级菜单所有uri + $secondUris = isset($second['uris']) ? $second['uris'] : [$second['index']]; + // 二级菜单:active + !isset($data[$group]['submenu'][$secondKey]['active']) + && $data[$group]['submenu'][$secondKey]['active'] = in_array($this->routeUri, $secondUris); + } + } + } + return $data; + } + + /** + * 验证登录状态 + * @return bool + */ + private function checkLogin() + { + // 验证当前请求是否在白名单 + if (in_array($this->routeUri, $this->allowAllAction)) { + return true; + } + // 验证登录状态 + if (empty($this->admin) + || (int)$this->admin['is_login'] !== 1 + ) { + $this->redirect('passport/login'); + return false; + } + return true; + } + + /** + * 返回封装后的 API 数据到客户端 + * @param int $code + * @param string $msg + * @param string $url + * @param array $data + * @return array + */ + protected function renderJson($code = 1, $msg = '', $url = '', $data = []) + { + return compact('code', 'msg', 'url', 'data'); + } + + /** + * 返回操作成功json + * @param string $msg + * @param string $url + * @param array $data + * @return array + */ + protected function renderSuccess($msg = 'success', $url = '', $data = []) + { + return $this->renderJson(1, $msg, $url, $data); + } + + /** + * 返回操作失败json + * @param string $msg + * @param string $url + * @param array $data + * @return array + */ + protected function renderError($msg = 'error', $url = '', $data = []) + { + return $this->renderJson(0, $msg, $url, $data); + } + + /** + * 获取post数据 (数组) + * @param $key + * @return mixed + */ + protected function postData($key) + { + return $this->request->post($key . '/a'); + } + +} diff --git a/source/application/admin/controller/Index.php b/source/application/admin/controller/Index.php new file mode 100644 index 0000000..05f56cc --- /dev/null +++ b/source/application/admin/controller/Index.php @@ -0,0 +1,22 @@ +fetch('index'); + } + +} diff --git a/source/application/admin/controller/Passport.php b/source/application/admin/controller/Passport.php new file mode 100644 index 0000000..411dd8d --- /dev/null +++ b/source/application/admin/controller/Passport.php @@ -0,0 +1,44 @@ +request->isAjax()) { + $model = new UserModel; + if ($model->login($this->postData('User'))) { + return $this->renderSuccess('登录成功', url('index/index')); + } + return $this->renderError($model->getError() ?: '登录失败'); + } + $this->view->engine->layout(false); + return $this->fetch('login'); + } + + /** + * 退出登录 + */ + public function logout() + { + Session::clear('yoshop_admin'); + $this->redirect('passport/login'); + } + +} diff --git a/source/application/admin/controller/Store.php b/source/application/admin/controller/Store.php new file mode 100644 index 0000000..6041b35 --- /dev/null +++ b/source/application/admin/controller/Store.php @@ -0,0 +1,122 @@ +fetch('index', [ + 'list' => $list = $model->getList(), + 'names' => $model->getStoreName($list) + ]); + } + + /** + * 进入商城 + * @param $wxapp_id + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function enter($wxapp_id) + { + $model = new StoreUser; + $model->login($wxapp_id); + $this->redirect('store/index/index'); + } + + /** + * 回收站列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function recycle() + { + $model = new WxappModel; + return $this->fetch('recycle', [ + 'list' => $list = $model->getList(true), + 'names' => $model->getStoreName($list) + ]); + } + + /** + * 添加小程序 + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function add() + { + $model = new WxappModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('store'))) { + return $this->renderSuccess('添加成功', url('store/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 回收小程序 + * @param $wxapp_id + * @return array + * @throws \think\exception\DbException + */ + public function recovery($wxapp_id) + { + // 小程序详情 + $model = WxappModel::detail($wxapp_id); + if (!$model->recycle()) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 移出回收站 + * @param $wxapp_id + * @return array + * @throws \think\exception\DbException + */ + public function move($wxapp_id) + { + // 小程序详情 + $model = WxappModel::detail($wxapp_id); + if (!$model->recycle(false)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除小程序 + * @param $wxapp_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($wxapp_id) + { + // 小程序详情 + $model = WxappModel::detail($wxapp_id); + if (!$model->setDelete()) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + +} \ No newline at end of file diff --git a/source/application/admin/controller/admin/User.php b/source/application/admin/controller/admin/User.php new file mode 100644 index 0000000..59e25cc --- /dev/null +++ b/source/application/admin/controller/admin/User.php @@ -0,0 +1,31 @@ +admin['user']['admin_user_id']); + if ($this->request->isAjax()) { + if ($model->renew($this->postData('user'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + return $this->fetch('renew', compact('model')); + } +} diff --git a/source/application/admin/controller/setting/Cache.php b/source/application/admin/controller/setting/Cache.php new file mode 100644 index 0000000..34548d9 --- /dev/null +++ b/source/application/admin/controller/setting/Cache.php @@ -0,0 +1,84 @@ +request->isAjax()) { + $this->rmCache($this->postData('cache')); + return $this->renderSuccess('操作成功'); + } + return $this->fetch('clear', [ + 'isForce' => !!$isForce ?: config('app_debug'), + ]); + } + + /** + * 删除缓存 + * @param $data + */ + private function rmCache($data) + { + // 数据缓存 + if (in_array('data', $data['item'])) { + // 强制模式 + $isForce = isset($data['isForce']) ? !!$data['isForce'] : false; + // 清除缓存 + CacheDriver::clear($isForce ? null : 'cache'); + } + // 临时文件 + if (in_array('temp', $data['item'])) { + $paths = [ + 'temp' => WEB_PATH . 'temp/', + 'runtime' => RUNTIME_PATH . 'image/' + ]; + foreach ($paths as $path) { + $this->deleteFolder($path); + } + } + } + + /** + * 递归删除指定目录下所有文件 + * @param $path + * @return bool + */ + private function deleteFolder($path) + { + if (!is_dir($path)) + return false; + // 扫描一个文件夹内的所有文件夹和文件 + foreach (scandir($path) as $val) { + // 排除目录中的.和.. + if (!in_array($val, ['.', '..', '.gitignore'])) { + // 如果是目录则递归子目录,继续操作 + if (is_dir($path . $val)) { + // 子目录中操作删除文件夹和文件 + $this->deleteFolder($path . $val . '/'); + // 目录清空后删除空文件夹 + rmdir($path . $val . '/'); + } else { + // 如果是文件直接删除 + unlink($path . $val); + } + } + } + return true; + } + +} diff --git a/source/application/admin/controller/setting/Science.php b/source/application/admin/controller/setting/Science.php new file mode 100644 index 0000000..bd9352d --- /dev/null +++ b/source/application/admin/controller/setting/Science.php @@ -0,0 +1,204 @@ + '', + 'warning' => 'am-active', + 'danger' => 'am-danger' + ]; + + /** + * 环境检测 + */ + public function index() + { + return $this->fetch('index', [ + 'statusClass' => $this->statusClass, + 'phpinfo' => $this->phpinfo(), // PHP环境要求 + 'server' => $this->server(), // 服务器信息 + 'writeable' => $this->writeable(), // 目录权限监测 + ]); + } + + /** + * 服务器信息 + * @return array + */ + private function server() + { + return [ + 'system' => [ + 'name' => '服务器操作系统', + 'value' => PHP_OS, + 'status' => PHP_SHLIB_SUFFIX === 'dll' ? 'warning' : 'normal', + 'remark' => '建议使用 Linux 系统以提升程序性能' + ], + 'webserver' => [ + 'name' => 'Web服务器环境', + 'value' => $this->request->server('SERVER_SOFTWARE'), + 'status' => PHP_SAPI === 'isapi' ? 'warning' : 'normal', + 'remark' => '建议使用 Apache 或 Nginx 以提升程序性能' + ], + 'php' => [ + 'name' => 'PHP版本', + 'value' => PHP_VERSION, + 'status' => version_compare(PHP_VERSION, '5.4.0') === -1 ? 'danger' : 'normal', + 'remark' => 'PHP版本必须为 5.4.0 以上' + ], + 'upload_max' => [ + 'name' => '文件上传限制', + 'value' => @ini_get('file_uploads') ? ini_get('upload_max_filesize') : 'unknow', + 'status' => 'normal', + 'remark' => '' + ], + 'web_path' => [ + 'name' => '程序运行目录', + 'value' => str_replace('\\', '/', WEB_PATH), + 'status' => 'normal', + 'remark' => '' + ], + ]; + } + + /** + * PHP环境要求 + * @return array + */ + private function phpinfo() + { +// pre( get_loaded_extensions() ); + return [ + 'php_version' => [ + 'name' => 'PHP版本', + 'value' => '5.4.0及以上', + 'status' => version_compare(PHP_VERSION, '5.4.0') === -1 ? 'danger' : 'normal', + 'remark' => 'PHP版本必须为 5.4.0及以上' + ], + 'curl' => [ + 'name' => 'CURL', + 'value' => '支持', + 'status' => extension_loaded('curl') && function_exists('curl_init') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持CURL, 系统无法正常运行' + ], + 'openssl' => [ + 'name' => 'OpenSSL', + 'value' => '支持', + 'status' => extension_loaded('openssl') ? 'normal' : 'danger', + 'remark' => '没有启用OpenSSL, 将无法访问微信平台的接口' + ], + 'pdo' => [ + 'name' => 'PDO', + 'value' => '支持', + 'status' => extension_loaded('PDO') && extension_loaded('pdo_mysql') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持PDO, 系统无法正常运行' + ], + 'gd' => [ + 'name' => 'GD', + 'value' => '支持', + 'status' => extension_loaded('gd') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持GD, 系统无法正常生成图片' + ], + 'bcmath' => [ + 'name' => 'BCMath', + 'value' => '支持', + 'status' => extension_loaded('bcmath') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持BCMath, 系统无法正常运行' + ], + 'mbstring' => [ + 'name' => 'mbstring', + 'value' => '支持', + 'status' => extension_loaded('mbstring') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持mbstring, 系统无法正常运行' + ], + 'SimpleXML' => [ + 'name' => 'SimpleXML', + 'value' => '支持', + 'status' => extension_loaded('SimpleXML') ? 'normal' : 'danger', + 'remark' => '您的PHP环境不支持SimpleXML, 系统无法解析xml 无法使用微信支付' + ], + ]; + + } + + /** + * 目录权限监测 + */ + private function writeable() + { + $paths = [ + 'uploads' => realpath(WEB_PATH) . '/uploads/', + 'temp' => realpath(WEB_PATH) . '/temp/', + 'wxpay_log' => realpath(APP_PATH) . '/common/library/wechat/logs/', + 'wxpay_cert' => realpath(APP_PATH) . '/common/library/wechat/cert/', + 'behavior_log' => realpath(APP_PATH) . '/task/behavior/logs/', + ]; + return [ + 'uploads' => [ + 'name' => '文件上传目录', + 'value' => str_replace('\\', '/', $paths['uploads']), + 'status' => $this->checkWriteable($paths['uploads']) ? 'normal' : 'danger', + 'remark' => '目录不可写,系统将无法正常上传文件' + ], + 'temp' => [ + 'name' => '临时文件目录', + 'value' => str_replace('\\', '/', $paths['temp']), + 'status' => $this->checkWriteable($paths['temp']) ? 'normal' : 'danger', + 'remark' => '目录不可写,系统将无法正常写入文件' + ], + 'wxpay_log' => [ + 'name' => '微信支付日志目录', + 'value' => str_replace('\\', '/', $paths['wxpay_log']), + 'status' => $this->checkWriteable($paths['wxpay_log']) ? 'normal' : 'danger', + 'remark' => '目录不可写,系统将无法正常写入文件' + ], + 'wxpay_cert' => [ + 'name' => '微信支付证书目录', + 'value' => str_replace('\\', '/', $paths['wxpay_cert']), + 'status' => $this->checkWriteable($paths['wxpay_cert']) ? 'normal' : 'danger', + 'remark' => '目录不可写,系统将无法正常写入文件' + ], +// 'behavior_log' => [ +// 'name' => '自动任务日志目录', +// 'value' => str_replace('\\', '/', $paths['behavior_log']), +// 'status' => $this->checkWriteable($paths['behavior_log']) ? 'normal' : 'danger', +// 'remark' => '目录不可写,系统将无法正常上传文件' +// ], + ]; + + } + + /** + * 检查目录是否可写 + * @param $path + * @return bool + */ + private function checkWriteable($path) + { + try { + !is_dir($path) && mkdir($path, 0755); + if (!is_dir($path)) + return false; + $fileName = $path . '/_test_write.txt'; + if ($fp = fopen($fileName, 'w')) { + return fclose($fp) && unlink($fileName); + } + } catch (\Exception $e) { + } + return false; + } + +} diff --git a/source/application/admin/controller/store/Access.php b/source/application/admin/controller/store/Access.php new file mode 100644 index 0000000..5e3fa9c --- /dev/null +++ b/source/application/admin/controller/store/Access.php @@ -0,0 +1,94 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加权限 + * @return array|mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function add() + { + $model = new AccesscModel; + if (!$this->request->isAjax()) { + // 权限列表 + $accessList = $model->getList(); + return $this->fetch('add', compact('accessList')); + } + // 新增记录 + if ($model->add($this->postData('access'))) { + return $this->renderSuccess('添加成功', url('store.access/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新权限 + * @param $access_id + * @return array|mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit($access_id) + { + // 权限详情 + $model = AccesscModel::detail($access_id); + if (!$this->request->isAjax()) { + // 权限列表 + $accessList = $model->getList(); + return $this->fetch('edit', compact('model', 'accessList')); + } + // 更新记录 + if ($model->edit($this->postData('access'))) { + return $this->renderSuccess('更新成功', url('store.access/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除权限 + * @param $access_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($access_id) + { + // 权限详情 + $model = AccesscModel::detail($access_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/admin/extra/menus.php b/source/application/admin/extra/menus.php new file mode 100644 index 0000000..067fe2f --- /dev/null +++ b/source/application/admin/extra/menus.php @@ -0,0 +1,47 @@ + [ + * 'name' => '首页', // 菜单名称 + * 'icon' => 'icon-home', // 图标 (class) + * 'index' => 'index/index', // 链接 + * ], + */ +return [ + 'store' => [ + 'name' => '小程序商城', + 'icon' => 'icon-shangcheng', + 'submenu' => [ + [ + 'name' => '商城列表', + 'index' => 'store/index', + 'uris' => [ + 'store/index', + 'store/add', + ] + ], + [ + 'name' => '回收站', + 'index' => 'store/recycle' + ], + [ + 'name' => '权限管理', + 'index' => 'store.access/index' + ] + ], + ], + 'setting' => [ + 'name' => '系统设置', + 'icon' => 'icon-shezhi', + 'submenu' => [ + [ + 'name' => '清理缓存', + 'index' => 'setting.cache/clear' + ], + [ + 'name' => '环境检测', + 'index' => 'setting.science/index' + ], + ], + ], +]; diff --git a/source/application/admin/model/Setting.php b/source/application/admin/model/Setting.php new file mode 100644 index 0000000..582ae8c --- /dev/null +++ b/source/application/admin/model/Setting.php @@ -0,0 +1,31 @@ +defaultData($store_name) as $key => $item) { + $data[] = array_merge($item, ['wxapp_id' => $wxapp_id]); + } + return $this->saveAll($data); + } + +} diff --git a/source/application/admin/model/Wxapp.php b/source/application/admin/model/Wxapp.php new file mode 100644 index 0000000..824964b --- /dev/null +++ b/source/application/admin/model/Wxapp.php @@ -0,0 +1,103 @@ +where('is_recycle', '=', (int)$is_recycle) + ->where('is_delete', '=', 0) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 从缓存中获取商城名称 + * @param $data + * @return array + */ + public function getStoreName($data) + { + $names = []; + foreach ($data as $wxapp) { + $names[$wxapp['wxapp_id']] = Setting::getItem('store', $wxapp['wxapp_id'])['name']; + } + return $names; + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function add($data) + { + if ($data['password'] !== $data['password_confirm']) { + $this->error = '确认密码不正确'; + return false; + } + $this->startTrans(); + try { + // 添加小程序记录 + $this->allowField(true)->save($data); + // 商城默认设置 + (new Setting)->insertDefault($this['wxapp_id'], $data['store_name']); + // 新增商家用户信息 + $StoreUser = new StoreUser; + if (!$StoreUser->add($this['wxapp_id'], $data)) { + $this->error = $StoreUser->error; + return false; + } + // 新增小程序默认帮助 + (new WxappHelp)->insertDefault($this['wxapp_id']); + // 新增小程序diy配置 + (new WxappPage)->insertDefault($this['wxapp_id']); + // 新增小程序分类页模板 + (new WxappCategory)->insertDefault($this['wxapp_id']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 移入移出回收站 + * @param bool $is_recycle + * @return false|int + */ + public function recycle($is_recycle = true) + { + return $this->save(['is_recycle' => (int)$is_recycle]); + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + +} diff --git a/source/application/admin/model/WxappCategory.php b/source/application/admin/model/WxappCategory.php new file mode 100644 index 0000000..b42f29c --- /dev/null +++ b/source/application/admin/model/WxappCategory.php @@ -0,0 +1,28 @@ +save([ + 'wxapp_id' => $wxapp_id, + 'category_style' => '11', + 'share_title' => '', + ]); + } + +} \ No newline at end of file diff --git a/source/application/admin/model/WxappHelp.php b/source/application/admin/model/WxappHelp.php new file mode 100644 index 0000000..1aaa62c --- /dev/null +++ b/source/application/admin/model/WxappHelp.php @@ -0,0 +1,29 @@ +save([ + 'title' => '关于小程序', + 'content' => '小程序本身无需下载,无需注册,不占用手机内存,可以跨平台使用,响应迅速,体验接近原生APP。', + 'sort' => 100, + 'wxapp_id' => $wxapp_id + ]); + } + +} diff --git a/source/application/admin/model/WxappPage.php b/source/application/admin/model/WxappPage.php new file mode 100644 index 0000000..81f6917 --- /dev/null +++ b/source/application/admin/model/WxappPage.php @@ -0,0 +1,75 @@ +save([ + 'page_type' => 10, + 'page_name' => '小程序首页', + 'page_data' => [ + 'page' => [ + 'type' => 'page', + 'name' => '页面设置', + 'params' => [ + 'name' => '页面标题', + 'title' => '页面标题', + 'share_title' => '分享标题' + ], + 'style' => [ + 'titleTextColor' => 'black', + 'titleBackgroundColor' => '#ffffff', + ] + ], + 'items' => [ + [ + 'type' => 'search', + 'name' => '搜索框', + 'params' => ['placeholder' => '搜索商品'], + 'style' => [ + 'textAlign' => 'center', + 'searchStyle' => 'radius', + ], + ], + [ + 'type' => 'banner', + 'name' => '图片轮播', + 'style' => [ + 'btnColor' => '#ffffff', + 'btnShape' => 'round', + ], + 'params' => [ + 'interval' => '2800' + ], + 'data' => [ + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'linkUrl' => '', + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'linkUrl' => '', + ], + ], + ] + ], + ], + 'wxapp_id' => $wxapp_id + ]); + } + +} diff --git a/source/application/admin/model/admin/User.php b/source/application/admin/model/admin/User.php new file mode 100644 index 0000000..bc2b3dc --- /dev/null +++ b/source/application/admin/model/admin/User.php @@ -0,0 +1,81 @@ +where([ + 'user_name' => $data['user_name'], + 'password' => yoshop_hash($data['password']) + ])->find()) { + $this->error = '登录失败, 用户名或密码错误'; + return false; + } + // 保存登录状态 + Session::set('yoshop_admin', [ + 'user' => [ + 'admin_user_id' => $user['admin_user_id'], + 'user_name' => $user['user_name'], + ], + 'is_login' => true, + ]); + return true; + } + + /** + * 超管用户信息 + * @param $admin_user_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($admin_user_id) + { + return self::get($admin_user_id); + } + + /** + * 更新当前管理员信息 + * @param $data + * @return bool + */ + public function renew($data) + { + if ($data['password'] !== $data['password_confirm']) { + $this->error = '确认密码不正确'; + return false; + } + // 更新管理员信息 + if ($this->save([ + 'user_name' => $data['user_name'], + 'password' => yoshop_hash($data['password']), + ]) === false) { + return false; + } + // 更新session + Session::set('yoshop_admin.user', [ + 'admin_user_id' => $this['admin_user_id'], + 'user_name' => $data['user_name'], + ]); + return true; + } + +} \ No newline at end of file diff --git a/source/application/admin/model/store/Access.php b/source/application/admin/model/store/Access.php new file mode 100644 index 0000000..e00c2c6 --- /dev/null +++ b/source/application/admin/model/store/Access.php @@ -0,0 +1,656 @@ +formatTreeData($all); + } + + /** + * 新增记录 + * @param $data + * @return false|int + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 更新记录 + * @param $data + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit($data) + { + // 判断上级角色是否为当前子级 + if ($data['parent_id'] > 0) { + // 获取所有上级id集 + $parentIds = $this->getTopAccessIds($data['parent_id']); + if (in_array($this['access_id'], $parentIds)) { + $this->error = '上级权限不允许设置为当前子权限'; + return false; + } + } + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除权限 + * @return bool|int + * @throws \think\exception\DbException + */ + public function remove() + { + // 判断是否存在下级权限 + if (self::detail(['parent_id' => $this['access_id']])) { + $this->error = '当前权限下存在子权限,请先删除'; + return false; + } + return $this->delete(); + } + + /** + * 获取所有上级id集 + * @param $access_id + * @param null $all + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getTopAccessIds($access_id, &$all = null) + { + static $ids = []; + is_null($all) && $all = $this->getAll(); + foreach ($all as $item) { + if ($item['access_id'] == $access_id && $item['parent_id'] > 0) { + $ids[] = $item['parent_id']; + $this->getTopAccessIds($item['parent_id'], $all); + } + } + return $ids; + } + + /** + * 获取权限列表 + * @param $all + * @param int $parent_id + * @param int $deep + * @return array + */ + private function formatTreeData(&$all, $parent_id = 0, $deep = 1) + { + static $tempTreeArr = []; + foreach ($all as $key => $val) { + if ($val['parent_id'] == $parent_id) { + // 记录深度 + $val['deep'] = $deep; + // 根据角色深度处理名称前缀 + $val['name_h1'] = $this->htmlPrefix($deep) . $val['name']; + $tempTreeArr[] = $val; + $this->formatTreeData($all, $val['access_id'], $deep + 1); + } + } + return $tempTreeArr; + } + + private function htmlPrefix($deep) + { + // 根据角色深度处理名称前缀 + $prefix = ''; + if ($deep > 1) { + for ($i = 1; $i <= $deep - 1; $i++) { + $prefix .= '   ├ '; + } + $prefix .= ' '; + } + return $prefix; + } + + /** + * 新增默认权限 + */ + public function insertDefault() + { + $defaultData = $this->defaultData(); + $this->buildData($defaultData); + } + + /** + * 生成并写入默认数据 + * @param $defaultData + * @param int $parent_id + */ + private function buildData(&$defaultData, $parent_id = 0) + { + foreach ($defaultData as $key => $item) { + // 保存数据 + $model = new static; + $model->save([ + 'name' => $item['name'], + 'url' => $item['url'], + 'parent_id' => $parent_id, + 'sort' => 100, + ]); + if (isset($item['subset']) && !empty($item['subset'])) { + $this->buildData($item['subset'], $model['access_id']); + } + } + } + + /** + * 默认权限数据 + * @return array + */ + private function defaultData() + { + return [ + [ + 'name' => '首页', + 'url' => 'index/index' + ], + [ + 'name' => '管理员', + 'url' => 'store', + 'subset' => [ + [ + 'name' => '管理员管理', + 'url' => 'store.user', + 'subset' => [ + [ + 'name' => '管理员列表', + 'url' => 'store.user/index' + ], + [ + 'name' => '添加管理员', + 'url' => 'store.user/add' + ], + [ + 'name' => '编辑管理员', + 'url' => 'store.user/edit' + ], + [ + 'name' => '删除管理员', + 'url' => 'store.user/delete' + ], + ] + ], + [ + 'name' => '角色管理', + 'url' => 'store.role', + 'subset' => [ + [ + 'name' => '角色列表', + 'url' => 'store.role/index' + ], + [ + 'name' => '添加角色', + 'url' => 'store.role/add' + ], + [ + 'name' => '编辑角色', + 'url' => 'store.role/edit' + ], + [ + 'name' => '删除角色', + 'url' => 'store.role/delete' + ], + ] + ], + [ + 'name' => '权限管理', + 'url' => 'store.access', + 'subset' => [ + [ + 'name' => '权限列表', + 'url' => 'store.access/index' + ], + [ + 'name' => '添加权限', + 'url' => 'store.access/add' + ], + [ + 'name' => '编辑权限', + 'url' => 'store.access/edit' + ], + [ + 'name' => '删除权限', + 'url' => 'store.access/delete' + ], + ] + ], + ] + ], + [ + 'name' => '商品管理', + 'url' => 'goods', + 'subset' => [ + [ + 'name' => '商品管理', + 'url' => 'goods', + 'subset' => [ + [ + 'name' => '商品列表', + 'url' => 'goods/index', + ], + [ + 'name' => '添加商品', + 'url' => 'goods/add', + ], + [ + 'name' => '编辑商品', + 'url' => 'goods/edit', + ], + [ + 'name' => '复制商品', + 'url' => 'goods/copy', + ], + [ + 'name' => '删除商品', + 'url' => 'goods/delete', + ], + [ + 'name' => '商品上下架', + 'url' => 'goods/state', + ], + ] + ], + [ + 'name' => '商品分类', + 'url' => 'goods.category', + 'subset' => [ + [ + 'name' => '分类列表', + 'url' => 'goods.category/index', + ], + [ + 'name' => '添加分类', + 'url' => 'goods.category/add', + ], + [ + 'name' => '编辑分类', + 'url' => 'goods.category/edit', + ], + [ + 'name' => '删除分类', + 'url' => 'goods.category/delete', + ], + ], + ], + [ + 'name' => '商品评价', + 'url' => 'goods.comment', + 'subset' => [ + [ + 'name' => '评价列表', + 'url' => 'goods.comment/index', + ], + [ + 'name' => '评价详情', + 'url' => 'goods.comment/detail', + ], + [ + 'name' => '删除评价', + 'url' => 'goods.comment/delete', + ], + ], + ], + ] + ], + [ + 'name' => '订单管理', + 'url' => 'order', + 'subset' => [ + [ + 'name' => '订单列表', + 'url' => '', + 'subset' => [ + [ + 'name' => '待发货', + 'url' => 'order/delivery_list' + ], + [ + 'name' => '待收货', + 'url' => 'order/receipt_list' + ], + [ + 'name' => '待付款', + 'url' => 'order/pay_list' + ], + [ + 'name' => '已完成', + 'url' => 'order/complete_list' + ], + [ + 'name' => '已取消', + 'url' => 'order/cancel_list' + ], + [ + 'name' => '全部订单', + 'url' => 'order/all_list', + ], + ] + ], + [ + 'name' => '订单详情', + 'url' => '', + 'subset' => [ + [ + 'name' => '详情信息', + 'url' => 'order/detail', + ], + [ + 'name' => '确认发货', + 'url' => 'order/delivery', + ], + [ + 'name' => '修改订单价格', + 'url' => 'order/updateprice', + ], + ] + ], + [ + 'name' => '订单导出', + 'url' => 'order.operate/export', + ], + [ + 'name' => '批量发货', + 'url' => 'order.operate/batchdelivery', + ], + ] + ], + [ + 'name' => '用户管理', + 'url' => 'user', + 'subset' => [ + [ + 'name' => '用户列表', + 'url' => 'user/index' + ], + [ + 'name' => '删除用户', + 'url' => 'user/delete' + ], + ] + ], + [ + 'name' => '营销设置', + 'url' => 'market', + 'subset' => [ + [ + 'name' => '优惠券', + 'url' => 'coupon', + 'subset' => [ + [ + 'name' => '优惠券列表', + 'url' => 'market.coupon/index', + ], + [ + 'name' => '新增优惠券', + 'url' => 'market.coupon/add', + ], + [ + 'name' => '编辑优惠券', + 'url' => 'market.coupon/edit', + ], + [ + 'name' => '删除优惠券', + 'url' => 'market.coupon/delete', + ], + [ + 'name' => '领取记录', + 'url' => 'market.coupon/receive', + ] + ] + ] + ] + ], + [ + 'name' => '小程序', + 'url' => 'wxapp', + 'subset' => [ + [ + 'name' => '小程序设置', + 'url' => 'wxapp/setting', + ], + [ + 'name' => '页面管理', + 'url' => 'wxapp.page', + 'subset' => [ + [ + 'name' => '页面设计', + 'url' => '', + 'subset' => [ + [ + 'name' => '页面列表', + 'url' => 'wxapp.page/index', + ], + [ + 'name' => '新增页面', + 'url' => 'wxapp.page/add', + ], + [ + 'name' => '编辑页面', + 'url' => 'wxapp.page/edit', + ], + [ + 'name' => '设为首页', + 'url' => 'wxapp.page/sethome', + ], + ] + ], + [ + 'name' => '分类页模板', + 'url' => 'wxapp.page/category', + ], + [ + 'name' => '页面链接', + 'url' => 'wxapp.page/links', + ], + ] + ], + [ + 'name' => '帮助中心', + 'url' => 'wxapp.help', + 'subset' => [ + [ + 'name' => '帮助列表', + 'url' => 'wxapp.help/index', + ], + [ + 'name' => '新增帮助', + 'url' => 'wxapp.help/add', + ], + [ + 'name' => '编辑帮助', + 'url' => 'wxapp.help/edit', + ], + [ + 'name' => '删除帮助', + 'url' => 'wxapp.help/delete', + ], + ] + ], + ] + ], + [ + 'name' => '应用中心', + 'url' => 'apps', + 'subset' => [ + [ + 'name' => '分销中心', + 'url' => 'apps.dealer', + 'subset' => [ + [ + 'name' => '入驻申请', + 'url' => 'apps.dealer.apply', + 'subset' => [ + [ + 'name' => '申请列表', + 'url' => 'apps.dealer.apply/index' + ], + [ + 'name' => '分销商审核', + 'url' => 'apps.dealer.apply/submit' + ] + ] + ], + [ + 'name' => '分销商用户', + 'url' => 'apps.dealer.user', + 'subset' => [ + [ + 'name' => '分销商列表', + 'url' => 'apps.dealer.user/index', + ], + [ + 'name' => '删除分销商', + 'url' => 'apps.dealer.user/delete' + ], + [ + 'name' => '分销商二维码', + 'url' => 'apps.dealer.user/qrcode' + ] + ] + ], + [ + 'name' => '分销订单', + 'url' => 'apps.dealer.order/index', + ], + [ + 'name' => '提现申请', + 'url' => 'apps.dealer.withdraw', + 'subset' => [ + [ + 'name' => '申请列表', + 'url' => 'apps.dealer.withdraw/index', + ], + [ + 'name' => '提现审核', + 'url' => 'apps.dealer.withdraw/submit' + ], + [ + 'name' => '确认打款', + 'url' => 'apps.dealer.withdraw/money' + ] + ] + ], + [ + 'name' => '分销设置', + 'url' => 'apps.dealer.setting/index', + ], + [ + 'name' => '分销海报', + 'url' => 'apps.dealer.setting/qrcode', + ], + ] + ], + ] + ], + [ + 'name' => '设置', + 'url' => 'setting', + 'subset' => [ + [ + 'name' => '商城设置', + 'url' => 'setting/store', + ], + [ + 'name' => '交易设置', + 'url' => 'setting/trade', + ], + [ + 'name' => '配送设置', + 'url' => 'setting.delivery', + 'subset' => [ + [ + 'name' => '运费模板列表', + 'url' => 'setting.delivery/index' + ], + [ + 'name' => '新增运费模板', + 'url' => 'setting.delivery/add' + ], + [ + 'name' => '编辑运费模板', + 'url' => 'setting.delivery/edit' + ], + [ + 'name' => '删除运费模板', + 'url' => 'setting.delivery/delete' + ], + ] + ], + [ + 'name' => '物流公司', + 'url' => 'setting.express', + 'subset' => [ + [ + 'name' => '物流公司列表', + 'url' => 'setting.express/index' + ], + [ + 'name' => '新增物流公司', + 'url' => 'setting.express/add' + ], + [ + 'name' => '编辑物流公司', + 'url' => 'setting.express/edit' + ], + [ + 'name' => '删除物流公司', + 'url' => 'setting.express/delete' + ], + ] + ], + [ + 'name' => '短信通知', + 'url' => 'setting/sms', + ], + [ + 'name' => '模板消息', + 'url' => 'setting/tplmsg', + ], + [ + 'name' => '上传设置', + 'url' => 'setting/storage', + ], + [ + 'name' => '其他', + 'url' => '', + 'subset' => [ + [ + 'name' => '清理缓存', + 'url' => 'setting.cache/clear', + ], + ] + ] + ] + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/admin/model/store/User.php b/source/application/admin/model/store/User.php new file mode 100644 index 0000000..076e460 --- /dev/null +++ b/source/application/admin/model/store/User.php @@ -0,0 +1,46 @@ +error = '商家用户名已存在'; + return false; + } + return $this->save([ + 'user_name' => $data['user_name'], + 'password' => yoshop_hash($data['password']), + 'wxapp_id' => $wxapp_id, + ]); + } + + /** + * 商家用户登录 + * @param $wxapp_id + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function login($wxapp_id) + { + // 验证用户名密码是否正确 + $user = self::detail(['wxapp_id' => $wxapp_id], ['wxapp']); + $this->loginState($user); + } + +} diff --git a/source/application/admin/view/admin/user/renew.php b/source/application/admin/view/admin/user/renew.php new file mode 100644 index 0000000..30f51cf --- /dev/null +++ b/source/application/admin/view/admin/user/renew.php @@ -0,0 +1,55 @@ +
+
+
+
+
+
+
+
+
管理员设置
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/admin/view/index/index.php b/source/application/admin/view/index/index.php new file mode 100644 index 0000000..a85ded8 --- /dev/null +++ b/source/application/admin/view/index/index.php @@ -0,0 +1,13 @@ + +
+
+
+
小程序运营管理系统
+
Welcome To YoShop System
+
+

平台运营管理后台

+
+
+
+
+ diff --git a/source/application/admin/view/layouts/layout.php b/source/application/admin/view/layouts/layout.php new file mode 100644 index 0000000..03dc60c --- /dev/null +++ b/source/application/admin/view/layouts/layout.php @@ -0,0 +1,104 @@ + + + + + + + 运营管理系统 + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+
+
+ + +
+ + + + + +
+ {__CONTENT__} +
+ +
+ + +
+ 当前系统版本号:v +
+
+ + + + + + + + + diff --git a/source/application/admin/view/passport/login.php b/source/application/admin/view/passport/login.php new file mode 100644 index 0000000..16fcf1e --- /dev/null +++ b/source/application/admin/view/passport/login.php @@ -0,0 +1,63 @@ + + + + + + 商城系统登录 + + + + +
+ +
+ + + + + + diff --git a/source/application/admin/view/setting/cache/clear.php b/source/application/admin/view/setting/cache/clear.php new file mode 100644 index 0000000..9e6cfe6 --- /dev/null +++ b/source/application/admin/view/setting/cache/clear.php @@ -0,0 +1,78 @@ +
+
+
+
+
+
+
+
清理缓存
+
+
+ +
+ + +
+
+ +
+ +
+ + +
+ 此操作将会强制清空所有缓存文件,包含用户授权登录状态、用户购物车数据,仅允许在开发环境中使用 + +
+
+
+ + + +
+
+ +
+
+
+
+
+
+
+
+ diff --git a/source/application/admin/view/setting/science/index.php b/source/application/admin/view/setting/science/index.php new file mode 100644 index 0000000..7c731bc --- /dev/null +++ b/source/application/admin/view/setting/science/index.php @@ -0,0 +1,100 @@ +
+
+
+
+
+
+
+
服务器信息
+
+
+
+ + + + + + + + + + + + + + + +
参数
+
+
+ +
+
PHP环境要求
+
+
+
+ + + + + + + + + + + + + + + + + +
选项要求状态
+ + + + + +
+
+
+ +
+
目录权限监测
+
+
+
+ + + + + + + + + + + + + + + + + +
名称路径状态
+ + + + + +
+
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/source/application/admin/view/store/access/add.php b/source/application/admin/view/store/access/add.php new file mode 100644 index 0000000..b59d92d --- /dev/null +++ b/source/application/admin/view/store/access/add.php @@ -0,0 +1,67 @@ +
+
+
+
+
+
+
+
+
添加权限
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 例如:index/index +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/admin/view/store/access/edit.php b/source/application/admin/view/store/access/edit.php new file mode 100644 index 0000000..7c7b09e --- /dev/null +++ b/source/application/admin/view/store/access/edit.php @@ -0,0 +1,69 @@ +
+
+
+
+
+
+
+
+
编辑权限
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/admin/view/store/access/index.php b/source/application/admin/view/store/access/index.php new file mode 100644 index 0000000..91201b9 --- /dev/null +++ b/source/application/admin/view/store/access/index.php @@ -0,0 +1,83 @@ +
+
+
+
+
+
权限列表
+
+
+
+
+

1:此处数据为商城商户后台的页面url,用于给管理员角色设置操作权限

+

2:如非二次开发需求,本页面数据无需更改

+

3:商户后台菜单配置文件:source/application/store/extra/menus.php

+
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
权限ID权限名称权限url排序添加时间操作
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/admin/view/store/add.php b/source/application/admin/view/store/add.php new file mode 100644 index 0000000..0118250 --- /dev/null +++ b/source/application/admin/view/store/add.php @@ -0,0 +1,70 @@ +
+
+
+
+
+
+
+
新增小程序商城
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+ +
+ + 商家后台用户名 +
+
+
+ +
+ + 商家后台用户密码 +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ diff --git a/source/application/admin/view/store/index.php b/source/application/admin/view/store/index.php new file mode 100644 index 0000000..53cb201 --- /dev/null +++ b/source/application/admin/view/store/index.php @@ -0,0 +1,80 @@ +
+
+
+
+
商城列表
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + +
商城ID商城名称添加时间操作
+

+
+

+
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+ + diff --git a/source/application/admin/view/store/recycle.php b/source/application/admin/view/store/recycle.php new file mode 100644 index 0000000..4376327 --- /dev/null +++ b/source/application/admin/view/store/recycle.php @@ -0,0 +1,75 @@ +
+
+
+
+
回收站列表
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + +
商城ID商城名称添加时间操作
+

+
+

+
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+ + diff --git a/source/application/api/behavior/order/PaySuccess.php b/source/application/api/behavior/order/PaySuccess.php new file mode 100644 index 0000000..02baa03 --- /dev/null +++ b/source/application/api/behavior/order/PaySuccess.php @@ -0,0 +1,114 @@ + 'app\api\service\master\order\PaySuccess', + OrderSourceEnum::BARGAIN => 'app\api\service\bargain\order\PaySuccess', + OrderSourceEnum::SHARP => 'app\api\service\sharp\order\PaySuccess', + ]; + + /** + * 执行入口 + * @param $order + * @param int $orderType + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function run($order, $orderType = OrderTypeEnum::MASTER) + { + // 设置当前类的属性 + $this->setAttribute($order, $orderType); + // 订单公共业务 + $this->onCommonEvent(); + // 普通订单业务 + if ($orderType == OrderTypeEnum::MASTER) { + $this->onMasterEvent(); + } + // 订单来源回调业务 + $this->onSourceCallback(); + return true; + } + + /** + * 设置当前类的属性 + * @param $order + * @param int $orderType + */ + private function setAttribute($order, $orderType = OrderTypeEnum::MASTER) + { + $this->order = $order; + $this->wxappId = $this->order['wxapp_id']; + $this->orderType = $orderType; + } + + /** + * 订单公共业务 + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function onCommonEvent() + { + // 发送消息通知 + (new MessageService)->payment($this->order, $this->orderType); + // 小票打印 + (new PrinterService)->printTicket($this->order, OrderStatusEnum::ORDER_PAYMENT); + } + + /** + * 普通订单业务 + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function onMasterEvent() + { + // 同步好物圈 + (new WowOrder($this->wxappId))->import([$this->order], true); + } + + /** + * 订单来源回调业务 + * @return bool + */ + private function onSourceCallback() + { + if (!isset($this->order['order_source'])) { + return false; + } + if (!isset($this->sourceCallbackClass[$this->order['order_source']])) { + return false; + } + $class = $this->sourceCallbackClass[$this->order['order_source']]; + return !is_null($class) ? (new $class)->onPaySuccess($this->order) : false; + } + +} \ No newline at end of file diff --git a/source/application/api/config.php b/source/application/api/config.php new file mode 100644 index 0000000..df15d4b --- /dev/null +++ b/source/application/api/config.php @@ -0,0 +1,8 @@ + 'json', + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => 'trim,htmlspecialchars,filter_emoji', +]; \ No newline at end of file diff --git a/source/application/api/controller/Address.php b/source/application/api/controller/Address.php new file mode 100644 index 0000000..d807129 --- /dev/null +++ b/source/application/api/controller/Address.php @@ -0,0 +1,112 @@ +getUser(); + $model = new UserAddress; + $list = $model->getList($user['user_id']); + return $this->renderSuccess([ + 'list' => $list, + 'default_id' => $user['address_id'], + ]); + } + + /** + * 添加收货地址 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function add() + { + $model = new UserAddress; + if ($model->add($this->getUser(), $this->request->post())) { + return $this->renderSuccess([], '添加成功'); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 收货地址详情 + * @param $address_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($address_id) + { + $user = $this->getUser(); + $detail = UserAddress::detail($user['user_id'], $address_id); + $region = array_values($detail['region']); + return $this->renderSuccess(compact('detail', 'region')); + } + + /** + * 编辑收货地址 + * @param $address_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function edit($address_id) + { + $user = $this->getUser(); + $model = UserAddress::detail($user['user_id'], $address_id); + if ($model->edit($this->request->post())) { + return $this->renderSuccess([], '更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 设为默认地址 + * @param $address_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function setDefault($address_id) + { + $user = $this->getUser(); + $model = UserAddress::detail($user['user_id'], $address_id); + if ($model->setDefault($user)) { + return $this->renderSuccess([], '设置成功'); + } + return $this->renderError($model->getError() ?: '设置失败'); + } + + /** + * 删除收货地址 + * @param $address_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function delete($address_id) + { + $user = $this->getUser(); + $model = UserAddress::detail($user['user_id'], $address_id); + if ($model->remove($user)) { + return $this->renderSuccess([], '删除成功'); + } + return $this->renderError($model->getError() ?: '删除失败'); + } + +} diff --git a/source/application/api/controller/Article.php b/source/application/api/controller/Article.php new file mode 100644 index 0000000..1c76060 --- /dev/null +++ b/source/application/api/controller/Article.php @@ -0,0 +1,53 @@ +renderSuccess(compact('categoryList')); + } + + /** + * 文章列表 + * @param int $category_id + * @return array + * @throws \think\exception\DbException + */ + public function lists($category_id = 0) + { + $model = new ArticleModel; + $list = $model->getList($category_id); + return $this->renderSuccess(compact('list')); + } + + /** + * 文章详情 + * @param $article_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function detail($article_id) + { + $detail = ArticleModel::detail($article_id); + return $this->renderSuccess(compact('detail')); + } + +} diff --git a/source/application/api/controller/Cart.php b/source/application/api/controller/Cart.php new file mode 100644 index 0000000..86d2796 --- /dev/null +++ b/source/application/api/controller/Cart.php @@ -0,0 +1,96 @@ +user = $this->getUser(); + $this->model = new CartModel($this->user); + } + + /** + * 购物车列表 + * @return array + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function lists() + { + // 请求参数 + $param = $this->request->param(); + $cartIds = isset($param['cart_ids']) ? $param['cart_ids'] : ''; + // 购物车商品列表 + $goodsList = $this->model->getList($cartIds); + // 获取订单结算信息 + $Checkout = new CheckoutModel; + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + return $this->renderSuccess($orderInfo); + } + + /** + * 加入购物车 + * @param int $goods_id 商品id + * @param int $goods_num 商品数量 + * @param string $goods_sku_id 商品sku索引 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function add($goods_id, $goods_num, $goods_sku_id) + { + if (!$this->model->add($goods_id, $goods_num, $goods_sku_id)) { + return $this->renderError($this->model->getError() ?: '加入购物车失败'); + } + // 购物车商品总数量 + $totalNum = $this->model->getGoodsNum(); + return $this->renderSuccess(['cart_total_num' => $totalNum], '加入购物车成功'); + } + + /** + * 减少购物车商品数量 + * @param $goods_id + * @param $goods_sku_id + * @return array + */ + public function sub($goods_id, $goods_sku_id) + { + $this->model->sub($goods_id, $goods_sku_id); + return $this->renderSuccess(); + } + + /** + * 删除购物车中指定商品 + * @param $goods_sku_id (支持字符串ID集) + * @return array + */ + public function delete($goods_sku_id) + { + $this->model->delete($goods_sku_id); + return $this->renderSuccess(); + } + +} diff --git a/source/application/api/controller/Category.php b/source/application/api/controller/Category.php new file mode 100644 index 0000000..27c18ff --- /dev/null +++ b/source/application/api/controller/Category.php @@ -0,0 +1,29 @@ +renderSuccess(compact('templet', 'list')); + } + +} diff --git a/source/application/api/controller/Comment.php b/source/application/api/controller/Comment.php new file mode 100644 index 0000000..6dab846 --- /dev/null +++ b/source/application/api/controller/Comment.php @@ -0,0 +1,29 @@ +getGoodsCommentList($goods_id, $scoreType); + $total = $model->getTotal($goods_id); + return $this->renderSuccess(compact('list', 'total')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/Controller.php b/source/application/api/controller/Controller.php new file mode 100644 index 0000000..8850353 --- /dev/null +++ b/source/application/api/controller/Controller.php @@ -0,0 +1,139 @@ +wxapp_id = $this->getWxappId(); + // 验证当前小程序状态 + $this->checkWxapp(); + } + + /** + * 获取当前小程序ID + * @return mixed + * @throws BaseException + */ + private function getWxappId() + { + if (!$wxapp_id = $this->request->param('wxapp_id')) { + throw new BaseException(['msg' => '缺少必要的参数:wxapp_id']); + } + return $wxapp_id; + } + + /** + * 验证当前小程序状态 + * @throws BaseException + * @throws \think\exception\DbException + */ + private function checkWxapp() + { + $wxapp = WxappModel::detail($this->wxapp_id); + if (empty($wxapp)) { + throw new BaseException(['msg' => '当前小程序信息不存在']); + } + if ($wxapp['is_recycle'] || $wxapp['is_delete']) { + throw new BaseException(['msg' => '当前小程序已删除']); + } + } + + /** + * 获取当前用户信息 + * @param bool $is_force + * @return UserModel|bool|null + * @throws BaseException + * @throws \think\exception\DbException + */ + protected function getUser($is_force = true) + { + if (!$token = $this->request->param('token')) { + $is_force && $this->throwError('缺少必要的参数:token', -1); + return false; + } + if (!$user = UserModel::getUser($token)) { + $is_force && $this->throwError('没有找到用户信息', -1); + return false; + } + return $user; + } + + /** + * 输出错误信息 + * @param int $code + * @param $msg + * @throws BaseException + */ + protected function throwError($msg, $code = 0) + { + throw new BaseException(['code' => $code, 'msg' => $msg]); + } + + /** + * 返回封装后的 API 数据到客户端 + * @param int $code + * @param string $msg + * @param array $data + * @return array + */ + protected function renderJson($code = self::JSON_SUCCESS_STATUS, $msg = '', $data = []) + { + return compact('code', 'msg', 'data'); + } + + /** + * 返回操作成功json + * @param array $data + * @param string|array $msg + * @return array + */ + protected function renderSuccess($data = [], $msg = 'success') + { + return $this->renderJson(self::JSON_SUCCESS_STATUS, $msg, $data); + } + + /** + * 返回操作失败json + * @param string $msg + * @param array $data + * @return array + */ + protected function renderError($msg = 'error', $data = []) + { + return $this->renderJson(self::JSON_ERROR_STATUS, $msg, $data); + } + + /** + * 获取post数据 (数组) + * @param $key + * @return mixed + */ + protected function postData($key = null) + { + return $this->request->post(is_null($key) ? '' : $key . '/a'); + } + +} diff --git a/source/application/api/controller/Coupon.php b/source/application/api/controller/Coupon.php new file mode 100644 index 0000000..b4fa88b --- /dev/null +++ b/source/application/api/controller/Coupon.php @@ -0,0 +1,29 @@ +getList($this->getUser(false)); + return $this->renderSuccess(compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/Goods.php b/source/application/api/controller/Goods.php new file mode 100644 index 0000000..8804b7c --- /dev/null +++ b/source/application/api/controller/Goods.php @@ -0,0 +1,81 @@ +request->param(), [ + 'status' => 10 + ]); + // 获取列表数据 + $model = new GoodsModel; + $list = $model->getList($param, $this->getUser(false)); + return $this->renderSuccess(compact('list')); + } + + /** + * 获取商品详情 + * @param $goods_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($goods_id) + { + // 用户信息 + $user = $this->getUser(false); + // 商品详情 + $model = new GoodsModel; + $goods = $model->getDetails($goods_id, $this->getUser(false)); + if ($goods === false) { + return $this->renderError($model->getError() ?: '商品信息不存在'); + } + // 多规格商品sku信息, todo: 已废弃 v1.1.25 + $specData = $goods['spec_type'] == 20 ? $model->getManySpecData($goods['spec_rel'], $goods['sku']) : null; + return $this->renderSuccess([ + // 商品详情 + 'detail' => $goods, + // 购物车商品总数量 + 'cart_total_num' => $user ? (new CartModel($user))->getGoodsNum() : 0, + // 多规格商品sku信息 + 'specData' => $specData, + ]); + } + + /** + * 生成商品海报 + * @param $goods_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function poster($goods_id) + { + // 商品详情 + $detail = GoodsModel::detail($goods_id); + $Qrcode = new GoodsPoster($detail, $this->getUser(false)); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} diff --git a/source/application/api/controller/Notify.php b/source/application/api/controller/Notify.php new file mode 100644 index 0000000..938b340 --- /dev/null +++ b/source/application/api/controller/Notify.php @@ -0,0 +1,25 @@ +notify(); + } +} diff --git a/source/application/api/controller/Order.php b/source/application/api/controller/Order.php new file mode 100644 index 0000000..bad70b8 --- /dev/null +++ b/source/application/api/controller/Order.php @@ -0,0 +1,141 @@ +user = $this->getUser(); + // 验证类 + $this->validate = new CheckoutValidate; + } + + /** + * 订单确认-立即购买 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function buyNow() + { + // 实例化结算台服务 + $Checkout = new CheckoutModel; + // 订单结算api参数 + $params = $Checkout->setParam($this->getParam([ + 'goods_id' => 0, + 'goods_num' => 0, + 'goods_sku_id' => '', + ])); + // 表单验证 + if (!$this->validate->scene('buyNow')->check($params)) { + return $this->renderError($this->validate->getError()); + } + // 立即购买:获取订单商品列表 + $model = new OrderModel; + $goodsList = $model->getOrderGoodsListByNow( + $params['goods_id'], + $params['goods_sku_id'], + $params['goods_num'] + ); + // 获取订单确认信息 + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + if ($this->request->isGet()) { + return $this->renderSuccess($orderInfo); + } + // 订单结算提交 + if ($Checkout->hasError()) { + return $this->renderError($Checkout->getError()); + } + // 创建订单 + if (!$Checkout->createOrder($orderInfo)) { + return $this->renderError($Checkout->getError() ?: '订单创建失败'); + } + // 构建微信支付请求 + $payment = $model->onOrderPayment($this->user, $Checkout->model, $params['pay_type']); + // 返回结算信息 + return $this->renderSuccess([ + 'order_id' => $Checkout->model['order_id'], // 订单id + 'pay_type' => $params['pay_type'], // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 订单确认-购物车结算 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function cart() + { + // 实例化结算台服务 + $Checkout = new CheckoutModel; + // 订单结算api参数 + $params = $Checkout->setParam($this->getParam([ + 'cart_ids' => '', + ])); + // 商品结算信息 + $CartModel = new CartModel($this->user); + // 购物车商品列表 + $goodsList = $CartModel->getList($params['cart_ids']); + // 获取订单结算信息 + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + if ($this->request->isGet()) { + return $this->renderSuccess($orderInfo); + } + // 创建订单 + if (!$Checkout->createOrder($orderInfo)) { + return $this->renderError($Checkout->getError() ?: '订单创建失败'); + } + // 移出购物车中已下单的商品 + $CartModel->clearAll($params['cart_ids']); + // 构建微信支付请求 + $payment = $Checkout->onOrderPayment(); + // 返回状态 + return $this->renderSuccess([ + 'order_id' => $Checkout->model['order_id'], // 订单id + 'pay_type' => $params['pay_type'], // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 订单结算提交的参数 + * @param array $define + * @return array + */ + private function getParam($define = []) + { + return array_merge($define, $this->request->param()); + } + +} diff --git a/source/application/api/controller/Page.php b/source/application/api/controller/Page.php new file mode 100644 index 0000000..ed17cc3 --- /dev/null +++ b/source/application/api/controller/Page.php @@ -0,0 +1,31 @@ +getUser(false), $page_id); + return $this->renderSuccess($data); + } + +} diff --git a/source/application/api/controller/Recharge.php b/source/application/api/controller/Recharge.php new file mode 100644 index 0000000..3b28a77 --- /dev/null +++ b/source/application/api/controller/Recharge.php @@ -0,0 +1,67 @@ +getUser(); + // 充值套餐列表 + $planList = (new PlanModel)->getList(); + // 充值设置 + $setting = SettingModel::getItem('recharge'); + return $this->renderSuccess(compact('userInfo', 'planList', 'setting')); + } + + /** + * 确认充值 + * @param null $planId + * @param int $customMoney + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function submit($planId = null, $customMoney = 0) + { + // 用户信息 + $userInfo = $this->getUser(); + // 生成充值订单 + $model = new OrderModel; + if (!$model->createOrder($userInfo, $planId, $customMoney)) { + return $this->renderError($model->getError() ?: '充值失败'); + } + // 构建微信支付 + $payment = PaymentService::wechat( + $userInfo, + $model['order_id'], + $model['order_no'], + $model['pay_price'], + OrderTypeEnum::RECHARGE + ); + // 充值状态提醒 + $message = ['success' => '充值成功', 'error' => '订单未支付']; + return $this->renderSuccess(compact('payment', 'message'), $message); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/Shop.php b/source/application/api/controller/Shop.php new file mode 100644 index 0000000..d617e81 --- /dev/null +++ b/source/application/api/controller/Shop.php @@ -0,0 +1,41 @@ +getList(true, $longitude, $latitude); + return $this->renderSuccess(compact('list')); + } + + /** + * 门店详情 + * @param $shop_id + * @return array + * @throws \think\exception\DbException + */ + public function detail($shop_id) + { + $detail = ShopModel::detail($shop_id); + return $this->renderSuccess(compact('detail')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/Upload.php b/source/application/api/controller/Upload.php new file mode 100644 index 0000000..6a590d4 --- /dev/null +++ b/source/application/api/controller/Upload.php @@ -0,0 +1,86 @@ +config = SettingModel::getItem('storage'); + // 验证用户 + $this->user = $this->getUser(); + } + + /** + * 图片上传接口 + * @return array + * @throws \think\Exception + */ + public function image() + { + // 实例化存储驱动 + $StorageDriver = new StorageDriver($this->config); + // 设置上传文件的信息 + $StorageDriver->setUploadFile('iFile'); + // 上传图片 + if (!$StorageDriver->upload()) { + return json(['code' => 0, 'msg' => '图片上传失败' . $StorageDriver->getError()]); + } + // 图片上传路径 + $fileName = $StorageDriver->getFileName(); + // 图片信息 + $fileInfo = $StorageDriver->getFileInfo(); + // 添加文件库记录 + $uploadFile = $this->addUploadFile($fileName, $fileInfo, 'image'); + // 图片上传成功 + return json(['code' => 1, 'msg' => '图片上传成功', 'data' => $uploadFile->visible(['file_id'])]); + } + + /** + * 添加文件库上传记录 + * @param $fileName + * @param $fileInfo + * @param $fileType + * @return UploadFile + */ + private function addUploadFile($fileName, $fileInfo, $fileType) + { + // 存储引擎 + $storage = $this->config['default']; + // 存储域名 + $fileUrl = isset($this->config['engine'][$storage]['domain']) + ? $this->config['engine'][$storage]['domain'] : ''; + // 添加文件库记录 + $model = new UploadFile; + $model->add([ + 'storage' => $storage, + 'file_url' => $fileUrl, + 'file_name' => $fileName, + 'file_size' => $fileInfo['size'], + 'file_type' => $fileType, + 'extension' => pathinfo($fileInfo['name'], PATHINFO_EXTENSION), + 'is_user' => 1 + ]); + return $model; + } + +} \ No newline at end of file diff --git a/source/application/api/controller/User.php b/source/application/api/controller/User.php new file mode 100644 index 0000000..511548a --- /dev/null +++ b/source/application/api/controller/User.php @@ -0,0 +1,43 @@ +renderSuccess([ + 'user_id' => $model->login($this->request->post()), + 'token' => $model->getToken() + ]); + } + + /** + * 当前用户详情 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail() + { + // 当前用户信息 + $userInfo = $this->getUser(); + return $this->renderSuccess(compact('userInfo')); + } + +} diff --git a/source/application/api/controller/Wxapp.php b/source/application/api/controller/Wxapp.php new file mode 100644 index 0000000..d3bafea --- /dev/null +++ b/source/application/api/controller/Wxapp.php @@ -0,0 +1,39 @@ +renderSuccess([]); + } + + /** + * 帮助中心 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function help() + { + $model = new WxappHelp; + $list = $model->getList(); + return $this->renderSuccess(compact('list')); + } + +} diff --git a/source/application/api/controller/balance/Log.php b/source/application/api/controller/balance/Log.php new file mode 100644 index 0000000..7cdc29f --- /dev/null +++ b/source/application/api/controller/balance/Log.php @@ -0,0 +1,28 @@ +getUser(); + $list = (new BalanceLogModel)->getList($user['user_id']); + return $this->renderSuccess(compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/bargain/Active.php b/source/application/api/controller/bargain/Active.php new file mode 100644 index 0000000..f532f15 --- /dev/null +++ b/source/application/api/controller/bargain/Active.php @@ -0,0 +1,84 @@ +getHallList(); + return $this->renderSuccess(compact('activeList')); + } + + /** + * 砍价活动详情 + * @param $active_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($active_id) + { + // 获取砍价活动详情 + $model = new ActiveModel; + $active = $model->getDetail($active_id); + if ($active === false) { + return $this->renderError($model->getError()); + } + // 标记当前用户是否正在参与 + $task_id = $model->getWhetherPartake($active_id, $this->getUser(false)); + $is_partake = $task_id > 0; + // 获取商品详情 + $goods = GoodsModel::detail($active['goods_id']); + // 砍价规则 + $setting = SettingModel::getBasic(); + return $this->renderSuccess(compact('active', 'goods', 'setting', 'is_partake', 'task_id')); + } + + /** + * 生成商品海报 + * @param $active_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function poster($active_id) + { + // 获取砍价活动详情 + $model = new ActiveModel; + $active = $model->getDetail($active_id); + if ($active === false) { + return $this->renderError($model->getError()); + } + // 获取商品详情 + $goods = GoodsModel::detail($active['goods_id']); + // 生成商品海报图 + $Qrcode = new GoodsPoster($active, $goods, $this->getUser(false)); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/bargain/Order.php b/source/application/api/controller/bargain/Order.php new file mode 100644 index 0000000..60596a8 --- /dev/null +++ b/source/application/api/controller/bargain/Order.php @@ -0,0 +1,101 @@ +user = $this->getUser(); + } + + /** + * 砍价订单结算 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function checkout() + { + // 实例化结算台服务 + $Checkout = new CheckoutModel; + // 订单结算api参数 + $params = $Checkout->setParam($this->getParam([ + 'task_id' => 0 + ])); + // 获取砍价任务详情 + $task = TaskModel::detail($params['task_id']); + // 获取砍价商品信息 + $goodsList = $task->getTaskGoods($params['task_id']); + if ($goodsList === false) { + return $this->renderError($task->getError()); + } + // 设置订单来源 + $Checkout->setOrderSource([ + 'source' => OrderSourceEnum::BARGAIN, + 'source_id' => $params['task_id'], + ]); + // 砍价商品不参与 等级折扣和优惠券折扣 + $Checkout->setCheckoutRule([ + 'is_user_grade' => false, + 'is_coupon' => false, + 'is_use_points' => false, + 'is_dealer' => SettingModel::getIsDealer(), + ]); + // 获取订单结算信息 + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + if ($this->request->isGet()) { + return $this->renderSuccess($orderInfo); + } + // submit:订单结算提交 + if ($Checkout->hasError()) { + return $this->renderError($Checkout->getError()); + } + // 创建订单 + if (!$Checkout->createOrder($orderInfo)) { + return $this->renderError($Checkout->getError() ?: '订单创建失败'); + } + // 订单创建后将砍价任务结束 + $task->setTaskEnd(); + // 构建微信支付请求 + $payment = $Checkout->onOrderPayment(); + // 支付状态提醒 + $message = ['success' => '支付成功', 'error' => '订单未支付']; + return $this->renderSuccess([ + 'order_id' => $Checkout->model['order_id'], // 订单id + 'pay_type' => $params['pay_type'], // 支付方式 + 'payment' => $payment // 微信支付参数 + ], $message); + } + + /** + * 订单结算提交的参数 + * @param array $define + * @return array + */ + private function getParam($define = []) + { + return array_merge($define, $this->request->param()); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/bargain/Task.php b/source/application/api/controller/bargain/Task.php new file mode 100644 index 0000000..7b8abb3 --- /dev/null +++ b/source/application/api/controller/bargain/Task.php @@ -0,0 +1,91 @@ +getMyList($this->getUser()['user_id']); + return $this->renderSuccess(compact('myList')); + } + + /** + * 创建砍价任务 + * @param $active_id + * @param $goods_sku_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function partake($active_id, $goods_sku_id) + { + // 用户信息 + $user = $this->getUser(); + // 创建砍价任务 + $model = new TaskModel; + if (!$model->partake($user['user_id'], $active_id, $goods_sku_id)) { + return $this->renderError($model->getError() ?: '砍价任务创建失败'); + } + return $this->renderSuccess([ + 'task_id' => $model['task_id'] + ]); + } + + /** + * 获取砍价任务详情 + * @param $task_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($task_id) + { + $model = new TaskModel; + $detail = $model->getTaskDetail($task_id, $this->getUser(false)); + if ($detail === false) { + return $this->renderError($model->getError()); + } + // 砍价规则 + $setting = SettingModel::getBasic(); + return $this->renderSuccess(array_merge($detail, ['setting' => $setting])); + } + + /** + * 帮砍一刀 + * @param $task_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function help_cut($task_id) + { + // 加阻塞锁, 防止并发 + Lock::lockUp("bargain_help_cut_{$task_id}"); + // 砍价任务详情 + $model = TaskModel::detail($task_id); + // 砍一刀的金额 + $cut_money = $model->getCutMoney(); + // 帮砍一刀事件 + $status = $model->helpCut($this->getUser()); + // 解除并发锁 + Lock::unLock("bargain_help_cut_{$task_id}"); + if ($status == true) { + return $this->renderSuccess(compact('cut_money'), '砍价成功'); + } + return $this->renderError($model->getError() ?: '砍价失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/points/Log.php b/source/application/api/controller/points/Log.php new file mode 100644 index 0000000..df39314 --- /dev/null +++ b/source/application/api/controller/points/Log.php @@ -0,0 +1,28 @@ +getUser(); + $list = (new PointsLogModel)->getList($user['user_id']); + return $this->renderSuccess(compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/recharge/Order.php b/source/application/api/controller/recharge/Order.php new file mode 100644 index 0000000..865a8eb --- /dev/null +++ b/source/application/api/controller/recharge/Order.php @@ -0,0 +1,28 @@ +getUser(); + $list = (new OrderModel)->getList($user['user_id']); + return $this->renderSuccess(compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/sharing/Active.php b/source/application/api/controller/sharing/Active.php new file mode 100644 index 0000000..e766e5d --- /dev/null +++ b/source/application/api/controller/sharing/Active.php @@ -0,0 +1,38 @@ +renderError('很抱歉,拼单不存在'); + } + // 拼团商品详情 + $model = new GoodsModel; + $goods = $model->getDetails($detail['goods_id'], $this->getUser(false)); + // 更多拼团商品 + $goodsList = $model->getList([], $this->getUser(false)); + return $this->renderSuccess(compact('detail', 'goods', 'goodsList')); + } + +} diff --git a/source/application/api/controller/sharing/Comment.php b/source/application/api/controller/sharing/Comment.php new file mode 100644 index 0000000..e2634b5 --- /dev/null +++ b/source/application/api/controller/sharing/Comment.php @@ -0,0 +1,68 @@ +getUser(); + // 订单信息 + $order = OrderModel::getUserOrderDetail($order_id, $user['user_id']); + // 验证订单是否已完成 + $model = new CommentModel; + if (!$model->checkOrderAllowComment($order)) { + return $this->renderError($model->getError()); + } + // 待评价商品列表 + /* @var \think\Collection $goodsList */ + $goodsList = OrderGoodsModel::getNotCommentGoodsList($order_id); + if ($goodsList->isEmpty()) { + return $this->renderError('该订单没有可评价的商品'); + } + // 提交商品评价 + if ($this->request->isPost()) { + $formData = $this->request->post('formData', '', null); + if ($model->addForOrder($order, $goodsList, $formData)) { + return $this->renderSuccess([], '评价发表成功'); + } + return $this->renderError($model->getError() ?: '评价发表失败'); + } + return $this->renderSuccess(compact('goodsList')); + } + + /** + * 商品评价列表 + * @param $goods_id + * @param int $scoreType + * @return array + * @throws \think\exception\DbException + */ + public function lists($goods_id, $scoreType = -1) + { + $model = new CommentModel; + $list = $model->getGoodsCommentList($goods_id, $scoreType); + $total = $model->getTotal($goods_id); + return $this->renderSuccess(compact('list', 'total')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/sharing/Goods.php b/source/application/api/controller/sharing/Goods.php new file mode 100644 index 0000000..9dd5ba7 --- /dev/null +++ b/source/application/api/controller/sharing/Goods.php @@ -0,0 +1,83 @@ +request->param(), [ + 'status' => 10 + ]); + // 获取列表数据 + $model = new GoodsModel; + $list = $model->getList($param, $this->getUser(false)); + return $this->renderSuccess(compact('list')); + } + + /** + * 获取商品详情 + * @param $goods_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($goods_id) + { + // 商品详情 + $model = new GoodsModel; + $goods = $model->getDetails($goods_id, $this->getUser(false)); + if ($goods === false) { + return $this->renderError($model->getError() ?: '商品信息不存在'); + } + // 多规格商品sku信息, todo: 已废弃 v1.1.25 + $specData = $goods['spec_type'] == 20 ? $model->getManySpecData($goods['spec_rel'], $goods['sku']) : null; + // 当前进行中的拼单 + $activeList = ActiveModel::getActivityListByGoods($goods_id, 2); + return $this->renderSuccess([ + // 商品详情 + 'detail' => $goods, + // 当前进行中的拼单 + 'activeList' => $activeList, + // 多规格商品sku信息 + 'specData' => $specData, + ]); + } + + /** + * 生成商品海报 + * @param $goods_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function poster($goods_id) + { + // 商品详情 + $detail = GoodsModel::detail($goods_id); + // 生成推广二维码 + $Qrcode = new GoodsPoster($detail, $this->getUser(false), 20); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} diff --git a/source/application/api/controller/sharing/Index.php b/source/application/api/controller/sharing/Index.php new file mode 100644 index 0000000..cd07dcd --- /dev/null +++ b/source/application/api/controller/sharing/Index.php @@ -0,0 +1,26 @@ +renderSuccess(compact('categoryList')); + } + +} diff --git a/source/application/api/controller/sharing/Order.php b/source/application/api/controller/sharing/Order.php new file mode 100644 index 0000000..a12c9fd --- /dev/null +++ b/source/application/api/controller/sharing/Order.php @@ -0,0 +1,242 @@ +user = $this->getUser(); + // 验证类 + $this->validate = new CheckoutValidate; + } + + /** + * 订单结算台 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function checkout() + { + // 实例化结算台服务 + $Checkout = new CheckoutModel; + // 订单结算api参数 + $params = $Checkout->setParam($this->getParam([ + 'goods_id' => 0, + 'goods_num' => 0, + 'goods_sku_id' => '', + ])); + // 表单验证 + if (!$this->validate->scene('buyNow')->check($params)) { + return $this->renderError($this->validate->getError()); + } + // 立即购买:获取订单商品列表 + $model = new OrderModel; + $goodsList = $model->getOrderGoodsListByNow($params); + // 获取订单确认信息 + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + if ($this->request->isGet()) { + return $this->renderSuccess($orderInfo); + } + // 订单结算提交 + if ($Checkout->hasError()) { + return $this->renderError($Checkout->getError()); + } + // 创建订单 + if (!$Checkout->createOrder($orderInfo)) { + return $this->renderError($Checkout->getError() ?: '订单创建失败'); + } + // 构建微信支付请求 + $payment = $model->onOrderPayment($this->user, $Checkout->model, $params['pay_type']); + // 返回结算信息 + return $this->renderSuccess([ + 'order_id' => $Checkout->model['order_id'], // 订单id + 'pay_type' => $params['pay_type'], // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 订单结算提交的参数 + * @param array $define + * @return array + */ + private function getParam($define = []) + { + return array_merge($define, $this->request->param()); + } + + /** + * 我的拼团订单列表 + * @param $dataType + * @return array + * @throws \think\exception\DbException + */ + public function lists($dataType) + { + $model = new OrderModel; + $list = $model->getList($this->user['user_id'], $dataType); + return $this->renderSuccess(compact('list')); + } + + /** + * 拼团订单详情信息 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + */ + public function detail($order_id) + { + // 订单详情 + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 该订单是否允许申请售后 + $model['isAllowRefund'] = $model->isAllowRefund(); + return $this->renderSuccess([ + 'order' => $model, // 订单详情 + 'setting' => [ + // 积分名称 + 'points_name' => SettingModel::getPointsName(), + ], + ]); + } + + /** + * 获取物流信息 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + */ + public function express($order_id) + { + // 订单信息 + $order = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if (!$order['express_no']) { + return $this->renderError('没有物流信息'); + } + // 获取物流信息 + /* @var \app\store\model\Express $model */ + $model = $order['express']; + $express = $model->dynamic($model['express_name'], $model['express_code'], $order['express_no']); + if ($express === false) { + return $this->renderError($model->getError()); + } + return $this->renderSuccess(compact('express')); + } + + /** + * 取消订单 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + */ + public function cancel($order_id) + { + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if ($model->cancel($this->user)) { + return $this->renderSuccess($model->getError() ?: '订单取消成功'); + } + return $this->renderError($model->getError() ?: '订单取消失败'); + } + + /** + * 确认收货 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + */ + public function receipt($order_id) + { + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if ($model->receipt()) { + return $this->renderSuccess(); + } + return $this->renderError($model->getError()); + } + + /** + * 立即支付 + * @param int $order_id 订单id + * @param int $payType 支付方式 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function pay($order_id, $payType = PayTypeEnum::WECHAT) + { + // 订单详情 + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 订单支付事件 + if (!$model->onPay($payType)) { + return $this->renderError($model->getError() ?: '订单支付失败'); + } + // 构建微信支付请求 + $payment = $model->onOrderPayment($this->user, $model, $payType); + // 支付状态提醒 + return $this->renderSuccess([ + 'order_id' => $model['order_id'], // 订单id + 'pay_type' => $payType, // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 获取订单核销二维码 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function extractQrcode($order_id) + { + // 订单详情 + $order = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 判断是否为待核销订单 + if (!$order->checkExtractOrder($order)) { + return $this->renderError($order->getError()); + } + $Qrcode = new ExtractQRcode( + $this->wxapp_id, + $this->user, + $order_id, + OrderTypeEnum::SHARING + ); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} diff --git a/source/application/api/controller/sharing/Refund.php b/source/application/api/controller/sharing/Refund.php new file mode 100644 index 0000000..21152c2 --- /dev/null +++ b/source/application/api/controller/sharing/Refund.php @@ -0,0 +1,109 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 用户售后单列表 + * @param int $state + * @return array + * @throws \think\exception\DbException + */ + public function lists($state = -1) + { + $model = new OrderRefundModel; + $list = $model->getList($this->user['user_id'], (int)$state); + return $this->renderSuccess(compact('list')); + } + + /** + * 申请售后 + * @param $order_goods_id + * @return array + * @throws \think\exception\DbException + * @throws \Exception + */ + public function apply($order_goods_id) + { + // 订单商品详情 + $goods = OrderGoodsModel::detail($order_goods_id); + if (isset($goods['refund']) && !empty($goods['refund'])) { + return $this->renderError('当前商品已申请售后'); + } + if (!$this->request->isPost()) { + return $this->renderSuccess(['detail' => $goods]); + } + // 新增售后单记录 + $model = new OrderRefundModel; + if ($model->apply($this->user, $goods, $this->request->post())) { + return $this->renderSuccess([], '提交成功'); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + + /** + * 售后单详情 + * @param $order_refund_id + * @return array + * @throws \think\exception\DbException + */ + public function detail($order_refund_id) + { + // 售后单详情 + $detail = OrderRefundModel::detail([ + 'user_id' => $this->user['user_id'], + 'order_refund_id' => $order_refund_id + ]); + if (empty($detail)) { + return $this->renderError('售后单不存在'); + } + // 物流公司列表 + $expressList = ExpressModel::getAll(); + return $this->renderSuccess(compact('detail', 'expressList')); + } + + /** + * 用户发货 + * @param $order_refund_id + * @return array + * @throws \think\exception\DbException + */ + public function delivery($order_refund_id) + { + // 售后单详情 + $model = OrderRefundModel::detail([ + 'user_id' => $this->user['user_id'], + 'order_refund_id' => $order_refund_id + ]); + if ($model->delivery($this->postData())) { + return $this->renderSuccess([], '操作成功'); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/sharing/Setting.php b/source/application/api/controller/sharing/Setting.php new file mode 100644 index 0000000..a8f44a4 --- /dev/null +++ b/source/application/api/controller/sharing/Setting.php @@ -0,0 +1,25 @@ +renderSuccess(['setting' => compact('basic')]); + } + +} diff --git a/source/application/api/controller/sharp/Goods.php b/source/application/api/controller/sharp/Goods.php new file mode 100644 index 0000000..3fdfcf5 --- /dev/null +++ b/source/application/api/controller/sharp/Goods.php @@ -0,0 +1,70 @@ +getGoodsListByActiveTimeId($active_time_id); + return $this->renderSuccess(compact('list')); + } + + /** + * 获取活动商品详情 + * @param $active_time_id + * @param $sharp_goods_id + * @return array + * @throws \think\exception\DbException + */ + public function detail($active_time_id, $sharp_goods_id) + { + // 获取秒杀活动商品详情 + $service = new ActiveService; + $data = $service->getyActiveGoodsDetail($active_time_id, $sharp_goods_id); + if ($data === false) { + return $this->renderError($service->getError()); + } + return $this->renderSuccess($data); + } + + /** + * 生成商品海报 + * @param $active_time_id + * @param $sharp_goods_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function poster($active_time_id, $sharp_goods_id) + { + // 获取秒杀活动商品详情 + $service = new ActiveService; + $data = $service->getyActiveGoodsDetail($active_time_id, $sharp_goods_id); + if ($data === false) { + return $this->renderError($service->getError()); + } + // 生成商品海报图 + $Qrcode = new GoodsPoster($data['active'], $data['goods'], $this->getUser(false)); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/sharp/Index.php b/source/application/api/controller/sharp/Index.php new file mode 100644 index 0000000..5b1bda8 --- /dev/null +++ b/source/application/api/controller/sharp/Index.php @@ -0,0 +1,32 @@ +getHallIndex(); + if (empty($data['tabbar'])) { + return $this->renderError('很抱歉,暂无秒杀活动'); + } + return $this->renderSuccess($data); + } +} \ No newline at end of file diff --git a/source/application/api/controller/sharp/Order.php b/source/application/api/controller/sharp/Order.php new file mode 100644 index 0000000..41eeb88 --- /dev/null +++ b/source/application/api/controller/sharp/Order.php @@ -0,0 +1,114 @@ +user = $this->getUser(); + } + + /** + * 秒杀订单结算 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function checkout() + { + // 实例化结算台服务 + $Checkout = new CheckoutModel; + // 订单结算api参数 + $params = $Checkout->setParam($this->getParam([ + 'active_time_id' => 0, + 'sharp_goods_id' => 0, + 'goods_sku_id' => '', + 'goods_num' => 0, + ])); + // 设置并发锁 + $lockId = "sharp_order_{$params['active_time_id']}_{$params['sharp_goods_id']}"; + Lock::lockUp($lockId); + // 获取秒杀商品信息 + $service = new ActiveService; + $goodsList = $service->getCheckoutGoodsList( + $params['active_time_id'], + $params['sharp_goods_id'], + $params['goods_sku_id'], + $params['goods_num'] + ); + if ($goodsList === false) { + Lock::unLock($lockId); + return $this->renderError($service->getError()); + } + // 设置订单来源 + $Checkout->setOrderSource([ + 'source' => OrderSourceEnum::SHARP, + 'source_id' => $params['active_time_id'], + ]) + // 秒杀商品不参与 等级折扣和优惠券折扣 + ->setCheckoutRule([ + 'is_user_grade' => false, + 'is_coupon' => false, + 'is_use_points' => false, + 'is_dealer' => SettingModel::getIsDealer(), + ]); + // 获取订单结算信息 + $orderInfo = $Checkout->onCheckout($this->user, $goodsList); + if ($this->request->isGet()) { + Lock::unLock($lockId); + return $this->renderSuccess($orderInfo); + } + // submit:订单结算提交 + if ($Checkout->hasError()) { + Lock::unLock($lockId); + return $this->renderError($Checkout->getError()); + } + // 创建订单 + if (!$Checkout->createOrder($orderInfo)) { + Lock::unLock($lockId); + return $this->renderError($Checkout->getError() ?: '订单创建失败'); + } + Lock::unLock($lockId); + // 构建微信支付请求 + $payment = $Checkout->onOrderPayment(); + // 支付状态提醒 + return $this->renderSuccess([ + 'order_id' => $Checkout->model['order_id'], // 订单id + 'pay_type' => $params['pay_type'], // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 订单结算提交的参数 + * @param array $define + * @return array + */ + private function getParam($define = []) + { + return array_merge($define, $this->request->param()); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/shop/Order.php b/source/application/api/controller/shop/Order.php new file mode 100644 index 0000000..f5feb08 --- /dev/null +++ b/source/application/api/controller/shop/Order.php @@ -0,0 +1,80 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 核销订单详情 + * @param $order_id + * @param int $order_type + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($order_id, $order_type = OrderTypeEnum::MASTER) + { + // 订单详情 + $model = OrderService::getOrderDetail($order_id, $order_type); + // 验证是否为该门店的核销员 + $clerkModel = ClerkModel::detail(['user_id' => $this->user['user_id']]); + return $this->renderSuccess([ + 'order' => $model, // 订单详情 + 'clerkModel' => $clerkModel, + 'setting' => [ + // 积分名称 + 'points_name' => SettingModel::getPointsName(), + ], + ]); + } + + /** + * 确认核销 + * @param $order_id + * @param int $order_type + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function extract($order_id, $order_type = OrderTypeEnum::MASTER) + { + // 订单详情 + $order = OrderService::getOrderDetail($order_id, $order_type); + // 验证是否为该门店的核销员 + $ClerkModel = ClerkModel::detail(['user_id' => $this->user['user_id']]); + if (!$ClerkModel->checkUser($order['extract_shop_id'])) { + return $this->renderError($ClerkModel->getError()); + } + // 确认核销 + if ($order->verificationOrder($ClerkModel['clerk_id'])) { + return $this->renderSuccess([], '订单核销成功'); + } + return $this->renderError($order->getError() ?: '核销失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/Comment.php b/source/application/api/controller/user/Comment.php new file mode 100644 index 0000000..9cb0165 --- /dev/null +++ b/source/application/api/controller/user/Comment.php @@ -0,0 +1,53 @@ +getUser(); + // 订单信息 + $order = OrderModel::getUserOrderDetail($order_id, $user['user_id']); + // 验证订单是否已完成 + $model = new CommentModel; + if (!$model->checkOrderAllowComment($order)) { + return $this->renderError($model->getError()); + } + // 待评价商品列表 + /* @var \think\Collection $goodsList */ + $goodsList = OrderGoodsModel::getNotCommentGoodsList($order_id); + if ($goodsList->isEmpty()) { + return $this->renderError('该订单没有可评价的商品'); + } + // 提交商品评价 + if ($this->request->isPost()) { + $formData = $this->request->post('formData', '', null); + if ($model->addForOrder($order, $goodsList, $formData)) { + return $this->renderSuccess([], '评价发表成功'); + } + return $this->renderError($model->getError() ?: '评价发表失败'); + } + return $this->renderSuccess(compact('goodsList')); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/Coupon.php b/source/application/api/controller/user/Coupon.php new file mode 100644 index 0000000..92cb8a5 --- /dev/null +++ b/source/application/api/controller/user/Coupon.php @@ -0,0 +1,74 @@ +model = new UserCouponModel; + $this->user = $this->getUser(); + } + + /** + * 优惠券列表 + * @param string $data_type + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function lists($data_type = 'all') + { + $is_use = false; + $is_expire = false; + switch ($data_type) { + case 'not_use': + $is_use = false; + break; + case 'is_use': + $is_use = true; + break; + case 'is_expire': + $is_expire = true; + break; + } + $list = $this->model->getList($this->user['user_id'], $is_use, $is_expire); + return $this->renderSuccess(compact('list')); + } + + /** + * 领取优惠券 + * @param $coupon_id + * @return array + * @throws \think\exception\DbException + */ + public function receive($coupon_id) + { + if ($this->model->receive($this->user, $coupon_id)) { + return $this->renderSuccess([], '领取成功'); + } + return $this->renderError($this->model->getError() ?: '添加失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/Dealer.php b/source/application/api/controller/user/Dealer.php new file mode 100644 index 0000000..7f48e45 --- /dev/null +++ b/source/application/api/controller/user/Dealer.php @@ -0,0 +1,115 @@ +user = $this->getUser(); + // 分销商用户信息 + $this->dealer = DealerUserModel::detail($this->user['user_id']); + // 分销商设置 + $this->setting = Setting::getAll(); + } + + /** + * 分销商中心 + * @return array + */ + public function center() + { + return $this->renderSuccess([ + // 当前是否为分销商 + 'is_dealer' => $this->isDealerUser(), + // 当前用户信息 + 'user' => $this->user, + // 分销商用户信息 + 'dealer' => $this->dealer, + // 背景图 + 'background' => $this->setting['background']['values']['index'], + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + + /** + * 分销商申请状态 + * @param null $referee_id + * @return array + * @throws \think\exception\DbException + */ + public function apply($referee_id = null) + { + // 推荐人昵称 + $referee_name = '平台'; + if ($referee_id > 0 && ($referee = DealerUserModel::detail($referee_id))) { + $referee_name = $referee['user']['nickName']; + } + return $this->renderSuccess([ + // 当前是否为分销商 + 'is_dealer' => $this->isDealerUser(), + // 当前是否在申请中 + 'is_applying' => DealerApplyModel::isApplying($this->user['user_id']), + // 推荐人昵称 + 'referee_name' => $referee_name, + // 背景图 + 'background' => $this->setting['background']['values']['apply'], + // 页面文字 + 'words' => $this->setting['words']['values'], + // 申请协议 + 'license' => $this->setting['license']['values']['license'], + ]); + } + + /** + * 分销商提现信息 + * @return array + */ + public function withdraw() + { + return $this->renderSuccess([ + // 分销商用户信息 + 'dealer' => $this->dealer, + // 结算设置 + 'settlement' => $this->setting['settlement']['values'], + // 背景图 + 'background' => $this->setting['background']['values']['withdraw_apply'], + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + + /** + * 当前用户是否为分销商 + * @return bool + */ + private function isDealerUser() + { + return !!$this->dealer && !$this->dealer['is_delete']; + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/Index.php b/source/application/api/controller/user/Index.php new file mode 100644 index 0000000..ed15f18 --- /dev/null +++ b/source/application/api/controller/user/Index.php @@ -0,0 +1,44 @@ +getUser(false); + // 订单总数 + $model = new OrderModel; + return $this->renderSuccess([ + 'userInfo' => $user, + 'orderCount' => [ + 'payment' => $model->getCount($user, 'payment'), + 'received' => $model->getCount($user, 'received'), + 'comment' => $model->getCount($user, 'comment'), + ], + 'setting' => [ + 'points_name' => SettingModel::getPointsName(), + ], + 'menus' => (new UserModel)->getMenus() // 个人中心菜单列表 + ]); + } + +} diff --git a/source/application/api/controller/user/Order.php b/source/application/api/controller/user/Order.php new file mode 100644 index 0000000..9ea27d4 --- /dev/null +++ b/source/application/api/controller/user/Order.php @@ -0,0 +1,179 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 我的订单列表 + * @param $dataType + * @return array + * @throws \think\exception\DbException + */ + public function lists($dataType) + { + $model = new OrderModel; + $list = $model->getList($this->user['user_id'], $dataType); + return $this->renderSuccess(compact('list')); + } + + /** + * 订单详情信息 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function detail($order_id) + { + // 订单详情 + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 该订单是否允许申请售后 + $model['isAllowRefund'] = $model->isAllowRefund(); + return $this->renderSuccess([ + 'order' => $model, // 订单详情 + 'setting' => [ + // 积分名称 + 'points_name' => SettingModel::getPointsName(), + ], + ]); + } + + /** + * 获取物流信息 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function express($order_id) + { + // 订单信息 + $order = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if (!$order['express_no']) { + return $this->renderError('没有物流信息'); + } + // 获取物流信息 + /* @var \app\store\model\Express $model */ + $model = $order['express']; + $express = $model->dynamic($model['express_name'], $model['express_code'], $order['express_no']); + if ($express === false) { + return $this->renderError($model->getError()); + } + return $this->renderSuccess(compact('express')); + } + + /** + * 取消订单 + * @param $order_id + * @return array + * @throws \Exception + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function cancel($order_id) + { + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if ($model->cancel($this->user)) { + return $this->renderSuccess($model->getError() ?: '订单取消成功'); + } + return $this->renderError($model->getError() ?: '订单取消失败'); + } + + /** + * 确认收货 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function receipt($order_id) + { + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + if ($model->receipt()) { + return $this->renderSuccess(); + } + return $this->renderError($model->getError()); + } + + /** + * 立即支付 + * @param int $order_id 订单id + * @param int $payType 支付方式 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function pay($order_id, $payType = PayTypeEnum::WECHAT) + { + // 获取订单详情 + $model = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 订单支付事件 + if (!$model->onPay($payType)) { + return $this->renderError($model->getError() ?: '订单支付失败'); + } + // 构建微信支付请求 + $payment = $model->onOrderPayment($this->user, $model, $payType); + // 支付状态提醒 + return $this->renderSuccess([ + 'order_id' => $model['order_id'], // 订单id + 'pay_type' => $payType, // 支付方式 + 'payment' => $payment // 微信支付参数 + ], ['success' => '支付成功', 'error' => '订单未支付']); + } + + /** + * 获取订单核销二维码 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function extractQrcode($order_id) + { + // 订单详情 + $order = OrderModel::getUserOrderDetail($order_id, $this->user['user_id']); + // 判断是否为待核销订单 + if (!$order->checkExtractOrder($order)) { + return $this->renderError($order->getError()); + } + $Qrcode = new ExtractQRcode( + $this->wxapp_id, + $this->user, + $order_id, + OrderTypeEnum::MASTER + ); + return $this->renderSuccess([ + 'qrcode' => $Qrcode->getImage(), + ]); + } + +} diff --git a/source/application/api/controller/user/Refund.php b/source/application/api/controller/user/Refund.php new file mode 100644 index 0000000..563d58b --- /dev/null +++ b/source/application/api/controller/user/Refund.php @@ -0,0 +1,109 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 用户售后单列表 + * @param int $state + * @return array + * @throws \think\exception\DbException + */ + public function lists($state = -1) + { + $model = new OrderRefundModel; + $list = $model->getList($this->user['user_id'], (int)$state); + return $this->renderSuccess(compact('list')); + } + + /** + * 申请售后 + * @param $order_goods_id + * @return array + * @throws \think\exception\DbException + * @throws \Exception + */ + public function apply($order_goods_id) + { + // 订单商品详情 + $goods = OrderGoodsModel::detail($order_goods_id); + if (isset($goods['refund']) && !empty($goods['refund'])) { + return $this->renderError('当前商品已申请售后'); + } + if (!$this->request->isPost()) { + return $this->renderSuccess(['detail' => $goods]); + } + // 新增售后单记录 + $model = new OrderRefundModel; + if ($model->apply($this->user, $goods, $this->request->post())) { + return $this->renderSuccess([], '提交成功'); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + + /** + * 售后单详情 + * @param $order_refund_id + * @return array + * @throws \think\exception\DbException + */ + public function detail($order_refund_id) + { + // 售后单详情 + $detail = OrderRefundModel::detail([ + 'user_id' => $this->user['user_id'], + 'order_refund_id' => $order_refund_id + ]); + if (empty($detail)) { + return $this->renderError('售后单不存在'); + } + // 物流公司列表 + $expressList = ExpressModel::getAll(); + return $this->renderSuccess(compact('detail', 'expressList')); + } + + /** + * 用户发货 + * @param $order_refund_id + * @return array + * @throws \think\exception\DbException + */ + public function delivery($order_refund_id) + { + // 售后单详情 + $model = OrderRefundModel::detail([ + 'user_id' => $this->user['user_id'], + 'order_refund_id' => $order_refund_id + ]); + if ($model->delivery($this->postData())) { + return $this->renderSuccess([], '操作成功'); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/Wallet.php b/source/application/api/controller/user/Wallet.php new file mode 100644 index 0000000..99c9c50 --- /dev/null +++ b/source/application/api/controller/user/Wallet.php @@ -0,0 +1,44 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 我的钱包信息 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function index() + { + // 当前用户信息 + $user = $this->getUser(); + // 获取充值设置 + $setting = SettingModel::getItem('recharge'); + return $this->renderSuccess([ + 'userInfo' => $user, + 'setting' => [ + 'is_entrance' => (bool)$setting['is_entrance'] + ] + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/dealer/Apply.php b/source/application/api/controller/user/dealer/Apply.php new file mode 100644 index 0000000..485acd2 --- /dev/null +++ b/source/application/api/controller/user/dealer/Apply.php @@ -0,0 +1,46 @@ +user = $this->getUser(); // 用户信息 + } + + /** + * 提交分销商申请 + * @param string $name + * @param string $mobile + * @return array + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function submit($name = '', $mobile = '') + { + $model = new DealerApplyModel; + if ($model->submit($this->user, $name, $mobile)) { + return $this->renderSuccess(); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/dealer/Order.php b/source/application/api/controller/user/dealer/Order.php new file mode 100644 index 0000000..f3c6421 --- /dev/null +++ b/source/application/api/controller/user/dealer/Order.php @@ -0,0 +1,56 @@ +user = $this->getUser(); + // 分销商用户信息 + $this->dealer = DealerUserModel::detail($this->user['user_id']); + // 分销商设置 + $this->setting = Setting::getAll(); + } + + /** + * 分销商订单列表 + * @param int $settled + * @return array + * @throws \think\exception\DbException + */ + public function lists($settled = -1) + { + $model = new OrderModel; + return $this->renderSuccess([ + // 提现明细列表 + 'list' => $model->getList($this->user['user_id'], (int)$settled), + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/dealer/Qrcode.php b/source/application/api/controller/user/dealer/Qrcode.php new file mode 100644 index 0000000..bbf2693 --- /dev/null +++ b/source/application/api/controller/user/dealer/Qrcode.php @@ -0,0 +1,57 @@ +user = $this->getUser(); + // 分销商用户信息 + $this->dealer = DealerUserModel::detail($this->user['user_id']); + // 分销商设置 + $this->setting = Setting::getAll(); + } + + /** + * 获取推广二维码 + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function poster() + { + $Qrcode = new Poster($this->dealer); + return $this->renderSuccess([ + // 二维码图片地址 + 'qrcode' => $Qrcode->getImage(), + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/dealer/Team.php b/source/application/api/controller/user/dealer/Team.php new file mode 100644 index 0000000..5717d89 --- /dev/null +++ b/source/application/api/controller/user/dealer/Team.php @@ -0,0 +1,60 @@ +user = $this->getUser(); + // 分销商用户信息 + $this->dealer = DealerUserModel::detail($this->user['user_id']); + // 分销商设置 + $this->setting = Setting::getAll(); + } + + /** + * 我的团队列表 + * @param int $level + * @return array + * @throws \think\exception\DbException + */ + public function lists($level = -1) + { + $model = new RefereeModel; + return $this->renderSuccess([ + // 分销商用户信息 + 'dealer' => $this->dealer, + // 我的团队列表 + 'list' => $model->getList($this->user['user_id'], (int)$level), + // 基础设置 + 'setting' => $this->setting['basic']['values'], + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/user/dealer/Withdraw.php b/source/application/api/controller/user/dealer/Withdraw.php new file mode 100644 index 0000000..99e9011 --- /dev/null +++ b/source/application/api/controller/user/dealer/Withdraw.php @@ -0,0 +1,72 @@ +user = $this->getUser(); + // 分销商用户信息 + $this->dealer = DealerUserModel::detail($this->user['user_id']); + // 分销商设置 + $this->setting = Setting::getAll(); + } + + /** + * 提交提现申请 + * @param $data + * @return array + * @throws \app\common\exception\BaseException + */ + public function submit($data) + { + $formData = json_decode(htmlspecialchars_decode($data), true); + $model = new WithdrawModel; + if ($model->submit($this->dealer, $formData)) { + return $this->renderSuccess([], '申请提现成功'); + } + return $this->renderError($model->getError() ?: '提交失败'); + } + + /** + * 分销商提现明细 + * @param int $status + * @return array + * @throws \think\exception\DbException + */ + public function lists($status = -1) + { + $model = new WithdrawModel; + return $this->renderSuccess([ + // 提现明细列表 + 'list' => $model->getList($this->user['user_id'], (int)$status), + // 页面文字 + 'words' => $this->setting['words']['values'], + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/controller/wxapp/Formid.php b/source/application/api/controller/wxapp/Formid.php new file mode 100644 index 0000000..a6471d2 --- /dev/null +++ b/source/application/api/controller/wxapp/Formid.php @@ -0,0 +1,33 @@ +getUser(false)) { + return $this->renderSuccess(); + } + if (FormidModel::add($user['user_id'], $formId)) { + return $this->renderSuccess(); + } + return $this->renderError(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/Article.php b/source/application/api/model/Article.php new file mode 100644 index 0000000..3cfeb87 --- /dev/null +++ b/source/application/api/model/Article.php @@ -0,0 +1,88 @@ + '文章不存在']); + } + // 累积阅读数 + $model->setInc('actual_views', 1); + return $model; + } + + /** + * 获取文章列表 + * @param int $category_id + * @param int $limit + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($category_id = 0, $limit = 15) + { + $category_id > 0 && $this->where('category_id', '=', $category_id); + return $this->field(['article_content'], true) + ->with(['image', 'category']) + ->where('article_status', '=', 1) + ->where('is_delete', '=', 0) + ->order(['article_sort' => 'asc', 'create_time' => 'desc']) + ->paginate($limit, false, [ + 'query' => \request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/Cart.php b/source/application/api/model/Cart.php new file mode 100644 index 0000000..c6b8563 --- /dev/null +++ b/source/application/api/model/Cart.php @@ -0,0 +1,292 @@ +user = $user; + $this->user_id = $this->user['user_id']; + $this->wxapp_id = $this->user['wxapp_id']; + $this->cart = Cache::get('cart_' . $this->user_id) ?: []; + } + + /** + * 购物车列表 (含商品信息) + * @return array + * @param string $cartIds 请求参数 + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getList($cartIds) + { + // 获取购物车商品列表 + return $this->getOrderGoodsList($cartIds); + } + + /** + * 获取购物车列表 + * @param string|null $cartIds 购物车索引集 (为null时则获取全部) + * @return array + */ + public function getCartList($cartIds = null) + { + if (empty($cartIds)) return $this->cart; + $cartList = []; + $indexArr = (strpos($cartIds, ',') !== false) ? explode(',', $cartIds) : [$cartIds]; + foreach ($indexArr as $index) { + isset($this->cart[$index]) && $cartList[$index] = $this->cart[$index]; + } + return $cartList; + } + + /** + * 获取购物车中的商品列表 + * @param $cartIds + * @return array|bool + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getOrderGoodsList($cartIds) + { + // 购物车商品列表 + $goodsList = []; + // 获取购物车列表 + $cartList = $this->getCartList($cartIds); + if (empty($cartList)) { + $this->setError('当前购物车没有商品'); + return $goodsList; + } + // 购物车中所有商品id集 + $goodsIds = array_unique(helper::getArrayColumn($cartList, 'goods_id')); + // 获取并格式化商品数据 + $sourceData = (new GoodsModel)->getListByIds($goodsIds); + $sourceData = helper::arrayColumn2Key($sourceData, 'goods_id'); + // 格式化购物车数据列表 + foreach ($cartList as $key => $item) { + // 判断商品不存在则自动删除 + if (!isset($sourceData[$item['goods_id']])) { + $this->delete($key); + continue; + } + /* @var GoodsModel $goods 商品信息 */ + $goods = clone $sourceData[$item['goods_id']]; + // 判断商品是否已删除 + if ($goods['is_delete']) { + $this->delete($key); + continue; + } + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $item['goods_sku_id']); + $goods['goods_sku_id'] = $item['goods_sku_id']; + $goods['spec_sku_id'] = $goods['goods_sku']['spec_sku_id']; + // 商品sku不存在则自动删除 + if (empty($goods['goods_sku'])) { + $this->delete($key); + continue; + } + // 商品单价 + $goods['goods_price'] = $goods['goods_sku']['goods_price']; + // 购买数量 + $goods['total_num'] = $item['goods_num']; + // 商品总价 + $goods['total_price'] = bcmul($goods['goods_price'], $item['goods_num'], 2); + $goodsList[] = $goods->hidden(['category', 'content', 'image', 'sku']); + } + return $goodsList; + } + + /** + * 加入购物车 + * @param int $goodsId 商品id + * @param int $goodsNum 加入购物车的数量 + * @param string $goodsSkuId 商品sku索引 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function add($goodsId, $goodsNum, $goodsSkuId) + { + // 购物车商品索引 + $index = "{$goodsId}_{$goodsSkuId}"; + // 加入购物车后的商品数量 + $cartGoodsNum = $goodsNum + (isset($this->cart[$index]) ? $this->cart[$index]['goods_num'] : 0); + // 获取商品信息 + $goods = GoodsModel::detail($goodsId); + // 验证商品能否加入 + if (!$this->checkGoods($goods, $goodsSkuId, $cartGoodsNum)) { + return false; + } + // 将商品同步到好物圈 + if (!$this->isExistGoodsId($goodsId)) { + (new WowService($this->wxapp_id))->add($this->user, [$goods]); + } + // 记录到购物车列表 + $this->cart[$index] = [ + 'goods_id' => $goodsId, + 'goods_num' => $cartGoodsNum, + 'goods_sku_id' => $goodsSkuId, + 'create_time' => time() + ]; + return true; + } + + /** + * 验证购物车中是否存在某商品 + * @param $goodsId + * @return bool + */ + private function isExistGoodsId($goodsId) + { + foreach ($this->cart as $item) { + if ($item['goods_id'] == $goodsId) return true; + } + return false; + } + + /** + * 验证商品是否可以购买 + * @param GoodsModel $goods 商品信息 + * @param string $goodsSkuId 商品sku索引 + * @param $cartGoodsNum + * @return bool + */ + private function checkGoods($goods, $goodsSkuId, $cartGoodsNum) + { + // 判断商品是否下架 + if (!$goods || $goods['is_delete'] || $goods['goods_status']['value'] != 10) { + $this->setError('很抱歉,商品信息不存在或已下架'); + return false; + } + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $goodsSkuId); + // 判断商品库存 + if ($cartGoodsNum > $goods['goods_sku']['stock_num']) { + $this->setError('很抱歉,商品库存不足'); + return false; + } + return true; + } + + /** + * 减少购物车中某商品数量 + * @param int $goodsId + * @param string $goodsSkuId + */ + public function sub($goodsId, $goodsSkuId) + { + $index = "{$goodsId}_{$goodsSkuId}"; + $this->cart[$index]['goods_num'] > 1 && $this->cart[$index]['goods_num']--; + } + + /** + * 删除购物车中指定商品 + * @param string $cartIds (支持字符串ID集) + */ + public function delete($cartIds) + { + $indexArr = strpos($cartIds, ',') !== false ? explode(',', $cartIds) : [$cartIds]; + foreach ($indexArr as $index) { + if (isset($this->cart[$index])) unset($this->cart[$index]); + } + } + + /** + * 获取当前用户购物车商品总数量(含件数) + * @return int + */ + public function getTotalNum() + { + return helper::getArrayColumnSum($this->cart, 'goods_num'); + } + + /** + * 获取当前用户购物车商品总数量(不含件数) + * @return int + */ + public function getGoodsNum() + { + return count($this->cart); + } + + /** + * 析构方法 + * 将cart数据保存到缓存文件 + */ + public function __destruct() + { + $this->clear !== true && Cache::set('cart_' . $this->user_id, $this->cart, 86400 * 15); + } + + /** + * 清空当前用户购物车 + * @param null $cartIds + */ + public function clearAll($cartIds = null) + { + if (empty($cartIds)) { + $this->clear = true; + Cache::rm('cart_' . $this->user_id); + } else { + $this->delete($cartIds); + } + } + + /** + * 设置错误信息 + * @param $error + */ + private function setError($error) + { + empty($this->error) && $this->error = $error; + } + + /** + * 获取错误信息 + * @return string + */ + public function getError() + { + return $this->error; + } + +} diff --git a/source/application/api/model/Category.php b/source/application/api/model/Category.php new file mode 100644 index 0000000..7130a97 --- /dev/null +++ b/source/application/api/model/Category.php @@ -0,0 +1,28 @@ +belongsTo('User')->field(['user_id', 'nickName', 'avatarUrl']); + } + + /** + * 获取指定商品评价列表 + * @param $goods_id + * @param int $scoreType + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getGoodsCommentList($goods_id, $scoreType = -1) + { + // 筛选条件 + $filter = [ + 'goods_id' => $goods_id, + 'is_delete' => 0, + 'status' => 1, + ]; + // 评分 + $scoreType > 0 && $filter['score'] = $scoreType; + return $this->with(['user', 'OrderGoods', 'image.file']) + ->where($filter) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 获取指定评分总数 + * @param $goods_id + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getTotal($goods_id) + { + return $this->field([ + 'count(comment_id) AS `all`', + 'count(score = 10 OR NULL) AS `praise`', + 'count(score = 20 OR NULL) AS `review`', + 'count(score = 30 OR NULL) AS `negative`', + ])->where([ + 'goods_id' => $goods_id, + 'is_delete' => 0, + 'status' => 1 + ])->find(); + } + + /** + * 验证订单是否允许评价 + * @param Order $order + * @return boolean + */ + public function checkOrderAllowComment($order) + { + // 验证订单是否已完成 + if ($order['order_status']['value'] != 30) { + $this->error = '该订单未完成,无法评价'; + return false; + } + // 验证订单是否已评价 + if ($order['is_comment'] == 1) { + $this->error = '该订单已完成评价'; + return false; + } + return true; + } + + /** + * 根据已完成订单商品 添加评价 + * @param Order $order + * @param \think\Collection|OrderGoods $goodsList + * @param $formJsonData + * @return boolean + * @throws \Exception + */ + public function addForOrder($order, $goodsList, $formJsonData) + { + // 生成 formData + $formData = $this->formatFormData($formJsonData); + // 生成评价数据 + $data = $this->createCommentData($order['user_id'], $order['order_id'], $goodsList, $formData); + if (empty($data)) { + $this->error = '没有输入评价内容'; + return false; + } + return $this->transaction(function () use ($order, $goodsList, $formData, $data) { + // 记录评价内容 + $result = $this->isUpdate(false)->saveAll($data); + // 记录评价图片 + $this->saveAllImages($result, $formData); + // 更新订单评价状态 + $isComment = count($goodsList) === count($data); + $this->updateOrderIsComment($order, $isComment, $result); + return true; + }); + } + + /** + * 更新订单评价状态 + * @param Order $order + * @param $isComment + * @param $commentList + * @return array|false + * @throws \Exception + */ + private function updateOrderIsComment($order, $isComment, $commentList) + { + // 更新订单商品 + $orderGoodsData = []; + foreach ($commentList as $comment) { + $orderGoodsData[] = [ + 'order_goods_id' => $comment['order_goods_id'], + 'is_comment' => 1 + ]; + } + // 更新订单 + $isComment && $order->save(['is_comment' => 1]); + return (new OrderGoods)->saveAll($orderGoodsData); + } + + /** + * 生成评价数据 + * @param $user_id + * @param $order_id + * @param $goodsList + * @param $formData + * @return array + * @throws BaseException + */ + private function createCommentData($user_id, $order_id, $goodsList, $formData) + { + $data = []; + foreach ($goodsList as $goods) { + if (!isset($formData[$goods['order_goods_id']])) { + throw new BaseException(['msg' => '提交的数据不合法']); + } + $item = $formData[$goods['order_goods_id']]; + !empty($item['content']) && $data[$goods['order_goods_id']] = [ + 'score' => $item['score'], + 'content' => $item['content'], + 'is_picture' => !empty($item['uploaded']), + 'sort' => 100, + 'status' => 1, + 'user_id' => $user_id, + 'order_id' => $order_id, + 'goods_id' => $item['goods_id'], + 'order_goods_id' => $item['order_goods_id'], + 'wxapp_id' => self::$wxapp_id + ]; + } + return $data; + } + + /** + * 格式化 formData + * @param string $formJsonData + * @return array + */ + private function formatFormData($formJsonData) + { + return array_column(json_decode($formJsonData, true), null, 'order_goods_id'); + } + + /** + * 记录评价图片 + * @param $commentList + * @param $formData + * @return bool + * @throws \Exception + */ + private function saveAllImages($commentList, $formData) + { + // 生成评价图片数据 + $imageData = []; + foreach ($commentList as $comment) { + $item = $formData[$comment['order_goods_id']]; + foreach ($item['uploaded'] as $imageId) { + $imageData[] = [ + 'comment_id' => $comment['comment_id'], + 'image_id' => $imageId, + 'wxapp_id' => self::$wxapp_id + ]; + } + } + $model = new CommentImage; + return !empty($imageData) && $model->saveAll($imageData); + } + +} diff --git a/source/application/api/model/CommentImage.php b/source/application/api/model/CommentImage.php new file mode 100644 index 0000000..cdb00ac --- /dev/null +++ b/source/application/api/model/CommentImage.php @@ -0,0 +1,23 @@ +where('is_delete', '=', 0); + // 只显示可领取(未过期,未发完)的优惠券 + if ($only_receive) { + $this->where(' IF ( `total_num` > - 1, `receive_num` < `total_num`, 1 = 1 )') + ->where('IF ( `expire_type` = 20, (`end_time` + 86400) >= ' . time() . ', 1 = 1 )'); + } + + // 优惠券列表 + $couponList = $this->order(['sort' => 'asc', 'create_time' => 'desc'])->limit($limit)->select(); + + // 获取用户已领取的优惠券 + if ($user !== false) { + $UserCouponModel = new UserCoupon; + $userCouponIds = $UserCouponModel->getUserCouponIds($user['user_id']); + foreach ($couponList as $key => $item) { + $couponList[$key]['is_receive'] = in_array($item['coupon_id'], $userCouponIds); + } + } + return $couponList; + } + + /** + * 验证优惠券是否可领取 + * @return bool + */ + public function checkReceive() + { + if ($this['total_num'] > -1 && $this['receive_num'] >= $this['total_num']) { + $this->error = '优惠券已发完'; + return false; + } + if ($this['expire_type'] == 20 && ($this->getData('end_time') + 86400) < time()) { + $this->error = '优惠券已过期'; + return false; + } + return true; + } + + /** + * 累计已领取数量 + * @return int|true + * @throws \think\Exception + */ + public function setIncReceiveNum() + { + return $this->setInc('receive_num'); + } + +} diff --git a/source/application/api/model/Delivery.php b/source/application/api/model/Delivery.php new file mode 100644 index 0000000..e2baec7 --- /dev/null +++ b/source/application/api/model/Delivery.php @@ -0,0 +1,24 @@ +isEmpty() && $data->hidden(['category', 'content', 'image', 'sku']); + // 整理列表数据并返回 + return $this->setGoodsListDataFromApi($data, true, ['userInfo' => $userInfo]); + } + + /** + * 商品详情 + * @param $goodsId + * @return GoodsModel + */ + public static function detail($goodsId) + { + // 商品详情 + $detail = parent::detail($goodsId); + // 多规格商品sku信息 + $detail['goods_multi_spec'] = GoodsService::getSpecData($detail); + return $detail; + } + + /** + * 获取商品详情页面 + * @param int $goodsId 商品id + * @param array|bool $userInfo 用户信息 + * @return array|bool|false|mixed|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getDetails($goodsId, $userInfo = false) + { + // 获取商品详情 + $detail = $this->with([ + 'category', + 'image' => ['file'], + 'sku' => ['image'], + 'spec_rel' => ['spec'], + 'delivery' => ['rule'], + 'commentData' => function ($query) { + $query->with('user')->where(['is_delete' => 0, 'status' => 1])->limit(2); + } + ])->withCount(['commentData' => function ($query) { + $query->where(['is_delete' => 0, 'status' => 1]); + }]) + ->where('goods_id', '=', $goodsId) + ->find(); + // 判断商品的状态 + if (empty($detail) || $detail['is_delete'] || $detail['goods_status']['value'] != 10) { + $this->error = '很抱歉,商品信息不存在或已下架'; + return false; + } + // 设置商品展示的数据 + $detail = $this->setGoodsListDataFromApi($detail, false, ['userInfo' => $userInfo]); + // 多规格商品sku信息 + $detail['goods_multi_spec'] = GoodsService::getSpecData($detail); + return $detail; + } + + /** + * 根据商品id集获取商品列表 + * @param $goodsIds + * @param bool $userInfo + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIdsFromApi($goodsIds, $userInfo = false) + { + // 获取商品列表 + $data = parent::getListByIds($goodsIds, 10); + // 整理列表数据并返回 + return $this->setGoodsListDataFromApi($data, true, ['userInfo' => $userInfo]); + } + + + /** + * 设置商品展示的数据 api模块 + * @param $data + * @param bool $isMultiple + * @param array $param + * @return mixed + */ + private function setGoodsListDataFromApi(&$data, $isMultiple, $param) + { + return parent::setGoodsListData($data, $isMultiple, function ($goods) use ($param) { + // 计算并设置商品会员价 + $this->setGoodsGradeMoney($param['userInfo'], $goods); + }); + } + + /** + * 设置商品的会员价 + * @param $user + * @param $goods + */ + private function setGoodsGradeMoney($user, &$goods) + { + // 会员等级状态 + $gradeStatus = (!empty($user) && $user['grade_id'] > 0 && !empty($user['grade'])) + && (!$user['grade']['is_delete'] && $user['grade']['status']); + // 判断商品是否参与会员折扣 + if (!$gradeStatus || !$goods['is_enable_grade']) { + $goods['is_user_grade'] = false; + return; + } + // 商品单独设置了会员折扣 + if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$user['grade_id']])) { + // 折扣比例 + $discountRatio = helper::bcdiv($goods['alone_grade_equity'][$user['grade_id']], 10); + } else { + // 折扣比例 + $discountRatio = helper::bcdiv($user['grade']['equity']['discount'], 10); + } + if ($discountRatio > 0) { + // 标记参与会员折扣 + $goods['is_user_grade'] = true; + // 会员折扣价 + foreach ($goods['sku'] as &$skuItem) { + $skuItem['goods_price'] = helper::number2(helper::bcmul($skuItem['goods_price'], $discountRatio), true); + } + } + } + +} diff --git a/source/application/api/model/GoodsImage.php b/source/application/api/model/GoodsImage.php new file mode 100644 index 0000000..7c4a7c6 --- /dev/null +++ b/source/application/api/model/GoodsImage.php @@ -0,0 +1,23 @@ + $orderNo, 'pay_status' => 10, 'is_delete' => 0], ['goods', 'user']); + } + + /** + * 订单支付事件 + * @param int $payType + * @return bool + * @throws \think\exception\DbException + */ + public function onPay($payType = PayTypeEnum::WECHAT) + { + // 判断订单状态 + $orderSource = OrderSourceFactory::getFactory($this['order_source']); + if (!$orderSource->checkOrderStatusOnPay($this)) { + $this->error = $orderSource->getError(); + return false; + } + // 余额支付 + if ($payType == PayTypeEnum::BALANCE) { + return $this->onPaymentByBalance($this['order_no']); + } + return true; + } + + /** + * 构建支付请求的参数 + * @param $user + * @param $order + * @param $payType + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + public function onOrderPayment($user, $order, $payType) + { + if ($payType == PayTypeEnum::WECHAT) { + return $this->onPaymentByWechat($user, $order); + } + return []; + } + + /** + * 构建微信支付请求 + * @param $user + * @param $order + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + protected function onPaymentByWechat($user, $order) + { + return PaymentService::wechat( + $user, + $order['order_id'], + $order['order_no'], + $order['pay_price'], + OrderTypeEnum::MASTER + ); + } + + /** + * 立即购买:获取订单商品列表 + * @param $goodsId + * @param $goodsSkuId + * @param $goodsNum + * @return array + */ + public function getOrderGoodsListByNow($goodsId, $goodsSkuId, $goodsNum) + { + // 商品详情 + /* @var GoodsModel $goods */ + $goods = GoodsModel::detail($goodsId); + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $goodsSkuId); + // 商品列表 + $goodsList = [$goods->hidden(['category', 'content', 'image', 'sku'])]; + foreach ($goodsList as &$item) { + // 商品单价 + $item['goods_price'] = $item['goods_sku']['goods_price']; + // 商品购买数量 + $item['total_num'] = $goodsNum; + $item['spec_sku_id'] = $item['goods_sku']['spec_sku_id']; + // 商品购买总金额 + $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum); + } + return $goodsList; + } + + /** + * 余额支付标记订单已支付 + * @param $orderNo + * @return bool + * @throws \think\exception\DbException + */ + public function onPaymentByBalance($orderNo) + { + // 获取订单详情 + $PaySuccess = new PaySuccess($orderNo); + // 发起余额支付 + $status = $PaySuccess->onPaySuccess(PayTypeEnum::BALANCE); + if (!$status) { + $this->error = $PaySuccess->getError(); + } + return $status; + } + + /** + * 用户中心订单列表 + * @param $user_id + * @param string $type + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id, $type = 'all') + { + // 筛选条件 + $filter = []; + // 订单数据类型 + switch ($type) { + case 'all': + break; + case 'payment'; + $filter['pay_status'] = PayStatusEnum::PENDING; + $filter['order_status'] = 10; + break; + case 'delivery'; + $filter['pay_status'] = PayStatusEnum::SUCCESS; + $filter['delivery_status'] = 10; + $filter['order_status'] = 10; + break; + case 'received'; + $filter['pay_status'] = PayStatusEnum::SUCCESS; + $filter['delivery_status'] = 20; + $filter['receipt_status'] = 10; + $filter['order_status'] = 10; + break; + case 'comment'; + $filter['is_comment'] = 0; + $filter['order_status'] = 30; + break; + } + return $this->with(['goods.image']) + ->where('user_id', '=', $user_id) + ->where($filter) + ->where('is_delete', '=', 0) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 取消订单 + * @param UserModel $user + * @return bool|mixed + */ + public function cancel($user) + { + if ($this['delivery_status']['value'] == 20) { + $this->error = '已发货订单不可取消'; + return false; + } + // 订单取消事件 + return $this->transaction(function () use ($user) { + // 订单是否已支付 + $isPay = $this['pay_status']['value'] == PayStatusEnum::SUCCESS; + // 未付款的订单 + if ($isPay == false) { + // 回退商品库存 + FactoryStock::getFactory($this['order_source'])->backGoodsStock($this['goods'], $isPay); + // 回退用户优惠券 + $this['coupon_id'] > 0 && UserCouponModel::setIsUse($this['coupon_id'], false); + // 回退用户积分 + $describe = "订单取消:{$this['order_no']}"; + $this['points_num'] > 0 && $user->setIncPoints($this['points_num'], $describe); + } + // 更新订单状态 + return $this->save(['order_status' => $isPay ? OrderStatusEnum::APPLY_CANCEL : OrderStatusEnum::CANCELLED]); + }); + } + + /** + * 确认收货 + * @return bool|mixed + */ + public function receipt() + { + // 验证订单是否合法 + // 条件1: 订单必须已发货 + // 条件2: 订单必须未收货 + if ($this['delivery_status']['value'] != 20 || $this['receipt_status']['value'] != 10) { + $this->error = '该订单不合法'; + return false; + } + return $this->transaction(function () { + // 更新订单状态 + $status = $this->save([ + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); +// // 获取已完成的订单 +// $completed = self::detail($this['order_id'], [ +// 'user', 'address', 'goods', 'express', // 用于好物圈 +// ]); + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::MASTER); + $OrderCompleteService->complete([$this], static::$wxapp_id); + return $status; + }); + } + + /** + * 获取订单总数 + * @param $user + * @param string $type + * @return int|string + * @throws \think\Exception + */ + public function getCount($user, $type = 'all') + { + if ($user === false) { + return false; + } + // 筛选条件 + $filter = []; + // 订单数据类型 + switch ($type) { + case 'all': + break; + case 'payment'; + $filter['pay_status'] = PayStatusEnum::PENDING; + break; + case 'received'; + $filter['pay_status'] = PayStatusEnum::SUCCESS; + $filter['delivery_status'] = 20; + $filter['receipt_status'] = 10; + break; + case 'comment'; + $filter['order_status'] = 30; + $filter['is_comment'] = 0; + break; + } + return $this->where('user_id', '=', $user['user_id']) + ->where('order_status', '<>', 20) + ->where($filter) + ->where('is_delete', '=', 0) + ->count(); + } + + /** + * 订单详情 + * @param $order_id + * @param null $user_id + * @return null|static + * @throws BaseException + * @throws \think\exception\DbException + */ + public static function getUserOrderDetail($order_id, $user_id) + { + if (!$order = self::get([ + 'order_id' => $order_id, + 'user_id' => $user_id, + ], [ + 'goods' => ['image', 'goods', 'refund'], + 'address', 'express', 'extract_shop' + ]) + ) { + throw new BaseException(['msg' => '订单不存在']); + } + return $order; + } + + /** + * 判断当前订单是否允许核销 + * @param static $order + * @return bool + */ + public function checkExtractOrder(&$order) + { + if ( + $order['pay_status']['value'] == PayStatusEnum::SUCCESS + && $order['delivery_type']['value'] == DeliveryTypeEnum::EXTRACT + && $order['delivery_status']['value'] == 10 + ) { + return true; + } + $this->setError('该订单不能被核销'); + return false; + } + + /** + * 当前订单是否允许申请售后 + * @return bool + */ + public function isAllowRefund() + { + // 必须是已发货的订单 + if ($this['delivery_status']['value'] != 20) { + return false; + } + // 允许申请售后期限(天) + $refundDays = SettingModel::getItem('trade')['order']['refund_days']; + // 不允许售后 + if ($refundDays == 0) { + return false; + } + // 当前时间超出允许申请售后期限 + if ( + $this['receipt_status'] == 20 + && time() > ($this['receipt_time'] + ((int)$refundDays * 86400)) + ) { + return false; + } + return true; + } + + /** + * 设置错误信息 + * @param $error + */ + protected function setError($error) + { + empty($this->error) && $this->error = $error; + } + + /** + * 是否存在错误 + * @return bool + */ + public function hasError() + { + return !empty($this->error); + } + +} diff --git a/source/application/api/model/OrderAddress.php b/source/application/api/model/OrderAddress.php new file mode 100644 index 0000000..c78ece2 --- /dev/null +++ b/source/application/api/model/OrderAddress.php @@ -0,0 +1,23 @@ + $order_id, 'is_comment' => 0], ['orderM', 'image']); + } + +} diff --git a/source/application/api/model/OrderRefund.php b/source/application/api/model/OrderRefund.php new file mode 100644 index 0000000..36e73b6 --- /dev/null +++ b/source/application/api/model/OrderRefund.php @@ -0,0 +1,171 @@ + '已同意退货并已退款', 20 => '已同意换货']; + return $text[$data['type']]; + } + // 已取消 + if ($data['status'] == 30) { + return '已取消'; + } + // 已拒绝 + if ($data['status'] == 10) { +// return '已拒绝'; + return $data['type'] == 10 ? '已拒绝退货退款' : '已拒绝换货'; + } + // 进行中 + if ($data['status'] == 0) { + if ($data['is_agree'] == 0) { + return '等待审核中'; + } + if ($data['type'] == 10) { + return $data['is_user_send'] ? '已发货,待平台确认' : '已同意退货,请及时发货'; + } + } + return $value; + } + + /** + * 获取用户售后单列表 + * @param $user_id + * @param int $state + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id, $state = -1) + { + $state > -1 && $this->where('status', '=', $state); + return $this->with(['order_master', 'order_goods.image']) + ->where('user_id', '=', $user_id) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 用户发货 + * @param $data + * @return false|int + */ + public function delivery($data) + { + if ( + $this['type']['value'] != 10 + || $this['is_agree']['value'] != 10 + || $this['is_user_send'] != 0 + ) { + $this->error = '当前售后单不合法,不允许该操作'; + return false; + } + if ($data['express_id'] <= 0) { + $this->error = '请选择物流公司'; + return false; + } + if (empty($data['express_no'])) { + $this->error = '请填写物流单号'; + return false; + } + return $this->save([ + 'is_user_send' => 1, + 'send_time' => time(), + 'express_id' => (int)$data['express_id'], + 'express_no' => $data['express_no'], + ]); + } + + /** + * 新增售后单记录 + * @param $user + * @param $goods + * @param $data + * @return bool + * @throws \Exception + */ + public function apply($user, $goods, $data) + { + $this->startTrans(); + try { + // 新增售后单记录 + $this->save([ + 'order_goods_id' => $data['order_goods_id'], + 'order_id' => $goods['order_id'], + 'user_id' => $user['user_id'], + 'type' => $data['type'], + 'apply_desc' => $data['content'], + 'is_agree' => 0, + 'status' => 0, + 'wxapp_id' => self::$wxapp_id, + ]); + // 记录凭证图片关系 + if (isset($data['images']) && !empty($data['images'])) { + $this->saveImages($this['order_refund_id'], $data['images']); + } + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 记录售后单图片 + * @param $order_refund_id + * @param $images + * @return bool + * @throws \Exception + */ + private function saveImages($order_refund_id, $images) + { + // 生成评价图片数据 + $data = []; + foreach (explode(',', $images) as $image_id) { + $data[] = [ + 'order_refund_id' => $order_refund_id, + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + } + return !empty($data) && (new OrderRefundImage)->saveAll($data); + } + +} \ No newline at end of file diff --git a/source/application/api/model/OrderRefundAddress.php b/source/application/api/model/OrderRefundAddress.php new file mode 100644 index 0000000..f9bfdff --- /dev/null +++ b/source/application/api/model/OrderRefundAddress.php @@ -0,0 +1,23 @@ + $openId], ['address', 'addressDefault', 'grade']); + } + + /** + * 用户登录 + * @param array $post + * @return string + * @throws BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function login($post) + { + // 微信登录 获取session_key + $session = $this->wxlogin($post['code']); + // 自动注册用户 + $refereeId = isset($post['referee_id']) ? $post['referee_id'] : null; + $userInfo = json_decode(htmlspecialchars_decode($post['user_info']), true); + $user_id = $this->register($session['openid'], $userInfo, $refereeId); + // 生成token (session3rd) + $this->token = $this->token($session['openid']); + // 记录缓存, 7天 + Cache::set($this->token, $session, 86400 * 7); + return $user_id; + } + + /** + * 获取token + * @return mixed + */ + public function getToken() + { + return $this->token; + } + + /** + * 微信登录 + * @param $code + * @return array|mixed + * @throws BaseException + * @throws \think\exception\DbException + */ + private function wxlogin($code) + { + // 获取当前小程序信息 + $wxConfig = Wxapp::getWxappCache(); + // 验证appid和appsecret是否填写 + if (empty($wxConfig['app_id']) || empty($wxConfig['app_secret'])) { + throw new BaseException(['msg' => '请到 [后台-小程序设置] 填写appid 和 appsecret']); + } + // 微信登录 (获取session_key) + $WxUser = new WxUser($wxConfig['app_id'], $wxConfig['app_secret']); + if (!$session = $WxUser->sessionKey($code)) { + throw new BaseException(['msg' => $WxUser->getError()]); + } + return $session; + } + + /** + * 生成用户认证的token + * @param $openid + * @return string + */ + private function token($openid) + { + $wxapp_id = self::$wxapp_id; + // 生成一个不会重复的随机字符串 + $guid = \getGuidV4(); + // 当前时间戳 (精确到毫秒) + $timeStamp = microtime(true); + // 自定义一个盐 + $salt = 'token_salt'; + return md5("{$wxapp_id}_{$timeStamp}_{$openid}_{$guid}_{$salt}"); + } + + /** + * 自动注册用户 + * @param $open_id + * @param $data + * @param int $refereeId + * @return mixed + * @throws \Exception + * @throws \think\exception\DbException + */ + private function register($open_id, $data, $refereeId = null) + { + // 查询用户是否已存在 + $user = self::detail(['open_id' => $open_id]); + $model = $user ?: $this; + $this->startTrans(); + try { + // 保存/更新用户记录 + if (!$model->allowField(true)->save(array_merge($data, [ + 'open_id' => $open_id, + 'wxapp_id' => self::$wxapp_id + ]))) { + throw new BaseException(['msg' => '用户注册失败']); + } + // 记录推荐人关系 + if (!$user && $refereeId > 0) { + RefereeModel::createRelation($model['user_id'], $refereeId); + } + $this->commit(); + } catch (\Exception $e) { + $this->rollback(); + throw new BaseException(['msg' => $e->getMessage()]); + } + return $model['user_id']; + } + + /** + * 个人中心菜单列表 + * @return array + */ + public function getMenus() + { + $menus = [ + 'address' => [ + 'name' => '收货地址', + 'url' => 'pages/address/index', + 'icon' => 'map' + ], + 'coupon' => [ + 'name' => '领券中心', + 'url' => 'pages/coupon/coupon', + 'icon' => 'lingquan' + ], + 'my_coupon' => [ + 'name' => '我的优惠券', + 'url' => 'pages/user/coupon/coupon', + 'icon' => 'youhuiquan' + ], + 'sharing_order' => [ + 'name' => '拼团订单', + 'url' => 'pages/sharing/order/index', + 'icon' => 'pintuan' + ], + 'my_bargain' => [ + 'name' => '我的砍价', + 'url' => 'pages/bargain/index/index?tab=1', + 'icon' => 'kanjia' + ], + 'dealer' => [ + 'name' => '分销中心', + 'url' => 'pages/dealer/index/index', + 'icon' => 'fenxiaozhongxin' + ], + 'help' => [ + 'name' => '我的帮助', + 'url' => 'pages/user/help/index', + 'icon' => 'help' + ], + ]; + // 判断分销功能是否开启 + if (DealerSettingModel::isOpen()) { + $menus['dealer']['name'] = DealerSettingModel::getDealerTitle(); + } else { + unset($menus['dealer']); + } + return $menus; + } + +} diff --git a/source/application/api/model/UserAddress.php b/source/application/api/model/UserAddress.php new file mode 100644 index 0000000..8d199c5 --- /dev/null +++ b/source/application/api/model/UserAddress.php @@ -0,0 +1,148 @@ +transaction(function () use ($user, $data) { + // 整理地区信息 + $region = explode(',', $data['region']); + $provinceId = Region::getIdByName($region[0], 1); + $cityId = Region::getIdByName($region[1], 2, $provinceId); + $regionId = Region::getIdByName($region[2], 3, $cityId); + // 验证城市ID是否合法 + if (!$this->checkCityId($cityId)) return false; + // 添加收货地址 + $this->allowField(true)->save([ + 'name' => $data['name'], + 'phone' => $data['phone'], + 'province_id' => $provinceId, + 'city_id' => $cityId, + 'region_id' => $regionId, + 'detail' => $data['detail'], + 'district' => ($regionId === 0 && !empty($region[2])) ? $region[2] : '', + 'user_id' => $user['user_id'], + 'wxapp_id' => self::$wxapp_id + ]); + // 设为默认收货地址 + !$user['address_id'] && $user->save(['address_id' => $this['address_id']]); + return true; + }); + } + + /** + * 编辑收货地址 + * @param $data + * @return false|int + */ + public function edit($data) + { + // 整理地区信息 + $region = explode(',', $data['region']); + $provinceId = Region::getIdByName($region[0], 1); + $cityId = Region::getIdByName($region[1], 2, $provinceId); + $regionId = Region::getIdByName($region[2], 3, $cityId); + // 验证城市ID是否合法 + if (!$this->checkCityId($cityId)) return false; + // 更新收货地址 + return $this->allowField(true)->save([ + 'name' => $data['name'], + 'phone' => $data['phone'], + 'province_id' => $provinceId, + 'city_id' => $cityId, + 'region_id' => $regionId, + 'detail' => $data['detail'], + 'district' => ($regionId === 0 && !empty($region[2])) ? $region[2] : '', + ]) !== false; + } + + /** + * 验证城市ID是否合法 + * @param $cityId + * @return bool + */ + private function checkCityId($cityId) + { + if ($cityId <= 0) { + \log_write([ + 'system_msg' => '选择的城市不存在', + 'param' => \request()->param() + ], 'error'); + $this->error = '很抱歉,您选择的城市不存在'; + return false; + } + return true; + } + + /** + * 设为默认收货地址 + * @param User $user + * @return int + */ + public function setDefault($user) + { + // 设为默认地址 + return $user->save(['address_id' => $this['address_id']]); + } + + /** + * 删除收货地址 + * @param User $user + * @return int + */ + public function remove($user) + { + // 查询当前是否为默认地址 + $user['address_id'] == $this['address_id'] && $user->save(['address_id' => 0]); + return $this->delete(); + } + + /** + * 收货地址详情 + * @param $user_id + * @param $address_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($user_id, $address_id) + { + return self::get(compact('user_id', 'address_id')); + } + +} diff --git a/source/application/api/model/UserCoupon.php b/source/application/api/model/UserCoupon.php new file mode 100644 index 0000000..a4185a3 --- /dev/null +++ b/source/application/api/model/UserCoupon.php @@ -0,0 +1,190 @@ +where('user_id', '=', $user_id) + ->where('is_use', '=', $is_use ? 1 : 0) + ->where('is_expire', '=', $is_expire ? 1 : 0) + ->select(); + } + + /** + * 获取用户优惠券总数量(可用) + * @param $user_id + * @return int|string + * @throws \think\Exception + */ + public function getCount($user_id) + { + return $this->where('user_id', '=', $user_id) + ->where('is_use', '=', 0) + ->where('is_expire', '=', 0) + ->count(); + } + + /** + * 获取用户优惠券ID集 + * @param $user_id + * @return array + */ + public function getUserCouponIds($user_id) + { + return $this->where('user_id', '=', $user_id)->column('coupon_id'); + } + + /** + * 领取优惠券 + * @param $user + * @param $coupon_id + * @return bool|false|int + * @throws \think\exception\DbException + */ + public function receive($user, $coupon_id) + { + // 获取优惠券信息 + $coupon = Coupon::detail($coupon_id); + // 验证优惠券是否可领取 + if (!$this->checkReceive($user, $coupon)) { + return false; + } + // 添加领取记录 + return $this->add($user, $coupon); + } + + /** + * 添加领取记录 + * @param $user + * @param Coupon $coupon + * @return bool + */ + private function add($user, $coupon) + { + // 计算有效期 + if ($coupon['expire_type'] == 10) { + $start_time = time(); + $end_time = $start_time + ($coupon['expire_day'] * 86400); + } else { + $start_time = $coupon['start_time']['value']; + $end_time = $coupon['end_time']['value']; + } + // 整理领取记录 + $data = [ + 'coupon_id' => $coupon['coupon_id'], + 'name' => $coupon['name'], + 'color' => $coupon['color']['value'], + 'coupon_type' => $coupon['coupon_type']['value'], + 'reduce_price' => $coupon['reduce_price'], + 'discount' => $coupon->getData('discount'), + 'min_price' => $coupon['min_price'], + 'expire_type' => $coupon['expire_type'], + 'expire_day' => $coupon['expire_day'], + 'start_time' => $start_time, + 'end_time' => $end_time, + 'apply_range' => $coupon['apply_range'], + 'user_id' => $user['user_id'], + 'wxapp_id' => self::$wxapp_id + ]; + return $this->transaction(function () use ($data, $coupon) { + // 添加领取记录 + $status = $this->save($data); + if ($status) { + // 更新优惠券领取数量 + $coupon->setIncReceiveNum(); + } + return $status; + }); + } + + /** + * 验证优惠券是否可领取 + * @param $user + * @param Coupon $coupon + * @return bool + */ + private function checkReceive($user, $coupon) + { + if (!$coupon) { + $this->error = '优惠券不存在'; + return false; + } + if (!$coupon->checkReceive()) { + $this->error = $coupon->getError(); + return false; + } + // 验证是否已领取 + $userCouponIds = $this->getUserCouponIds($user['user_id']); + if (in_array($coupon['coupon_id'], $userCouponIds)) { + $this->error = '该优惠券已领取'; + return false; + } + return true; + } + + /** + * 订单结算优惠券列表 + * @param int $user_id 用户id + * @param double $orderPayPrice 订单商品总金额 + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function getUserCouponList($user_id, $orderPayPrice) + { + // todo: 新增筛选条件: 最低消费金额 + // 获取用户可用的优惠券列表 + $list = (new self)->getList($user_id); + $data = []; + foreach ($list as $coupon) { + // 最低消费金额 + if ($orderPayPrice < $coupon['min_price']) continue; + // 有效期范围内 + if ($coupon['start_time']['value'] > time()) continue; + $key = $coupon['user_coupon_id']; + $data[$key] = [ + 'user_coupon_id' => $coupon['user_coupon_id'], + 'name' => $coupon['name'], + 'color' => $coupon['color'], + 'coupon_type' => $coupon['coupon_type'], + 'reduce_price' => $coupon['reduce_price'], + 'discount' => $coupon['discount'], + 'min_price' => $coupon['min_price'], + 'expire_type' => $coupon['expire_type'], + 'start_time' => $coupon['start_time'], + 'end_time' => $coupon['end_time'], + ]; + // 计算打折金额 + if ($coupon['coupon_type']['value'] == 20) { +// $reduce_price = $orderPayPrice * ($coupon['discount'] / 10); + $reducePrice = helper::bcmul($orderPayPrice, $coupon['discount'] / 10); + $data[$key]['reduced_price'] = bcsub($orderPayPrice, $reducePrice, 2); + } else + $data[$key]['reduced_price'] = $coupon['reduce_price']; + } + // 根据折扣金额排序并返回 + return array_sort($data, 'reduced_price', true); + } + +} diff --git a/source/application/api/model/Wxapp.php b/source/application/api/model/Wxapp.php new file mode 100644 index 0000000..bbf194f --- /dev/null +++ b/source/application/api/model/Wxapp.php @@ -0,0 +1,29 @@ + 0 ? parent::detail($page_id) : parent::getHomePage(); + // 页面diy元素 + $items = $detail['page_data']['items']; + // 页面顶部导航 + isset($detail['page_data']['page']) && $items['page'] = $detail['page_data']['page']; + // 获取动态数据 + $model = new self; + foreach ($items as $key => $item) { + if ($item['type'] === 'window') { + $items[$key]['data'] = array_values($item['data']); + } else if ($item['type'] === 'goods') { + $items[$key]['data'] = $model->getGoodsList($user, $item); + } else if ($item['type'] === 'sharingGoods') { + $items[$key]['data'] = $model->getSharingGoodsList($user, $item); + } else if ($item['type'] === 'bargainGoods') { + $items[$key]['data'] = $model->getBargainGoodsList($item); + } else if ($item['type'] === 'sharpGoods') { + $items[$key]['data'] = $model->getSharpGoodsList($item); + } else if ($item['type'] === 'coupon') { + $items[$key]['data'] = $model->getCouponList($user, $item); + } else if ($item['type'] === 'article') { + $items[$key]['data'] = $model->getArticleList($item); + } else if ($item['type'] === 'special') { + $items[$key]['data'] = $model->getSpecialList($item); + } else if ($item['type'] === 'shop') { + $items[$key]['data'] = $model->getShopList($item); + } + } + return ['page' => $items['page'], 'items' => $items]; + } + + /** + * 商品组件:获取商品列表 + * @param $user + * @param $item + * @return array + * @throws \think\exception\DbException + */ + private function getGoodsList($user, $item) + { + // 获取商品数据 + $model = new GoodsModel; + if ($item['params']['source'] === 'choice') { + // 数据来源:手动 + $goodsIds = array_column($item['data'], 'goods_id'); + $goodsList = $model->getListByIdsFromApi($goodsIds, $user); + } else { + // 数据来源:自动 + $goodsList = $model->getList([ + 'status' => 10, + 'category_id' => $item['params']['auto']['category'], + 'sortType' => $item['params']['auto']['goodsSort'], + 'listRows' => $item['params']['auto']['showNum'] + ], $user); + } + if ($goodsList->isEmpty()) return []; + // 格式化商品列表 + $data = []; + foreach ($goodsList as $goods) { + $data[] = [ + 'goods_id' => $goods['goods_id'], + 'goods_name' => $goods['goods_name'], + 'selling_point' => $goods['selling_point'], + 'image' => $goods['image'][0]['file_path'], + 'goods_image' => $goods['image'][0]['file_path'], + 'goods_price' => $goods['sku'][0]['goods_price'], + 'line_price' => $goods['sku'][0]['line_price'], + 'goods_sales' => $goods['goods_sales'], + ]; + } + return $data; + } + + /** + * 商品组件:获取拼团商品列表 + * @param $user + * @param $item + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getSharingGoodsList($user, $item) + { + // 获取商品数据 + $model = new SharingGoodsModel; + if ($item['params']['source'] === 'choice') { + // 数据来源:手动 + $goodsIds = array_column($item['data'], 'goods_id'); + $goodsList = $model->getListByIdsFromApi($goodsIds, $user); + } else { + // 数据来源:自动 + $goodsList = $model->getList([ + 'status' => 10, + 'category_id' => $item['params']['auto']['category'], + 'sortType' => $item['params']['auto']['goodsSort'], + 'listRows' => $item['params']['auto']['showNum'] + ], $user); + } + if ($goodsList->isEmpty()) return []; + // 格式化商品列表 + $data = []; + foreach ($goodsList as $goods) { + $data[] = [ + 'goods_id' => $goods['goods_id'], + 'goods_name' => $goods['goods_name'], + 'selling_point' => $goods['selling_point'], + 'people' => $goods['people'], + 'goods_sales' => $goods['goods_sales'], + 'image' => $goods['image'][0]['file_path'], + 'goods_image' => $goods['image'][0]['file_path'], + 'sharing_price' => $goods['sku'][0]['sharing_price'], + 'goods_price' => $goods['sku'][0]['goods_price'], + 'line_price' => $goods['sku'][0]['line_price'], + ]; + } + return $data; + } + + /** + * 商品组件:获取拼团商品列表 + * @param $item + * @return array + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getBargainGoodsList($item) + { + // 获取商品数据 + $model = new BargainActiveModel; + if ($item['params']['source'] === 'choice') { + // 数据来源:手动 + $activeIds = helper::getArrayColumn($item['data'], 'active_id'); + $activeList = $model->getListByIds($activeIds); + } else { + // 数据来源:自动 + $activeList = $model->getHallList([ + 'sortType' => $item['params']['auto']['goodsSort'], + 'listRows' => $item['params']['auto']['showNum'] + ]); + } + if ($activeList->isEmpty()) return []; + // 格式化商品列表 + $data = []; + foreach ($activeList as $item) { + $data[] = [ + 'active_id' => $item['active_id'], + 'goods_name' => $item['goods']['goods_name'], + 'goods_image' => $item['goods']['goods_image'], + 'floor_price' => $item['floor_price'], + 'original_price' => $item['goods']['goods_sku']['goods_price'], + 'peoples' => $item['peoples'], + 'helps_count' => $item['helps_count'], + 'helps' => $item['helps'], + ]; + } + return $data; + } + + /** + * 秒杀商品组件:获取秒杀活动 + * @param $item + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getSharpGoodsList($item) + { + // 获取商品数据 + $service = new SharpActiveService; + // 获取秒杀活动及商品列表 + return $service->getSharpModular(['limit' => $item['params']['showNum']]); + } + + /** + * 优惠券组件:获取优惠券列表 + * @param $user + * @param $item + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getCouponList($user, $item) + { + // 获取优惠券数据 + return (new Coupon)->getList($user, $item['params']['limit'], true); + } + + /** + * 文章组件:获取文章列表 + * @param $item + * @return array + * @throws \think\exception\DbException + */ + private function getArticleList($item) + { + // 获取文章数据 + $model = new Article; + $articleList = $model->getList($item['params']['auto']['category'], $item['params']['auto']['showNum']); + return $articleList->isEmpty() ? [] : $articleList->toArray()['data']; + } + + /** + * 头条快报:获取头条列表 + * @param $item + * @return array + * @throws \think\exception\DbException + */ + private function getSpecialList($item) + { + // 获取头条数据 + $model = new Article; + $articleList = $model->getList($item['params']['auto']['category'], $item['params']['auto']['showNum']); + return $articleList->isEmpty() ? [] : $articleList->toArray()['data']; + } + + /** + * 线下门店组件:获取门店列表 + * @param $item + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getShopList($item) + { + // 获取商品数据 + $model = new ShopModel; + if ($item['params']['source'] === 'choice') { + // 数据来源:手动 + $shopIds = array_column($item['data'], 'shop_id'); + $shopList = $model->getListByIds($shopIds); + } else { + // 数据来源:自动 + $shopList = $model->getList(null, false, false, $item['params']['auto']['showNum']); + } + if ($shopList->isEmpty()) return []; + // 格式化商品列表 + $data = []; + foreach ($shopList as $shop) { + $data[] = [ + 'shop_id' => $shop['shop_id'], + 'shop_name' => $shop['shop_name'], + 'logo_image' => $shop['logo']['file_path'], + 'phone' => $shop['phone'], + 'region' => $shop['region'], + 'address' => $shop['address'], + ]; + } + return $data; + } + +} diff --git a/source/application/api/model/WxappPrepayId.php b/source/application/api/model/WxappPrepayId.php new file mode 100644 index 0000000..6a50815 --- /dev/null +++ b/source/application/api/model/WxappPrepayId.php @@ -0,0 +1,37 @@ +save([ + 'prepay_id' => $prepayId, + 'order_id' => $orderId, + 'order_type' => $orderType, + 'user_id' => $userId, + 'can_use_times' => 0, + 'used_times' => 0, + 'expiry_time' => time() + (7 * 86400), + 'wxapp_id' => self::$wxapp_id, + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/article/Category.php b/source/application/api/model/article/Category.php new file mode 100644 index 0000000..abe46df --- /dev/null +++ b/source/application/api/model/article/Category.php @@ -0,0 +1,76 @@ +deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + $this->deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 删除商品分类 + * @param $category_id + * @return bool|int + */ + public function remove($category_id) + { + // 判断是否存在文章 + $articleCount = ArticleModel::getArticleTotal(['category_id' => $category_id]); + if ($articleCount > 0) { + $this->error = '该分类下存在' . $articleCount . '个文章,不允许删除'; + return false; + } + $this->deleteCache(); + return $this->delete(); + } + + /** + * 删除缓存 + * @return bool + */ + private function deleteCache() + { + return Cache::rm('article_category_' . self::$wxapp_id); + } + +} diff --git a/source/application/api/model/bargain/Active.php b/source/application/api/model/bargain/Active.php new file mode 100644 index 0000000..c625f02 --- /dev/null +++ b/source/application/api/model/bargain/Active.php @@ -0,0 +1,184 @@ +getList($param); + } + + /** + * 获取砍价活动列表(根据活动id集) + * @param $activeIds + * @param array $param + * @return mixed|\think\Paginator + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($activeIds, $param = []) + { + $this->where('active_id', 'in', $activeIds); + return $this->getList($param); + } + + /** + * 获取砍价活动列表 + * @param $param + * @return mixed|\think\Paginator + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getList($param) + { + // 商品列表获取条件 + $params = array_merge([ + 'status' => 1, // 商品状态 + 'sortType' => 'all', // 排序类型 + 'listRows' => 15, // 每页数量 + ], $param); + // 排序规则 + if ($params['sortType'] === 'all') { + $this->order(['sort' => 'asc']); + } elseif ($params['sortType'] === 'sales') { + $this->order(['active_sales' => 'desc']); + } elseif ($params['sortType'] === 'price') { + $this->order(['floor_price' => $params['sortPrice'] ? 'desc' : 'asc']); + } + // 砍价活动列表 + $list = $this->field(['*', '(actual_sales + initial_sales) as active_sales']) + ->where('start_time', '<=', time()) + ->where('end_time', '>=', time()) + ->where('status', '=', 1) + ->where('is_delete', '=', 0) + ->order(['sort' => 'asc']) + ->paginate($params['listRows'], false, [ + 'query' => \request()->request() + ]); + // 设置商品数据 + $list = GoodsService::setGoodsData($list); + // 整理正在砍价的助力信息 + $list = $this->setHelpsData($list); + return $list; + } + + /** + * 整理正在砍价的助力信息 + * @param $list + * @return mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function setHelpsData($list) + { + $model = new TaskHelpModel; + foreach ($list as &$item) { + $item['helps'] = $model->getHelpListByActiveId($item['active_id']); + $item['helps_count'] = $model->getHelpCountByActiveId($item['active_id']); + } + return $list; + } + + /** + * 获取砍价活动详情 + * @param $activeId + * @return Active|bool|null + * @throws \think\exception\DbException + */ + public function getDetail($activeId) + { + $model = static::detail($activeId); + if (empty($model) || $model['is_delete'] == true || $model['status'] == false) { + $this->error = '很抱歉,该砍价商品不存在或已下架'; + return false; + } + return $model; + } + + /** + * 获取用户是否正在参与改砍价活动,如果已参与则返回task_id + * @param $activeId + * @param bool $user + * @return bool|int + */ + public function getWhetherPartake($activeId, $user = false) + { + if ($user === false) { + return false; + } + return TaskModel::getHandByUser($activeId, $user['user_id']); + } + + /** + * 累计活动销量(实际) + * @return int|true + * @throws \think\Exception + */ + public function setIncSales() + { + return $this->setInc('actual_sales'); + } + +} \ No newline at end of file diff --git a/source/application/api/model/bargain/Setting.php b/source/application/api/model/bargain/Setting.php new file mode 100644 index 0000000..7622f7b --- /dev/null +++ b/source/application/api/model/bargain/Setting.php @@ -0,0 +1,35 @@ + static::getItem('basic')['bargain_rules'], + ]; + } + + /** + * 获取是否开启分销 + * @return array + */ + public static function getIsDealer() + { + return static::getItem('basic')['is_dealer']; + } + +} \ No newline at end of file diff --git a/source/application/api/model/bargain/Task.php b/source/application/api/model/bargain/Task.php new file mode 100644 index 0000000..74d66e2 --- /dev/null +++ b/source/application/api/model/bargain/Task.php @@ -0,0 +1,366 @@ +where('user_id', '=', $userId) + ->where('is_delete', '=', 0) + ->order(['create_time' => 'desc']) + ->paginate(5, false, [ + 'query' => \request()->request() + ]); + // 设置商品数据 + $list = GoodsService::setGoodsData($list); + return $list; + } + + /** + * 获取砍价任务详情 + * @param $taskId + * @param bool $user + * @return array|bool + * @throws \think\exception\DbException + */ + public function getTaskDetail($taskId, $user = false) + { + // 砍价任务详情 + $task = static::detail($taskId, ['user']); + if (empty($task)) { + $this->error = '砍价任务不存在'; + return false; + } + // 砍价活动详情 + $active = ActiveModel::detail($task['active_id']); + // 砍价商品详情 + $goods = GoodsModel::detail($task['goods_id']); + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $task['spec_sku_id']); + // 好友助力榜 + $help_list = TaskHelpModel::getListByTaskId($taskId); + // 当前是否为发起人 + $is_creater = $this->isCreater($task, $user); + // 当前是否已砍 + $is_cut = $this->isCut($help_list, $user); + return compact('task', 'is_creater', 'is_cut', 'active', 'goods', 'help_list'); + } + + /** + * 获取砍价任务的商品列表(用于订单结算) + * @param $taskId + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function getTaskGoods($taskId) + { + // 砍价任务详情 + $task = static::detail($taskId); + if (empty($task) || $task['is_delete'] || $task['status'] == false) { + $this->error = '砍价任务不存在或已结束'; + return false; + } + if ($task['is_buy'] == true) { + $this->error = '该砍价商品已购买'; + return false; + } + // 砍价商品详情 + $goods = GoodsModel::detail($task['goods_id']); + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $task['spec_sku_id']); + // 商品列表 + $goodsList = [$goods->hidden(['category', 'content', 'image', 'sku'])]; + foreach ($goodsList as &$item) { + // 商品单价 + $item['goods_price'] = $task['actual_price']; + // 商品购买数量 + $item['total_num'] = 1; + $item['spec_sku_id'] = $item['goods_sku']['spec_sku_id']; + // 商品购买总金额 + $item['total_price'] = $task['actual_price']; + } + return $goodsList; + } + + /** + * 订单创建后将砍价任务结束 + * @return false|int + */ + public function setTaskEnd() + { + return $this->save(['status' => 0]); + } + + /** + * 获取用户是否正在参与改砍价活动,如果已参与则返回task_id + * @param $activeId + * @param $userId + * @return bool|int + */ + public static function getHandByUser($activeId, $userId) + { + $taskId = (new static)->where('active_id', '=', $activeId) + ->where('user_id', '=', $userId) + ->where('end_time', '>', time()) + ->where('status', '=', 1) + ->where('is_delete', '=', 0) + ->value('task_id'); + return $taskId ?: false; + } + + /** + * 新增砍价任务 + * @param $userId + * @param $activeId + * @param $goodsSkuId + * @return bool + * @throws \think\exception\DbException + * @throws \Exception + */ + public function partake($userId, $activeId, $goodsSkuId) + { + // 获取活动详情 + if (!$active = $this->getActiveDetail($activeId)) { + return false; + } + // 验证能否创建砍价任务 + if (!$this->onVerify($active, $userId)) { + return false; + } + // 获取商品详情 + $goods = GoodsModel::detail($active['goods_id']); + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $goodsSkuId); + // 事务处理 + return $this->transaction(function () use ($userId, $active, $goodsSkuId, $goods) { + // 创建砍价任务 + $this->add($userId, $active, $goodsSkuId, $goods); + // 发起人自砍一刀 + $active['is_self_cut'] && $this->onCutEvent($userId, true); + return true; + }); + } + + /** + * 帮砍一刀 + * @param $user + * @return bool|false|int + */ + public function helpCut($user) + { + // 好友助力榜 + $helpList = TaskHelpModel::getListByTaskId($this['task_id']); + // 当前是否已砍 + if ($this->isCut($helpList, $user)) { + $this->error = '您已参与砍价,请不要重复操作'; + return false; + } + // 帮砍一刀事件 + return $this->transaction(function () use ($user) { + return $this->onCutEvent($user['user_id'], $this->isCreater($this, $user)); + }); + } + + /** + * 砍一刀的金额 + * @return mixed + */ + public function getCutMoney() + { + return $this['section'][$this['cut_people']]; + } + + /** + * 帮砍一刀事件 + * @param $userId + * @param bool $isCreater + * @return false|int + */ + private function onCutEvent($userId, $isCreater = false) + { + // 砍价金额 + $cutMoney = $this->getCutMoney(); + // 砍价助力记录 + $model = new TaskHelpModel; + $model->add($this, $userId, $cutMoney, $isCreater); + // 实际购买金额 + $actualPrice = helper::bcsub($this['actual_price'], $cutMoney); + // 更新砍价任务信息 + $this->save([ + 'cut_people' => ['inc', 1], + 'cut_money' => ['inc', $cutMoney], + 'actual_price' => $actualPrice, + 'is_floor' => helper::bcequal($actualPrice, $this['floor_price']), + ]); + return true; + } + + /** + * 创建砍价任务记录 + * @param $userId + * @param $active + * @param $goodsSkuId + * @param $goods + * @return false|int + * @throws \Exception + */ + private function add($userId, $active, $goodsSkuId, $goods) + { + // 分配砍价金额区间 + $section = $this->calcBargainSection( + $goods['goods_sku']['goods_price'], + $active['floor_price'], + $active['peoples'] + ); + // 新增记录 + return $this->save([ + 'active_id' => $active['active_id'], + 'user_id' => $userId, + 'goods_id' => $active['goods_id'], + 'spec_sku_id' => $goodsSkuId, + 'goods_price' => $goods['goods_sku']['goods_price'], + 'floor_price' => $active['floor_price'], + 'peoples' => $active['peoples'], + 'cut_people' => 0, + 'section' => $section, + 'cut_money' => 0.00, + 'actual_price' => $goods['goods_sku']['goods_price'], + 'end_time' => time() + ($active['expiryt_time'] * 3600), + 'is_buy' => 0, + 'status' => 1, + 'wxapp_id' => static::$wxapp_id, + ]); + } + + /** + * 砍价任务标记为已购买 + * @return false|int + */ + public function setIsBuy() + { + return $this->save(['is_buy' => 1]); + } + + /** + * 分配砍价金额区间 + * @param $goodsPrice + * @param $floorPrice + * @param $peoples + * @return mixed + * @throws \Exception + */ + private function calcBargainSection($goodsPrice, $floorPrice, $peoples) + { + $AmountService = new AmountService(helper::bcsub($goodsPrice, $floorPrice), $peoples); + return $AmountService->handle()['items']; + } + + /** + * 当前是否为发起人 + * @param $task + * @param $user + * @return bool + */ + private function isCreater($task, $user) + { + if ($user === false) return false; + return $user['user_id'] == $task['user_id']; + } + + /** + * 当前是否已砍 + * @param $helpList + * @param $user + * @return bool + */ + private function isCut($helpList, $user) + { + if ($user === false) return false; + foreach ($helpList as $item) { + if ($item['user_id'] == $user['user_id']) return true; + } + return false; + } + + /** + * 获取活动详情 + * @param $activeId + * @return Active|bool|null + * @throws \think\exception\DbException + */ + private function getActiveDetail($activeId) + { + // 获取活动详情 + $ActiveModel = new ActiveModel; + $detail = $ActiveModel->getDetail($activeId); + // 活动详情不存在 + if ($detail === false) { + $this->error = $ActiveModel->getError(); + return false; + } + return $detail; + } + + /** + * 验证能否创建砍价任务 + * @param $active + * @param $userId + * @return bool + */ + private function onVerify($active, $userId) + { + // 活动是否开始 + if (!$active['is_start']) { + $this->error = '很抱歉,当前砍价活动未开始'; + return false; + } + // 活动是否到期合法 + if ($active['is_end']) { + $this->error = '很抱歉,当前砍价活动已结束'; + return false; + } + // 判断当前用户是否已参加 + $taskId = static::getHandByUser($active['active_id'], $userId); + if ($taskId !== false && $taskId > 0) { + $this->error = '很抱歉,当前砍价活动您已参加,无需重复参与'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/model/bargain/TaskHelp.php b/source/application/api/model/bargain/TaskHelp.php new file mode 100644 index 0000000..d5d6d08 --- /dev/null +++ b/source/application/api/model/bargain/TaskHelp.php @@ -0,0 +1,108 @@ +with(['user']) + ->where('task_id', '=', $taskId) + ->order(['create_time' => 'desc']) + ->select(); + // 隐藏会员昵称 + foreach ($list as &$item) { + $item['user']['nickName'] = \substr_cut($item['user']['nickName']); + } + return $list; + } + + /** + * 新增记录 + * @param $task + * @param $userId + * @param $cutMoney + * @param $isCreater + * @return false|int + */ + public function add($task, $userId, $cutMoney, $isCreater = false) + { + return $this->save([ + 'task_id' => $task['task_id'], + 'active_id' => $task['active_id'], + 'user_id' => $userId, + 'cut_money' => $cutMoney, + 'is_creater' => $isCreater, + 'wxapp_id' => static::$wxapp_id, + ]); + } + + /** + * 根据砍价活动id获取正在砍价的助力信息列表 + * @param $activeId + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getHelpListByActiveId($activeId) + { + return $this + ->with('user') // todo: 废弃 + ->alias('help') + ->field(['help.user_id', 'user.nickName', 'user.avatarUrl']) + ->join('user', 'user.user_id = help.user_id') + ->join('bargain_task task', 'task.task_id = help.task_id') + ->where('help.active_id', '=', $activeId) + // is_creater 只统计发起人 +// ->where('help.is_creater', '=', 1) + ->where('task.status', '=', 1) + ->where('task.is_delete', '=', 0) + ->group('help.user_id') + ->limit(5) + ->select(); + } + + /** + * 根据砍价活动id获取正在砍价的助力人数 + * @param $activeId + * @return int|string + * @throws \think\Exception + */ + public function getHelpCountByActiveId($activeId) + { + return $this->alias('help') + ->join('user', 'user.user_id = help.user_id') + ->join('bargain_task task', 'task.task_id = help.task_id') + ->where('help.active_id', '=', $activeId) + // is_creater 只统计发起人 +// ->where('help.is_creater', '=', 1) + ->where('task.status', '=', 1) + ->where('task.is_delete', '=', 0) + ->group('help.user_id') + ->count(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/dealer/Apply.php b/source/application/api/model/dealer/Apply.php new file mode 100644 index 0000000..3c5dd01 --- /dev/null +++ b/source/application/api/model/dealer/Apply.php @@ -0,0 +1,92 @@ + $user_id]); + return $detail ? ((int)$detail['apply_status'] === 10) : false; + } + + /** + * 提交申请 + * @param $user + * @param $name + * @param $mobile + * @return mixed + * @throws \think\exception\DbException + */ + public function submit($user, $name, $mobile) + { + // 成为分销商条件 + $config = Setting::getItem('condition'); + // 数据整理 + $data = [ + 'user_id' => $user['user_id'], + 'real_name' => trim($name), + 'mobile' => trim($mobile), + 'referee_id' => Referee::getRefereeUserId($user['user_id'], 1), + 'apply_type' => $config['become'], + 'apply_time' => time(), + 'wxapp_id' => self::$wxapp_id, + ]; + if ($config['become'] == 10) { + $data['apply_status'] = 10; + } elseif ($config['become'] == 20) { + $data['apply_status'] = 20; + } + return $this->add($user, $data); + } + + /** + * 更新分销商申请信息 + * @param $user + * @param $data + * @return mixed + */ + private function add($user, $data) + { + // 更新记录 + return $this->transaction(function () use ($user, $data) { + // 实例化模型 + $model = self::detail(['user_id' => $user['user_id']]) ?: $this; + // 保存申请信息 + $model->save($data); + // 无需审核,自动通过 + if ($data['apply_type'] == 20) { + // 新增分销商用户记录 + User::add($user['user_id'], [ + 'real_name' => $data['real_name'], + 'mobile' => $data['mobile'], + 'referee_id' => $data['referee_id'] + ]); + } + return true; + }); + } + +} diff --git a/source/application/api/model/dealer/Capital.php b/source/application/api/model/dealer/Capital.php new file mode 100644 index 0000000..ea45fa6 --- /dev/null +++ b/source/application/api/model/dealer/Capital.php @@ -0,0 +1,23 @@ + -1 && $this->where('is_settled', '=', !!$is_settled); + $data = $this->with(['user']) + ->where('first_user_id|second_user_id|third_user_id', '=', $user_id) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + if ($data->isEmpty()) { + return $data; + } + // 整理订单信息 + $with = ['goods' => ['image', 'refund'], 'address', 'user']; + return OrderService::getOrderList($data, 'order_master', $with); + } + + /** + * 创建分销商订单记录 + * @param $order + * @param int $order_type 订单类型 (10商城订单 20拼团订单) + * @return bool|false|int + * @throws \think\exception\DbException + */ + public static function createOrder(&$order, $order_type = OrderTypeEnum::MASTER) + { + // 分销订单模型 + $model = new self; + // 分销商基本设置 + $setting = Setting::getItem('basic'); + // 是否开启分销功能 + if (!$setting['is_open']) { + return false; + } + // 获取当前买家的所有上级分销商用户id + $dealerUser = $model->getDealerUserId($order['user_id'], $setting['level'], $setting['self_buy']); + // 非分销订单 + if (!$dealerUser['first_user_id']) { + return false; + } + // 计算订单分销佣金 + $capital = $model->getCapitalByOrder($order); + // 保存分销订单记录 + return $model->save([ + 'user_id' => $order['user_id'], + 'order_id' => $order['order_id'], + 'order_type' => $order_type, + // 'order_no' => $order['order_no'], // 废弃 + 'order_price' => $capital['orderPrice'], + 'first_money' => max($capital['first_money'], 0), + 'second_money' => max($capital['second_money'], 0), + 'third_money' => max($capital['third_money'], 0), + 'first_user_id' => $dealerUser['first_user_id'], + 'second_user_id' => $dealerUser['second_user_id'], + 'third_user_id' => $dealerUser['third_user_id'], + 'is_settled' => 0, + 'wxapp_id' => $model::$wxapp_id + ]); + } + + /** + * 获取当前买家的所有上级分销商用户id + * @param $user_id + * @param $level + * @param $self_buy + * @return mixed + * @throws \think\exception\DbException + */ + private function getDealerUserId($user_id, $level, $self_buy) + { + $dealerUser = [ + 'first_user_id' => $level >= 1 ? Referee::getRefereeUserId($user_id, 1, true) : 0, + 'second_user_id' => $level >= 2 ? Referee::getRefereeUserId($user_id, 2, true) : 0, + 'third_user_id' => $level == 3 ? Referee::getRefereeUserId($user_id, 3, true) : 0 + ]; + // 分销商自购 + if ($self_buy && User::isDealerUser($user_id)) { + return [ + 'first_user_id' => $user_id, + 'second_user_id' => $dealerUser['first_user_id'], + 'third_user_id' => $dealerUser['second_user_id'], + ]; + } + return $dealerUser; + } + +} diff --git a/source/application/api/model/dealer/Referee.php b/source/application/api/model/dealer/Referee.php new file mode 100644 index 0000000..645e224 --- /dev/null +++ b/source/application/api/model/dealer/Referee.php @@ -0,0 +1,100 @@ +add($referee_id, $user_id, 1); + // # 记录二级推荐关系 + if ($setting['level'] >= 2) { + // 二级分销商id + $referee_2_id = self::getRefereeUserId($referee_id, 1, true); + // 新增关系记录 + $referee_2_id > 0 && $model->add($referee_2_id, $user_id, 2); + } + // # 记录三级推荐关系 + if ($setting['level'] == 3) { + // 三级分销商id + $referee_3_id = self::getRefereeUserId($referee_id, 2, true); + // 新增关系记录 + $referee_3_id > 0 && $model->add($referee_3_id, $user_id, 3); + } + return true; + } + + /** + * 新增关系记录 + * @param $dealer_id + * @param $user_id + * @param int $level + * @return bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function add($dealer_id, $user_id, $level = 1) + { + // 新增推荐关系 + $wxapp_id = self::$wxapp_id; + $create_time = time(); + $this->insert(compact('dealer_id', 'user_id', 'level', 'wxapp_id', 'create_time')); + // 记录分销商成员数量 + User::setMemberInc($dealer_id, $level); + return true; + } + + /** + * 是否已存在推荐关系 + * @param $user_id + * @return bool + * @throws \think\exception\DbException + */ + private static function isExistReferee($user_id) + { + return !!self::get(['user_id' => $user_id]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/dealer/Setting.php b/source/application/api/model/dealer/Setting.php new file mode 100644 index 0000000..a7a5805 --- /dev/null +++ b/source/application/api/model/dealer/Setting.php @@ -0,0 +1,22 @@ +save([ + 'money' => $this['money'] - $money, + 'freeze_money' => $this['freeze_money'] + $money, + ]); + } + + /** + * 累计分销商成员数量 + * @param $dealer_id + * @param $level + * @return int|true + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public static function setMemberInc($dealer_id, $level) + { + $fields = [1 => 'first_num', 2 => 'second_num', 3 => 'third_num']; + $model = static::detail($dealer_id); + return $model->setInc($fields[$level]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/dealer/Withdraw.php b/source/application/api/model/dealer/Withdraw.php new file mode 100644 index 0000000..78f12dc --- /dev/null +++ b/source/application/api/model/dealer/Withdraw.php @@ -0,0 +1,99 @@ +where('user_id', '=', $user_id); + $apply_status > -1 && $this->where('apply_status', '=', $apply_status); + return $this->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 提交申请 + * @param User $dealer + * @param $data + * @return false|int + * @throws BaseException + */ + public function submit($dealer, $data) + { + // 数据验证 + $this->validation($dealer, $data); + // 新增申请记录 + $this->save(array_merge($data, [ + 'user_id' => $dealer['user_id'], + 'apply_status' => 10, + 'wxapp_id' => self::$wxapp_id, + ])); + // 冻结用户资金 + $dealer->freezeMoney($data['money']); + return true; + } + + /** + * 数据验证 + * @param $dealer + * @param $data + * @throws BaseException + */ + private function validation($dealer, $data) + { + // 结算设置 + $settlement = Setting::getItem('settlement'); + // 最低提现佣金 + if ($data['money'] <= 0) { + throw new BaseException(['msg' => '提现金额不正确']); + } + if ($dealer['money'] <= 0) { + throw new BaseException(['msg' => '当前用户没有可提现佣金']); + } + if ($data['money'] > $dealer['money']) { + throw new BaseException(['msg' => '提现金额不能大于可提现佣金']); + } + if ($data['money'] < $settlement['min_money']) { + throw new BaseException(['msg' => '最低提现金额为' . $settlement['min_money']]); + } + if (!in_array($data['pay_type'], $settlement['pay_type'])) { + throw new BaseException(['msg' => '提现方式不正确']); + } + if ($data['pay_type'] == '20') { + if (empty($data['alipay_name']) || empty($data['alipay_account'])) { + throw new BaseException(['msg' => '请补全提现信息']); + } + } elseif ($data['pay_type'] == '30') { + if (empty($data['bank_name']) || empty($data['bank_account']) || empty($data['bank_card'])) { + throw new BaseException(['msg' => '请补全提现信息']); + } + } + } + +} \ No newline at end of file diff --git a/source/application/api/model/recharge/Order.php b/source/application/api/model/recharge/Order.php new file mode 100644 index 0000000..2ec743d --- /dev/null +++ b/source/application/api/model/recharge/Order.php @@ -0,0 +1,206 @@ +where('user_id', '=', $userId) + ->where('pay_status', '=', PayStatusEnum::SUCCESS) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 获取订单详情(待付款状态) + * @param $orderNo + * @return self|null + * @throws \think\exception\DbException + */ + public static function getPayDetail($orderNo) + { + return self::detail(['order_no' => $orderNo, 'pay_status' => PayStatusEnum::PENDING]); + } + + /** + * 创建充值订单 + * @param \app\api\model\User $user 当前用户信息 + * @param int $planId 套餐id + * @param double $customMoney 自定义充值金额 + * @return bool|false|int + * @throws BaseException + * @throws \think\exception\DbException + */ + public function createOrder($user, $planId = null, $customMoney = 0.00) + { + // 确定充值方式 + $rechargeType = $planId > 0 ? RechargeTypeEnum::PLAN : RechargeTypeEnum::CUSTOM; + // 验证用户输入 + if (!$this->validateForm($rechargeType, $planId, $customMoney)) { + $this->error = $this->error ?: '数据验证错误'; + return false; + } + // 获取订单数据 + $data = $this->getOrderData($user, $rechargeType, $planId, $customMoney); + // 记录订单信息 + return $this->saveOrder($data); + } + + /** + * 保存订单记录 + * @param $data + * @return bool|false|int + */ + private function saveOrder($data) + { + // 写入订单记录 + $this->save($data['order']); + // 记录订单套餐快照 + if (!empty($data['plan'])) { + $PlanModel = new OrderPlanModel; + return $PlanModel->add($this['order_id'], $data['plan']); + } + return true; + } + + /** + * 生成充值订单 + * @param $user + * @param $rechargeType + * @param $planId + * @param $customMoney + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + private function getOrderData($user, $rechargeType, $planId, $customMoney) + { + // 订单信息 + $data = [ + 'order' => [ + 'user_id' => $user['user_id'], + 'order_no' => 'RC' . OrderService::createOrderNo(), + 'recharge_type' => $rechargeType, + 'gift_money' => 0.00, + 'wxapp_id' => self::$wxapp_id, + ], + 'plan' => [] // 订单套餐快照 + ]; + // 自定义金额充值 + if ($rechargeType == RechargeTypeEnum::CUSTOM) { + $this->createDataByCustom($data, $customMoney); + } + // 套餐充值 + if ($rechargeType == RechargeTypeEnum::PLAN) { + $this->createDataByPlan($data, $planId); + } + // 实际到账金额 + $data['order']['actual_money'] = bcadd($data['order']['pay_price'], $data['order']['gift_money'], 2); + return $data; + } + + /** + * 创建套餐充值订单数据 + * @param $order + * @param $planId + * @return bool + * @throws BaseException + * @throws \think\exception\DbException + */ + private function createDataByPlan(&$order, $planId) + { + // 获取套餐详情 + $planInfo = PlanModel::detail($planId); + if (empty($planInfo)) { + throw new BaseException(['msg' => '充值套餐不存在']); + } + $order['plan'] = $planInfo; + $order['order']['plan_id'] = $planInfo['plan_id']; + $order['order']['gift_money'] = $planInfo['gift_money']; + $order['order']['pay_price'] = $planInfo['money']; + return true; + } + + /** + * 创建自定义充值订单数据 + * @param $order + * @param $customMoney + * @return bool + */ + private function createDataByCustom(&$order, $customMoney) + { + // 用户支付金额 + $order['order']['pay_price'] = $customMoney; + // 充值设置 + $setting = SettingModel::getItem('recharge'); + if ($setting['is_custom'] == false) { + return true; + } + // 根据自定义充值金额匹配满足的套餐 + if ($setting['is_match_plan'] == true) { + $matchPlanInfo = (new PlanModel)->getMatchPlan($customMoney); + if (!empty($matchPlanInfo)) { + $order['plan'] = $matchPlanInfo; + $order['order']['plan_id'] = $matchPlanInfo['plan_id']; + $order['order']['gift_money'] = $matchPlanInfo['gift_money']; + } + } + return true; + } + + /** + * 表单验证 + * @param $rechargeType + * @param $planId + * @param $customMoney + * @return bool + */ + private function validateForm($rechargeType, $planId, $customMoney) + { + if (empty($planId) && $customMoney <= 0) { + $this->error = '请选择充值套餐或输入充值金额'; + return false; + } + // 验证自定义的金额 + if ($rechargeType == RechargeTypeEnum::CUSTOM && $customMoney <= 0) { + $this->error = '请选择充值套餐或输入充值金额'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/model/recharge/OrderPlan.php b/source/application/api/model/recharge/OrderPlan.php new file mode 100644 index 0000000..b658139 --- /dev/null +++ b/source/application/api/model/recharge/OrderPlan.php @@ -0,0 +1,32 @@ +save([ + 'order_id' => $orderId, + 'plan_id' => $data['plan_id'], + 'plan_name' => $data['plan_name'], + 'money' => $data['money'], + 'gift_money' => $data['gift_money'], + 'wxapp_id' => self::$wxapp_id + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/recharge/Plan.php b/source/application/api/model/recharge/Plan.php new file mode 100644 index 0000000..9738dfd --- /dev/null +++ b/source/application/api/model/recharge/Plan.php @@ -0,0 +1,73 @@ +where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'money' => 'desc', 'create_time' => 'desc']) + ->select(); + } + + /** + * 根据自定义充值金额匹配满足的套餐 + * @param $payPrice + * @return array|false|\PDOStatement|string|\think\Model + */ + public function getMatchPlan($payPrice) + { + return (new static)->where('money', '<=', $payPrice) + ->where('is_delete', '=', 0) + ->order(['money' => 'desc']) + ->find(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/sharing/Active.php b/source/application/api/model/sharing/Active.php new file mode 100644 index 0000000..f6681e6 --- /dev/null +++ b/source/application/api/model/sharing/Active.php @@ -0,0 +1,50 @@ +save($data); + } + + /** + * 根据商品id获取进行中的拼单列表 + * @param $goods_id + * @param int $limit + * @return false|\PDOStatement|string|\think\Collection + */ + public static function getActivityListByGoods($goods_id, $limit = 15) + { + return (new static)->with(['user']) + ->where('goods_id', '=', $goods_id) + ->where('status', '=', 10) + ->limit($limit) + ->select(); + } + +} diff --git a/source/application/api/model/sharing/ActiveUsers.php b/source/application/api/model/sharing/ActiveUsers.php new file mode 100644 index 0000000..a541d34 --- /dev/null +++ b/source/application/api/model/sharing/ActiveUsers.php @@ -0,0 +1,23 @@ +belongsTo("app\\{$module}\\model\\User") + ->field(['user_id', 'nickName', 'avatarUrl']); + } + + /** + * 获取指定商品评价列表 + * @param $goods_id + * @param int $scoreType + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getGoodsCommentList($goods_id, $scoreType = -1) + { + // 筛选条件 + $filter = [ + 'goods_id' => $goods_id, + 'is_delete' => 0, + 'status' => 1, + ]; + // 评分 + $scoreType > 0 && $filter['score'] = $scoreType; + return $this->with(['user', 'OrderGoods', 'image.file']) + ->where($filter) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 获取指定评分总数 + * @param $goods_id + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getTotal($goods_id) + { + return $this->field([ + 'count(comment_id) AS `all`', + 'count(score = 10 OR NULL) AS `praise`', + 'count(score = 20 OR NULL) AS `review`', + 'count(score = 30 OR NULL) AS `negative`', + ])->where([ + 'goods_id' => $goods_id, + 'is_delete' => 0, + 'status' => 1 + ])->find(); + } + + /** + * 验证订单是否允许评价 + * @param Order $order + * @return boolean + */ + public function checkOrderAllowComment($order) + { + // 验证订单是否已完成 + if ($order['order_status']['value'] != 30) { + $this->error = '该订单未完成,无法评价'; + return false; + } + // 验证订单是否已评价 + if ($order['is_comment'] == 1) { + $this->error = '该订单已完成评价'; + return false; + } + return true; + } + + /** + * 根据已完成订单商品 添加评价 + * @param Order $order + * @param \think\Collection|OrderGoods $goodsList + * @param $formJsonData + * @return boolean + * @throws \Exception + */ + public function addForOrder($order, $goodsList, $formJsonData) + { + // 生成 formData + $formData = $this->formatFormData($formJsonData); + // 生成评价数据 + $data = $this->createCommentData($order['user_id'], $order['order_id'], $goodsList, $formData); + if (empty($data)) { + $this->error = '没有输入评价内容'; + return false; + } + return $this->transaction(function () use ($order, $goodsList, $formData, $data) { + // 记录评价内容 + $result = $this->isUpdate(false)->saveAll($data); + // 记录评价图片 + $this->saveAllImages($result, $formData); + // 更新订单评价状态 + $isComment = count($goodsList) === count($data); + $this->updateOrderIsComment($order, $isComment, $result); + return true; + }); + } + + /** + * 更新订单评价状态 + * @param Order $order + * @param $isComment + * @param $commentList + * @return array|false + * @throws \Exception + */ + private function updateOrderIsComment($order, $isComment, &$commentList) + { + // 更新订单商品 + $orderGoodsData = []; + foreach ($commentList as $comment) { + $orderGoodsData[] = [ + 'order_goods_id' => $comment['order_goods_id'], + 'is_comment' => 1 + ]; + } + // 更新订单 + $isComment && $order->save(['is_comment' => 1]); + return (new OrderGoods)->saveAll($orderGoodsData); + } + + /** + * 生成评价数据 + * @param $user_id + * @param $order_id + * @param $goodsList + * @param $formData + * @return array + * @throws BaseException + */ + private function createCommentData($user_id, $order_id, &$goodsList, &$formData) + { + $data = []; + foreach ($goodsList as $goods) { + if (!isset($formData[$goods['order_goods_id']])) { + throw new BaseException(['msg' => '提交的数据不合法']); + } + $item = $formData[$goods['order_goods_id']]; + !empty($item['content']) && $data[$goods['order_goods_id']] = [ + 'score' => $item['score'], + 'content' => $item['content'], + 'is_picture' => !empty($item['uploaded']), + 'sort' => 100, + 'status' => 1, + 'user_id' => $user_id, + 'order_id' => $order_id, + 'goods_id' => $item['goods_id'], + 'order_goods_id' => $item['order_goods_id'], + 'wxapp_id' => self::$wxapp_id + ]; + } + return $data; + } + + /** + * 格式化 formData + * @param string $formJsonData + * @return array + */ + private function formatFormData($formJsonData) + { + return array_column(json_decode($formJsonData, true), null, 'order_goods_id'); + } + + /** + * 记录评价图片 + * @param $commentList + * @param $formData + * @return bool + * @throws \Exception + */ + private function saveAllImages(&$commentList, &$formData) + { + // 生成评价图片数据 + $imageData = []; + foreach ($commentList as $comment) { + $item = $formData[$comment['order_goods_id']]; + foreach ($item['uploaded'] as $imageId) { + $imageData[] = [ + 'comment_id' => $comment['comment_id'], + 'image_id' => $imageId, + 'wxapp_id' => self::$wxapp_id + ]; + } + } + $model = new CommentImage; + return !empty($imageData) && $model->saveAll($imageData); + } + +} diff --git a/source/application/api/model/sharing/CommentImage.php b/source/application/api/model/sharing/CommentImage.php new file mode 100644 index 0000000..4d88b64 --- /dev/null +++ b/source/application/api/model/sharing/CommentImage.php @@ -0,0 +1,23 @@ +hidden(['category', 'content', 'image', 'sku']); + // 整理列表数据并返回 + return $this->setGoodsListDataFromApi($data, true, ['userInfo' => $userInfo]); + } + + /** + * 获取商品详情信息 + * @param int $goodsId 商品id + * @param array|bool $userInfo 用户信息 + * @return array|false|\PDOStatement|string|\think\Model|static + * @throws BaseException + */ + public function getDetails($goodsId, $userInfo = false) + { + // 获取商品详情 + $model = new static; + $goods = $model->with([ + 'category', + 'image' => ['file'], + 'sku' => ['image'], + 'spec_rel' => ['spec'], + 'delivery' => ['rule'], + 'commentData' => function ($query) { + $query->with('user')->where(['is_delete' => 0, 'status' => 1])->limit(2); + } + ])->withCount(['commentData' => function ($query) { + $query->where(['is_delete' => 0, 'status' => 1]); + }]) + ->where('goods_id', '=', $goodsId) + ->find(); + // 判断商品的状态 + if (!$goods || $goods['is_delete'] || $goods['goods_status']['value'] != 10) { + throw new BaseException(['msg' => '很抱歉,商品信息不存在或已下架']); + } + // 设置商品展示的数据 + $goods = $model->setGoodsListDataFromApi($goods, false, ['userInfo' => $userInfo]); + // 多规格商品sku信息 + $goods['goods_multi_spec'] = $goods['spec_type'] == 20 ? $model->getManySpecData($goods['spec_rel'], $goods['sku']) : null; + return $goods; + } + + /** + * 根据商品id集获取商品列表 + * @param $goodsIds + * @param bool $userInfo + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIdsFromApi($goodsIds, $userInfo = false) + { + // 获取商品列表 + $data = parent::getListByIds($goodsIds, 10); + // 整理列表数据并返回 + return $this->setGoodsListDataFromApi($data, true, ['userInfo' => $userInfo]); + } + + + /** + * 设置商品展示的数据 api模块 + * @param $data + * @param bool $isMultiple + * @param array $param + * @return mixed + */ + private function setGoodsListDataFromApi(&$data, $isMultiple, $param) + { + return parent::setGoodsListData($data, $isMultiple, function ($goods) use ($param) { + // 计算并设置商品会员价 + $this->setGoodsGradeMoney($param['userInfo'], $goods); + }); + } + + /** + * 设置商品的会员价 + * @param $user + * @param $goods + */ + private function setGoodsGradeMoney($user, &$goods) + { + // 会员等级状态 + $gradeStatus = (!empty($user) && $user['grade_id'] > 0 && !empty($user['grade'])) + && (!$user['grade']['is_delete'] && $user['grade']['status']); + // 判断商品是否参与会员折扣 + if (!$gradeStatus || !$goods['is_enable_grade']) { + $goods['is_user_grade'] = false; + return; + } + // 商品单独设置了会员折扣 + if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$user['grade_id']])) { + // 折扣比例 + $discountRatio = helper::bcdiv($goods['alone_grade_equity'][$user['grade_id']], 10); + } else { + // 折扣比例 + $discountRatio = helper::bcdiv($user['grade']['equity']['discount'], 10); + } + if ($discountRatio > 0) { + // 标记参与会员折扣 + $goods['is_user_grade'] = true; + // 会员折扣价 + foreach ($goods['sku'] as &$skuItem) { + $skuItem['goods_price'] = helper::number2(helper::bcmul($skuItem['goods_price'], $discountRatio), true); + $skuItem['sharing_price'] = helper::number2(helper::bcmul($skuItem['sharing_price'], $discountRatio), true); + } + } + } + +} diff --git a/source/application/api/model/sharing/GoodsImage.php b/source/application/api/model/sharing/GoodsImage.php new file mode 100644 index 0000000..1adb416 --- /dev/null +++ b/source/application/api/model/sharing/GoodsImage.php @@ -0,0 +1,23 @@ + 0, // 参与的拼单id + 'delivery' => null, // 配送方式 + 'shop_id' => 0, // 自提门店id + 'linkman' => '', // 自提联系人 + 'phone' => '', // 自提联系电话 + 'coupon_id' => 0, // 优惠券id + 'remark' => '', // 买家留言 + 'pay_type' => PayTypeEnum::WECHAT, // 支付方式 + ]; + + /** + * 待支付订单详情 + * @param $orderNo + * @return null|static + * @throws \think\exception\DbException + */ + public static function getPayDetail($orderNo) + { + return self::get(['order_no' => $orderNo, 'pay_status' => 10, 'is_delete' => 0], ['goods', 'user']); + } + + /** + * 获取订单商品列表 + * @param $params + * @return array + */ + public function getOrderGoodsListByNow($params) + { + // 商品详情 + /* @var GoodsModel $goods */ + $goods = GoodsModel::detail($params['goods_id']); + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $params['goods_sku_id']); + // 商品列表 + $goodsList = [$goods->hidden(['category', 'content', 'image', 'sku'])]; + foreach ($goodsList as &$item) { + // 商品单价(根据order_type判断单买还是拼单) + // order_type:下单类型 10 => 单独购买,20 => 拼团 + $item['goods_price'] = $params['order_type'] == 10 ? $item['goods_sku']['goods_price'] + : $item['goods_sku']['sharing_price']; + // 商品购买数量 + $item['total_num'] = $params['goods_num']; + $item['spec_sku_id'] = $item['goods_sku']['spec_sku_id']; + // 商品购买总金额 + $item['total_price'] = helper::bcmul($item['goods_price'], $params['goods_num']); + } + return $goodsList; + } + + /** + * 订单支付事件 + * @param int $payType + * @return bool + * @throws \think\exception\DbException + */ + public function onPay($payType = PayTypeEnum::WECHAT) + { + // 判断商品状态、库存 + if (!$this->checkGoodsStatusFromOrder($this['goods'])) { + return false; + } + // 余额支付 + if ($payType == PayTypeEnum::BALANCE) { + return $this->onPaymentByBalance($this['order_no']); + } + return true; + } + + /** + * 构建支付请求的参数 + * @param $user + * @param $order + * @param $payType + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + public function onOrderPayment($user, $order, $payType) + { + if ($payType == PayTypeEnum::WECHAT) { + return $this->onPaymentByWechat($user, $order); + } + return []; + } + + /** + * 构建微信支付请求 + * @param $user + * @param $order + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + private function onPaymentByWechat($user, $order) + { + return PaymentService::wechat( + $user, + $order['order_id'], + $order['order_no'], + $order['pay_price'], + OrderTypeEnum::SHARING + ); + } + + /** + * 余额支付标记订单已支付 + * @param $orderNo + * @return bool + * @throws \think\exception\DbException + */ + public function onPaymentByBalance($orderNo) + { + // 获取订单详情 + $PaySuccess = new PaySuccess($orderNo); + // 发起余额支付 + $status = $PaySuccess->onPaySuccess(PayTypeEnum::BALANCE); + if (!$status) { + $this->error = $PaySuccess->getError(); + } + return $status; + } + + /** + * 验证拼单是否允许加入 + * @param $active_id + * @return bool + * @throws BaseException + * @throws \think\exception\DbException + */ + public function checkActiveIsAllowJoin($active_id) + { + // 拼单详情 + $detail = Active::detail($active_id); + if (!$detail) { + throw new BaseException('很抱歉,拼单不存在'); + } + // 验证当前拼单是否允许加入新成员 + return $detail->checkAllowJoin(); + } + + /** + * 保存上门自提联系人 + * @param $linkman + * @param $phone + * @return false|\think\Model + */ + public function saveOrderExtract($linkman, $phone) + { + // 记忆上门自提联系人(缓存),用于下次自动填写 + UserService::setLastExtract($this['user_id'], trim($linkman), trim($phone)); + // 保存上门自提联系人(数据库) + return $this->extract()->save([ + 'linkman' => trim($linkman), + 'phone' => trim($phone), + 'user_id' => $this['user_id'], + 'wxapp_id' => self::$wxapp_id, + ]); + } + + /** + * 用户拼团订单列表 + * @param $user_id + * @param string $type + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id, $type = 'all') + { + // 筛选条件 + $filter = []; + // 订单数据类型 + switch ($type) { + case 'all': + // 全部 + break; + case 'payment'; + // 待支付 + $filter['pay_status'] = PayStatusEnum::PENDING; + break; + case 'sharing'; + // 拼团中 + $filter['active.status'] = 10; + break; + case 'delivery'; + // 待发货 + $this->where('IF ( (`order`.`order_type` = 20), (`active`.`status` = 20), TRUE)'); + $filter['pay_status'] = 20; + $filter['delivery_status'] = 10; + break; + case 'received'; + // 待收货 + $filter['pay_status'] = 20; + $filter['delivery_status'] = 20; + $filter['receipt_status'] = 10; + break; + case 'comment'; + $filter['order_status'] = 30; + $filter['is_comment'] = 0; + break; + } + return $this->with(['goods.image', 'active']) + ->alias('order') + ->field('order.*, active.status as active_status') + ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT') + ->where('user_id', '=', $user_id) + ->where($filter) + ->where('order.is_delete', '=', 0) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 取消订单 + * @param UserModel $user + * @return bool|false|int + */ + public function cancel($user) + { + // 订单是否已支付 + $isPay = $this['pay_status']['value'] == PayStatusEnum::SUCCESS; + // 已发货订单不可取消 + if ($this['delivery_status']['value'] == 20) { + $this->error = '已发货订单不可取消'; + return false; + } + // 已付款的拼团订单不允许取消 + if ($isPay && $this['order_type']['value'] == 20) { + $this->error = '已付款的拼团订单不允许取消'; + return false; + } + // 订单取消事件 + $this->transaction(function () use ($user, $isPay) { + // 未付款的订单 + if ($isPay == false) { + // 回退商品库存 + (new OrderGoodsModel)->backGoodsStock($this['goods'], $isPay); + // 回退用户优惠券 + $this['coupon_id'] > 0 && UserCouponModel::setIsUse($this['coupon_id'], false); + // 回退用户积分 + $describe = "订单取消:{$this['order_no']}"; + $this['points_num'] > 0 && $user->setIncPoints($this['points_num'], $describe); + } + // 更新订单状态 + return $this->save(['order_status' => $isPay ? OrderStatusEnum::APPLY_CANCEL : OrderStatusEnum::CANCELLED]); + }); + return true; + } + + /** + * 确认收货 + * @return bool|mixed + */ + public function receipt() + { + // 验证订单是否合法 + // 条件1: 订单必须已发货 + // 条件2: 订单必须未收货 + if ($this['delivery_status']['value'] != 20 || $this['receipt_status']['value'] != 10) { + $this->error = '该订单不合法'; + return false; + } + return $this->transaction(function () { + // 更新订单状态 + $status = $this->save([ + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::SHARING); + $OrderCompleteService->complete([$this], static::$wxapp_id); + return $status; + }); + } + + /** + * 获取订单总数 + * @param $user_id + * @param string $type + * @return int|string + * @throws \think\Exception + */ + public function getCount($user_id, $type = 'all') + { + // 筛选条件 + $filter = []; + // 订单数据类型 + switch ($type) { + case 'all': + break; + case 'payment'; + $filter['pay_status'] = PayStatusEnum::PENDING; + break; + case 'received'; + $filter['pay_status'] = PayStatusEnum::SUCCESS; + $filter['delivery_status'] = 20; + $filter['receipt_status'] = 10; + break; + case 'comment'; + $filter['order_status'] = 30; + $filter['is_comment'] = 0; + break; + } + return $this->where('user_id', '=', $user_id) + ->where('order_status', '<>', 20) + ->where($filter) + ->where('is_delete', '=', 0) + ->count(); + } + + /** + * 订单详情 + * @param $order_id + * @param $user_id + * @return array|false|\PDOStatement|string|\think\Model|static + * @throws BaseException + */ + public static function getUserOrderDetail($order_id, $user_id) + { + $order = (new static)->with(['goods' => ['image', 'refund'], 'address', 'express', 'extract_shop']) + ->alias('order') + ->field('order.*, active.status as active_status') + ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT') + ->where([ + 'order_id' => $order_id, + 'user_id' => $user_id, + ])->find(); + if (!$order) { + throw new BaseException(['msg' => '订单不存在']); + } + return $order; + } + + /** + * 判断商品库存不足 (未付款订单) + * @param $goodsList + * @return bool + * @throws \think\exception\DbException + */ + private function checkGoodsStatusFromOrder($goodsList) + { + foreach ($goodsList as $goods) { + // 判断商品是否下架 + if ( + empty($goods['goods']) + || $goods['goods']['goods_status']['value'] != 10 + ) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架"; + return false; + } + // 获取商品的sku信息 + $goodsSku = GoodsSkuModel::detail($goods['goods_id'], $goods['spec_sku_id']); + // sku已不存在 + if (empty($goodsSku)) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单"; + return false; + } + // 付款减库存 + if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['stock_num']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + + /** + * 当前订单是否允许申请售后 + * @return bool + */ + public function isAllowRefund() + { + // 必须是已发货的订单 + if ($this['delivery_status']['value'] != 20) { + return false; + } + // 允许申请售后期限(天) + $refundDays = SettingModel::getItem('trade')['order']['refund_days']; + // 不允许售后 + if ($refundDays == 0) { + return false; + } + // 当前时间超出允许申请售后期限 + if ( + $this['receipt_status'] == 20 + && time() > ($this['receipt_time'] + ((int)$refundDays * 86400)) + ) { + return false; + } + return true; + } + + /** + * 判断当前订单是否允许核销 + * @param static $order + * @return bool + */ + public function checkExtractOrder(&$order) + { + if ( + $order['pay_status']['value'] == PayStatusEnum::SUCCESS + && $order['delivery_type']['value'] == DeliveryTypeEnum::EXTRACT + && $order['delivery_status']['value'] == 10 + // 拼团订单验证拼单状态 + && ($order['order_type']['value'] == 20 ? $order['active']['status']['value'] == 20 : true) + ) { + return true; + } + $this->setError('该订单不能被核销'); + return false; + } + + /** + * 设置错误信息 + * @param $error + */ + private function setError($error) + { + empty($this->error) && $this->error = $error; + } + + /** + * 是否存在错误 + * @return bool + */ + public function hasError() + { + return !empty($this->error); + } + +} diff --git a/source/application/api/model/sharing/OrderAddress.php b/source/application/api/model/sharing/OrderAddress.php new file mode 100644 index 0000000..5897a46 --- /dev/null +++ b/source/application/api/model/sharing/OrderAddress.php @@ -0,0 +1,23 @@ + $order_id, 'is_comment' => 0], ['orderM', 'image']); + } + +} diff --git a/source/application/api/model/sharing/OrderRefund.php b/source/application/api/model/sharing/OrderRefund.php new file mode 100644 index 0000000..8658430 --- /dev/null +++ b/source/application/api/model/sharing/OrderRefund.php @@ -0,0 +1,171 @@ + '已同意退货并已退款', 20 => '已同意换货']; + return $text[$data['type']]; + } + // 已取消 + if ($data['status'] == 30) { + return '已取消'; + } + // 已拒绝 + if ($data['status'] == 10) { +// return '已拒绝'; + return $data['type'] == 10 ? '已拒绝退货退款' : '已拒绝换货'; + } + // 进行中 + if ($data['status'] == 0) { + if ($data['is_agree'] == 0) { + return '等待审核中'; + } + if ($data['type'] == 10) { + return $data['is_user_send'] ? '已发货,待平台确认' : '已同意退货,请及时发货'; + } + } + return $value; + } + + /** + * 获取用户售后单列表 + * @param $user_id + * @param int $state + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id, $state = -1) + { + $state > -1 && $this->where('status', '=', $state); + return $this->with(['order_goods.image']) + ->where('user_id', '=', $user_id) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 用户发货 + * @param $data + * @return false|int + */ + public function delivery($data) + { + if ( + $this['type']['value'] != 10 + || $this['is_agree']['value'] != 10 + || $this['is_user_send'] != 0 + ) { + $this->error = '当前售后单不合法,不允许该操作'; + return false; + } + if ($data['express_id'] <= 0) { + $this->error = '请选择物流公司'; + return false; + } + if (empty($data['express_no'])) { + $this->error = '请填写物流单号'; + return false; + } + return $this->save([ + 'is_user_send' => 1, + 'send_time' => time(), + 'express_id' => (int)$data['express_id'], + 'express_no' => $data['express_no'], + ]); + } + + /** + * 新增售后单记录 + * @param $user + * @param $goods + * @param $data + * @return bool + * @throws \Exception + */ + public function apply($user, $goods, $data) + { + $this->startTrans(); + try { + // 新增售后单记录 + $this->save([ + 'order_goods_id' => $data['order_goods_id'], + 'order_id' => $goods['order_id'], + 'user_id' => $user['user_id'], + 'type' => $data['type'], + 'apply_desc' => $data['content'], + 'is_agree' => 0, + 'status' => 0, + 'wxapp_id' => self::$wxapp_id, + ]); + // 记录凭证图片关系 + if (isset($data['images']) && !empty($data['images'])) { + $this->saveImages($this['order_refund_id'], $data['images']); + } + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 记录售后单图片 + * @param $order_refund_id + * @param $images + * @return bool + * @throws \Exception + */ + private function saveImages($order_refund_id, $images) + { + // 生成评价图片数据 + $data = []; + foreach (explode(',', $images) as $image_id) { + $data[] = [ + 'order_refund_id' => $order_refund_id, + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + } + return !empty($data) && (new OrderRefundImage)->saveAll($data); + } + +} \ No newline at end of file diff --git a/source/application/api/model/sharing/OrderRefundAddress.php b/source/application/api/model/sharing/OrderRefundAddress.php new file mode 100644 index 0000000..f52b961 --- /dev/null +++ b/source/application/api/model/sharing/OrderRefundAddress.php @@ -0,0 +1,23 @@ +getActiveByDate($todayTime, '='); + } + + /** + * 获取当天的活动 + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getNextActive() + { + $todayTime = strtotime(date('Y-m-d')); + return $this->getActiveByDate($todayTime, '>'); + } + + /** + * 根据日期获取活动 + * @param $date + * @param string $op + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getActiveByDate($date, $op = '=') + { + return $this->where('active_date', $op, $date) + ->where('status', '=', 1) + ->where('is_delete', '=', 0) + ->find(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/sharp/ActiveGoods.php b/source/application/api/model/sharp/ActiveGoods.php new file mode 100644 index 0000000..dade77e --- /dev/null +++ b/source/application/api/model/sharp/ActiveGoods.php @@ -0,0 +1,115 @@ +with(['active', 'activeTime']) + ->where('active_time_id', '=', $activeTimeId) + ->where('sharp_goods_id', '=', $sharpGoodsId) + ->find(); + } + + /** + * 获取活动商品详情 + * @param $active + * @param $sharpGoodsId + * @param $isCheckStatus + * @return GoodsModel|bool|\think\model\Collection + * @throws \think\exception\DbException + */ + public function getGoodsActiveDetail($active, $sharpGoodsId, $isCheckStatus = true) + { + // 获取商品详情 + $goods = $this->getGoodsDetail($sharpGoodsId); + if (empty($goods)) return false; + if ($isCheckStatus == true && ($goods['is_delete'] || !$goods['status'])) { + $this->error = '很抱歉,秒杀商品不存在或已下架'; + return false; + } + // 活动商品的销量 + $goods['sales_actual'] = $active['sales_actual']; + // 商品销售进度 + $goods['progress'] = $this->getProgress($active['sales_actual'], $goods['seckill_stock']); + /* @var $goods \think\model\Collection */ + return $goods; + } + + /** + * 获取商品详情 + * @param $sharpGoodsId + * @return GoodsModel|bool + * @throws \think\exception\DbException + */ + private function getGoodsDetail($sharpGoodsId) + { + // 获取秒杀商品详情 + $model = $this->getGoodsModel(); + $sharpGoods = $model::detail($sharpGoodsId, ['sku']); + if (empty($sharpGoods)) { + $this->error = '秒杀商品信息不存在'; + return false; + } + // 获取主商品详情 + $goods = GoodsModel::detail($sharpGoods['goods_id']); + if (empty($goods)) return false; + // 整理商品信息 + $goods['sharp_goods_id'] = $sharpGoods['sharp_goods_id']; + $goods['deduct_stock_type'] = $sharpGoods['deduct_stock_type']; + $goods['limit_num'] = $sharpGoods['limit_num']; + $goods['seckill_stock'] = $sharpGoods['seckill_stock']; + $goods['total_sales'] = $sharpGoods['total_sales']; + $goods['status'] = $sharpGoods['status']; + $goods['is_delete'] = $sharpGoods['is_delete']; + // 商品sku信息 + $goods['sku'] = $this->getSharpSku($sharpGoods['sku'], $goods['sku']); + /* @var \think\Collection $goods */ + return $goods->hidden(['category', 'sku']); + } + + /** + * 获取秒杀商品的sku信息 + * @param $sharpSku + * @param $goodsSku + * @return array + */ + protected function getSharpSku($sharpSku, $goodsSku) + { + $sharpSku = helper::arrayColumn2Key($sharpSku, 'spec_sku_id'); + foreach ($goodsSku as &$item) { + $sharpSkuItem = clone $sharpSku[$item['spec_sku_id']]; + $item['original_price'] = $item['goods_price']; + $item['seckill_price'] = $sharpSkuItem['seckill_price']; + $item['seckill_stock'] = $sharpSkuItem['seckill_stock']; + } + return $goodsSku; + } + +} \ No newline at end of file diff --git a/source/application/api/model/sharp/ActiveTime.php b/source/application/api/model/sharp/ActiveTime.php new file mode 100644 index 0000000..3985d42 --- /dev/null +++ b/source/application/api/model/sharp/ActiveTime.php @@ -0,0 +1,77 @@ +where('active_id', '=', $activeId) + ->where('active_time', '=', $nowTime) + ->where('status', '=', 1) + ->find(); + } + + /** + * 获取下一场活动场次 + * @param $activeId + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getNextActiveTimes($activeId) + { + // 当前的时间点 + $nowTime = date('H'); + return $this->where('active_id', '=', $activeId) + ->where('active_time', '>', $nowTime) + ->where('status', '=', 1) + ->order(['active_time' => 'asc']) + ->select(); + } + + /** + * 获取指定日期最近的活动场次 + * @param $activeId + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getRecentActiveTime($activeId) + { + return $this->where('active_id', '=', $activeId) + ->where('status', '=', 1) + ->order(['active_time' => 'asc']) + ->find(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/sharp/Goods.php b/source/application/api/model/sharp/Goods.php new file mode 100644 index 0000000..4894fe8 --- /dev/null +++ b/source/application/api/model/sharp/Goods.php @@ -0,0 +1,25 @@ +where('is_check', '=', $is_check); + // 获取数量 + $limit != false && $this->limit($limit); + // 获取门店列表数据 + $data = $this->where('is_delete', '=', '0') + ->where('status', '=', '1') + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->select(); + // 根据距离排序 + if (!empty($longitude) && !empty($latitude)) { + return $this->sortByDistance($data, $longitude, $latitude); + } + return $data; + } + + /** + * 根据距离排序 + * @param string $longitude + * @param string $latitude + * @param \think\Collection|false|\PDOStatement|string $data + * @return array + * @throws + */ + private function sortByDistance(&$data, $longitude, $latitude) + { + // 根据距离排序 + $list = $data->isEmpty() ? [] : $data->toArray(); + $sortArr = []; + foreach ($list as &$shop) { + // 计算距离 + $distance = self::getDistance($longitude, $latitude, $shop['longitude'], $shop['latitude']); + // 排序列 + $sortArr[] = $distance; + $shop['distance'] = $distance; + if ($distance >= 1000) { + $distance = bcdiv($distance, 1000, 2); + $shop['distance_unit'] = $distance . 'km'; + } else + $shop['distance_unit'] = $distance . 'm'; + } + // 根据距离排序 + array_multisort($sortArr, SORT_ASC, $list); + return $list; + } + + /** + * 获取两个坐标点的距离 + * @param $ulon + * @param $ulat + * @param $slon + * @param $slat + * @return float + */ + private static function getDistance($ulon, $ulat, $slon, $slat) + { + // 地球半径 + $R = 6378137; + // 将角度转为狐度 + $radLat1 = deg2rad($ulat); + $radLat2 = deg2rad($slat); + $radLng1 = deg2rad($ulon); + $radLng2 = deg2rad($slon); + // 结果 + $s = acos(cos($radLat1) * cos($radLat2) * cos($radLng1 - $radLng2) + sin($radLat1) * sin($radLat2)) * $R; + // 精度 + $s = round($s * 10000) / 10000; + return round($s); + } + + /** + * 根据门店id集获取门店列表 + * @param $shopIds + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($shopIds) + { + // 筛选条件 + $filter = ['shop_id' => ['in', $shopIds]]; + if (!empty($shopIds)) { + $this->orderRaw('field(shop_id, ' . implode(',', $shopIds) . ')'); + } + // 获取商品列表数据 + return $this->with(['logo']) + ->where('is_delete', '=', '0') + ->where('status', '=', '1') + ->where($filter) + ->select(); + } + +} \ No newline at end of file diff --git a/source/application/api/model/store/shop/Clerk.php b/source/application/api/model/store/shop/Clerk.php new file mode 100644 index 0000000..b2721fe --- /dev/null +++ b/source/application/api/model/store/shop/Clerk.php @@ -0,0 +1,65 @@ + '未找到店员信息']); + } + return $model; + } + + /** + * 验证用户是否为核销员 + * @param $shop_id + * @return bool + */ + public function checkUser($shop_id) + { + if ($this['is_delete']) { + $this->error = '未找到店员信息'; + return false; + } + if ($this['shop_id'] != $shop_id) { + $this->error = '当前店员不属于该门店,没有核销权限'; + return false; + } + if (!$this['status']) { + $this->error = '当前店员状态已被禁用'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/model/store/shop/ClerkRel.php b/source/application/api/model/store/shop/ClerkRel.php new file mode 100644 index 0000000..c8539a3 --- /dev/null +++ b/source/application/api/model/store/shop/ClerkRel.php @@ -0,0 +1,15 @@ +where('user_id', '=', $userId) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/user/Grade.php b/source/application/api/model/user/Grade.php new file mode 100644 index 0000000..bbe247f --- /dev/null +++ b/source/application/api/model/user/Grade.php @@ -0,0 +1,15 @@ +where('user_id', '=', $userId) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/api/model/wow/Order.php b/source/application/api/model/wow/Order.php new file mode 100644 index 0000000..987b8c6 --- /dev/null +++ b/source/application/api/model/wow/Order.php @@ -0,0 +1,15 @@ +save([ + 'user_id' => $user_id, + 'form_id' => $form_id, + 'expiry_time' => time() + (7 * 86400) - 10, + 'wxapp_id' => self::$wxapp_id + ]); + } +} \ No newline at end of file diff --git a/source/application/api/service/Basics.php b/source/application/api/service/Basics.php new file mode 100644 index 0000000..e4691cc --- /dev/null +++ b/source/application/api/service/Basics.php @@ -0,0 +1,8 @@ +unifiedorder($orderNo, $user['open_id'], $payPrice, $orderType); + // 记录prepay_id + $model = new WxappPrepayIdModel; + $model->add($payment['prepay_id'], $orderId, $user['user_id'], $orderType); + return $payment; + } + +} \ No newline at end of file diff --git a/source/application/api/service/User.php b/source/application/api/service/User.php new file mode 100644 index 0000000..ec6b0f5 --- /dev/null +++ b/source/application/api/service/User.php @@ -0,0 +1,36 @@ + '', 'phone' => '']; + } + +} \ No newline at end of file diff --git a/source/application/api/service/bargain/Amount.php b/source/application/api/service/bargain/Amount.php new file mode 100644 index 0000000..ad9f25f --- /dev/null +++ b/source/application/api/service/bargain/Amount.php @@ -0,0 +1,147 @@ +amount = $amount; + $this->num = $num; + $this->coupon_min = $coupon_min; + } + + /** + * 处理返回 + * @return array + * @throws \Exception + */ + public function handle() + { + // A. 验证 + if ($this->amount < $validAmount = $this->coupon_min * $this->num) { + throw new \Exception('砍价总金额必须≥' . $validAmount . '元'); + } + // B. 分配砍价 + $this->apportion(); + return [ + 'items' => $this->items, + ]; + } + + /** + * 分配砍价 + */ + protected function apportion() + { + $num = $this->num; // 剩余可分配的砍价个数 + $amount = $this->amount; //剩余可领取的砍价金额 + while ($num >= 1) { + // 剩余一个的时候,直接取剩余砍价 + if ($num == 1) { + $coupon_amount = $this->decimal_number($amount); + } else { + $avg_amount = $this->decimal_number($amount / $num); // 剩余的砍价的平均金额 + $coupon_amount = $this->decimal_number( + $this->calcCouponAmount($avg_amount, $amount, $num) + ); + } + $this->items[] = $coupon_amount; // 追加分配 + $amount -= $coupon_amount; + --$num; + } + shuffle($this->items); // 随机打乱 + } + + /** + * 计算分配的砍价金额 + * @param float $avg_amount 每次计算的平均金额 + * @param float $amount 剩余可领取金额 + * @param int $num 剩余可领取的砍价个数 + * + * @return float + */ + protected function calcCouponAmount($avg_amount, $amount, $num) + { + // 如果平均金额小于等于最低金额,则直接返回最低金额 + if ($avg_amount <= $this->coupon_min) { + return $this->coupon_min; + } + // 浮动计算 + $coupon_amount = $this->decimal_number($avg_amount * (1 + $this->apportionRandRatio())); + // 如果低于最低金额或超过可领取的最大金额,则重新获取 + if ($coupon_amount < $this->coupon_min + || $coupon_amount > $this->calcCouponAmountMax($amount, $num) + ) { + return $this->calcCouponAmount($avg_amount, $amount, $num); + } + return $coupon_amount; + } + + /** + * 计算分配的砍价金额-可领取的最大金额 + * @param $amount + * @param $num + * @return float|int + */ + protected function calcCouponAmountMax($amount, $num) + { + return $this->coupon_min + $amount - $num * $this->coupon_min; + } + + /** + * 砍价金额浮动比例 + */ + protected function apportionRandRatio() + { + // 60%机率获取剩余平均值的大幅度砍价(可能正数、可能负数) + if (rand(1, 100) <= 60) { + return rand(-70, 70) / 100; // 上下幅度70% + } + return rand(-30, 30) / 100; // 其他情况,上下浮动30%; + } + + /** + * 格式化金额,保留2位 + * @param float $amount + * @return float + */ + protected function decimal_number($amount) + { + return sprintf('%01.2f', round($amount, 2)); + } +} \ No newline at end of file diff --git a/source/application/api/service/bargain/order/PaySuccess.php b/source/application/api/service/bargain/order/PaySuccess.php new file mode 100644 index 0000000..262bf32 --- /dev/null +++ b/source/application/api/service/bargain/order/PaySuccess.php @@ -0,0 +1,45 @@ +error = '未找到砍价任务信息'; + return false; + } + // 标记为已购买 + $task->setIsBuy(); + // 砍价活动详情 + $active = ActiveModel::detail($task['active_id']); + if (empty($active)) { + $this->error = '未找到砍价活动信息'; + return false; + } + // 累计活动销量 + $active->setIncSales(); + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/coupon/GoodsDeduct.php b/source/application/api/service/coupon/GoodsDeduct.php new file mode 100644 index 0000000..f1f36b0 --- /dev/null +++ b/source/application/api/service/coupon/GoodsDeduct.php @@ -0,0 +1,90 @@ +setActualReducedMoney($reducedMoney, $orderTotalPrice); + // 实际抵扣金额为0, + if ($this->actualReducedMoney > 0) { + // 计算商品的价格权重 + $goodsList = $this->getGoodsListWeight($goodsList, $orderTotalPrice); + // 计算商品优惠券抵扣金额 + $this->setGoodsListCouponMoney($goodsList); + // 总抵扣金额 + $totalCouponMoney = helper::getArrayColumnSum($goodsList, 'coupon_money'); + $this->setGoodsListCouponMoneyFill($goodsList, $totalCouponMoney); + $this->setGoodsListCouponMoneyDiff($goodsList, $totalCouponMoney); + } + return $goodsList; + } + + public function getActualReducedMoney() + { + return $this->actualReducedMoney; + } + + private function setActualReducedMoney($reducedMoney, $orderTotalPrice) + { + $reducedMoney *= 100; + $this->actualReducedMoney = ($reducedMoney >= $orderTotalPrice) ? $orderTotalPrice - 1 : $reducedMoney; + } + + private function arraySortByWeight($goodsList) + { + return array_sort($goodsList, 'weight', true); + } + + private function getGoodsListWeight($goodsList, $orderTotalPrice) + { + foreach ($goodsList as &$goods) { + $goods['weight'] = $goods['total_price'] / $orderTotalPrice; + } + return $this->arraySortByWeight($goodsList); + } + + private function setGoodsListCouponMoney(&$goodsList) + { + foreach ($goodsList as &$goods) { + $goods['coupon_money'] = bcmul($this->actualReducedMoney, $goods['weight']); + } + return true; + } + + private function setGoodsListCouponMoneyFill(&$goodsList, $totalCouponMoney) + { + if ($totalCouponMoney === 0) { + $temReducedMoney = $this->actualReducedMoney; + foreach ($goodsList as &$goods) { + if ($temReducedMoney === 0) break; + $goods['coupon_money'] = 1; + $temReducedMoney--; + } + } + return true; + } + + private function setGoodsListCouponMoneyDiff(&$goodsList, $totalCouponMoney) + { + $tempDiff = $this->actualReducedMoney - $totalCouponMoney; + foreach ($goodsList as &$goods) { + if ($tempDiff < 1) break; + $goods['coupon_money']++ && $tempDiff--; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/master/order/PaySuccess.php b/source/application/api/service/master/order/PaySuccess.php new file mode 100644 index 0000000..5db35ac --- /dev/null +++ b/source/application/api/service/master/order/PaySuccess.php @@ -0,0 +1,43 @@ +becomeDealerUser($order); + return true; + } + + /** + * 购买指定商品成为分销商 + * @param $order + * @return bool + * @throws \think\exception\DbException + */ + private function becomeDealerUser($order) + { + // 整理商品id集 + $goodsIds = helper::getArrayColumn($order['goods'], 'goods_id'); + $model = new DealerApplyModel; + return $model->becomeDealerUser($order['user_id'], $goodsIds, $order['wxapp_id']); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/Checkout.php b/source/application/api/service/order/Checkout.php new file mode 100644 index 0000000..16180e7 --- /dev/null +++ b/source/application/api/service/order/Checkout.php @@ -0,0 +1,856 @@ + null, // 配送方式 + 'shop_id' => 0, // 自提门店id + 'linkman' => '', // 自提联系人 + 'phone' => '', // 自提联系电话 + 'coupon_id' => 0, // 优惠券id + 'is_use_points' => 0, // 是否使用积分抵扣 + 'remark' => '', // 买家留言 + 'pay_type' => PayTypeEnum::WECHAT, // 支付方式 + ]; + + /** + * 订单结算的规则 + * @var array + */ + private $checkoutRule = [ + 'is_user_grade' => true, // 会员等级折扣 + 'is_coupon' => true, // 优惠券抵扣 + 'is_use_points' => true, // 是否使用积分抵扣 + 'is_dealer' => true, // 是否开启分销 + ]; + + /** + * 订单来源 + * @var array + */ + private $orderSource = [ + 'source' => OrderSourceEnum::MASTER, + 'source_id' => 0, + ]; + + /** + * 订单结算数据 + * @var array + */ + private $orderData = []; + + /** + * 构造函数 + * Checkout constructor. + */ + public function __construct() + { + $this->model = new OrderModel; + $this->wxapp_id = OrderModel::$wxapp_id; + } + + /** + * 设置结算台请求的参数 + * @param $param + * @return array + */ + public function setParam($param) + { + $this->param = array_merge($this->param, $param); + return $this->getParam(); + } + + /** + * 获取结算台请求的参数 + * @return array + */ + public function getParam() + { + return $this->param; + } + + /** + * 订单结算的规则 + * @param $data + * @return $this + */ + public function setCheckoutRule($data) + { + $this->checkoutRule = array_merge($this->checkoutRule, $data); + return $this; + } + + /** + * 设置订单来源(普通订单、砍价订单、秒杀订单) + * @param $data + * @return $this + */ + public function setOrderSource($data) + { + $this->orderSource = array_merge($this->orderSource, $data); + return $this; + } + + /** + * 订单确认-结算台 + * @param $user + * @param $goodsList + * @return array + * @throws BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function onCheckout($user, $goodsList) + { + $this->user = $user; + $this->goodsList = $goodsList; + // 订单确认-立即购买 + return $this->checkout(); + } + + /** + * 订单结算台 + * @return array + * @throws BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function checkout() + { + // 整理订单数据 + $this->orderData = $this->getOrderData(); + // 验证商品状态, 是否允许购买 + $this->validateGoodsList(); + // 订单商品总数量 + $orderTotalNum = helper::getArrayColumnSum($this->goodsList, 'total_num'); + // 设置订单商品会员折扣价 + $this->setOrderGoodsGradeMoney(); + // 设置订单商品总金额(不含优惠折扣) + $this->setOrderTotalPrice(); + // 计算可用积分抵扣 + $this->setOrderPoints(); + // 当前用户可用的优惠券列表 + $couponList = $this->getUserCouponList($this->orderData['order_total_price']); + // 计算优惠券抵扣 + $this->setOrderCouponMoney($couponList, $this->param['coupon_id']); + // 计算订单商品的实际付款金额 + $this->setOrderGoodsPayPrice(); + // 设置默认配送方式 + !$this->param['delivery'] && $this->param['delivery'] = current(SettingModel::getItem('store')['delivery_type']); + // 处理配送方式 + if ($this->param['delivery'] == DeliveryTypeEnum::EXPRESS) { + $this->setOrderExpress(); + } elseif ($this->param['delivery'] == DeliveryTypeEnum::EXTRACT) { + $this->param['shop_id'] > 0 && $this->orderData['extract_shop'] = ShopModel::detail($this->param['shop_id']); + } + // 计算订单最终金额 + $this->setOrderPayPrice(); + // 计算订单积分赠送数量 + $this->setOrderPointsBonus(); + // 返回订单数据 + return array_merge([ + 'goods_list' => array_values($this->goodsList), // 商品信息 + 'order_total_num' => $orderTotalNum, // 商品总数量 + 'coupon_list' => array_values($couponList), // 优惠券列表 + 'has_error' => $this->hasError(), + 'error_msg' => $this->getError(), + ], $this->orderData); + } + + /** + * 计算订单可用积分抵扣 + * @return bool + */ + private function setOrderPoints() + { + // 设置默认的商品积分抵扣信息 + $this->setDefaultGoodsPoints(); + // 积分设置 + $setting = SettingModel::getItem('points'); + // 条件:后台开启下单使用积分抵扣 + if (!$setting['is_shopping_discount'] || !$this->checkoutRule['is_use_points']) { + return false; + } + // 条件:订单金额满足[?]元 + if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['order_total_price']) === 1) { + return false; + } + // 计算订单商品最多可抵扣的积分数量 + $this->setOrderGoodsMaxPointsNum(); + // 订单最多可抵扣的积分总数量 + $maxPointsNumCount = helper::getArrayColumnSum($this->goodsList, 'max_points_num'); + // 实际可抵扣的积分数量 + $actualPointsNum = min($maxPointsNumCount, $this->user['points']); + if ($actualPointsNum < 1) { + return false; + } + // 计算订单商品实际抵扣的积分数量和金额 + $GoodsDeduct = new PointsDeductService($this->goodsList); + $GoodsDeduct->setGoodsPoints($maxPointsNumCount, $actualPointsNum); + // 积分抵扣总金额 + $orderPointsMoney = helper::getArrayColumnSum($this->goodsList, 'points_money'); + $this->orderData['points_money'] = helper::number2($orderPointsMoney); + // 积分抵扣总数量 + $this->orderData['points_num'] = $actualPointsNum; + // 允许积分抵扣 + $this->orderData['is_allow_points'] = true; + return true; + } + + /** + * 计算订单商品最多可抵扣的积分数量 + * @return bool + */ + private function setOrderGoodsMaxPointsNum() + { + // 积分设置 + $setting = SettingModel::getItem('points'); + foreach ($this->goodsList as &$goods) { + // 商品不允许积分抵扣 + if (!$goods['is_points_discount']) continue; + // 积分抵扣比例 + $deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100); + // 最多可抵扣的金额 + $maxPointsMoney = helper::bcmul($goods['total_price'], $deductionRatio); + // 最多可抵扣的积分数量 + $goods['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0); + } + return true; + } + + /** + * 设置默认的商品积分抵扣信息 + * @return bool + */ + private function setDefaultGoodsPoints() + { + foreach ($this->goodsList as &$goods) { + // 最多可抵扣的积分数量 + $goods['max_points_num'] = 0; + // 实际抵扣的积分数量 + $goods['points_num'] = 0; + // 实际抵扣的金额 + $goods['points_money'] = 0.00; + } + return true; + } + + /** + * 整理订单数据(结算台初始化) + * @return array + */ + private function getOrderData() + { + // 系统支持的配送方式 (后台设置) + $deliveryType = SettingModel::getItem('store')['delivery_type']; + // 积分设置 + $pointsSetting = SettingModel::getItem('points'); + return [ + // 配送类型 + 'delivery' => $this->param['delivery'] > 0 ? $this->param['delivery'] : $deliveryType[0], + // 默认地址 + 'address' => $this->user['address_default'], + // 是否存在收货地址 + 'exist_address' => $this->user['address_id'] > 0, + // 配送费用 + 'express_price' => 0.00, + // 当前用户收货城市是否存在配送规则中 + 'intra_region' => true, + // 自提门店信息 + 'extract_shop' => [], + // 是否允许使用积分抵扣 + 'is_allow_points' => false, + // 是否使用积分抵扣 + 'is_use_points' => $this->param['is_use_points'], + // 积分抵扣金额 + 'points_money' => 0.00, + // 赠送的积分数量 + 'points_bonus' => 0, + // 支付方式 + 'pay_type' => $this->param['pay_type'], + // 系统设置 + 'setting' => [ + 'delivery' => $deliveryType, // 支持的配送方式 + 'points_name' => $pointsSetting['points_name'], // 积分名称 + 'points_describe' => $pointsSetting['describe'], // 积分说明 + ], + // 记忆的自提联系方式 + 'last_extract' => UserService::getLastExtract($this->user['user_id']), + // todo: delete - 兼容处理 + 'deliverySetting' => $deliveryType, + ]; + } + + /** + * 当前用户可用的优惠券列表 + * @param $orderTotalPrice + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getUserCouponList($orderTotalPrice) + { + // 是否开启优惠券折扣 + if (!$this->checkoutRule['is_coupon']) { + return []; + } + return UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice); + } + + /** + * 验证订单商品的状态 + * @return bool + */ + private function validateGoodsList() + { + $Checkout = CheckoutFactory::getFactory( + $this->user, + $this->goodsList, + $this->orderSource['source'] + ); + $status = $Checkout->validateGoodsList(); + $status == false && $this->setError($Checkout->getError()); + return $status; + } + + /** + * 设置订单的商品总金额(不含优惠折扣) + */ + private function setOrderTotalPrice() + { + // 订单商品的总金额(不含优惠券折扣) + $this->orderData['order_total_price'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_price')); + } + + /** + * 设置订单的实际支付金额(含配送费) + */ + private function setOrderPayPrice() + { + // 订单金额(含优惠折扣) + $this->orderData['order_price'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_pay_price')); + // 订单实付款金额(订单金额 + 运费) + $this->orderData['order_pay_price'] = helper::number2(helper::bcadd($this->orderData['order_price'], $this->orderData['express_price'])); + } + + /** + * 计算订单积分赠送数量 + * @return bool + */ + private function setOrderPointsBonus() + { + // 初始化商品积分赠送数量 + foreach ($this->goodsList as &$goods) { + $goods['points_bonus'] = 0; + } + // 积分设置 + $setting = SettingModel::getItem('points'); + // 条件:后台开启开启购物送积分 + if (!$setting['is_shopping_gift']) { + return false; + } + // 设置商品积分赠送数量 + foreach ($this->goodsList as &$goods) { + // 积分赠送比例 + $ratio = $setting['gift_ratio'] / 100; + // 计算抵扣积分数量 + $goods['points_bonus'] = !$goods['is_points_gift'] ? 0 : helper::bcmul($goods['total_pay_price'], $ratio, 0); + } + // 订单积分赠送数量 + $this->orderData['points_bonus'] = helper::getArrayColumnSum($this->goodsList, 'points_bonus'); + return true; + } + + /** + * 计算订单商品的实际付款金额 + * @return bool + */ + private function setOrderGoodsPayPrice() + { + // 商品总价 - 优惠抵扣 + foreach ($this->goodsList as &$goods) { + // 减去优惠券抵扣金额 + $value = helper::bcsub($goods['total_price'], $goods['coupon_money']); + // 减去积分抵扣金额 + if ($this->orderData['is_allow_points'] && $this->orderData['is_use_points']) { + $value = helper::bcsub($value, $goods['points_money']); + } + $goods['total_pay_price'] = helper::number2($value); + } + return true; + } + + /** + * 设置订单商品会员折扣价 + * @return bool + */ + private function setOrderGoodsGradeMoney() + { + // 设置默认数据 + helper::setDataAttribute($this->goodsList, [ + // 标记参与会员折扣 + 'is_user_grade' => false, + // 会员等级抵扣的金额 + 'grade_ratio' => 0, + // 会员折扣的商品单价 + 'grade_goods_price' => 0.00, + // 会员折扣的总额差 + 'grade_total_money' => 0.00, + ], true); + + // 是否开启会员等级折扣 + if (!$this->checkoutRule['is_user_grade']) { + return false; + } + // 会员等级状态 + if (!( + $this->user['grade_id'] > 0 && !empty($this->user['grade']) + && !$this->user['grade']['is_delete'] && $this->user['grade']['status'] + )) { + return false; + } + // 计算抵扣金额 + foreach ($this->goodsList as &$goods) { + // 判断商品是否参与会员折扣 + if (!$goods['is_enable_grade']) { + continue; + } + // 商品单独设置了会员折扣 + if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$this->user['grade_id']])) { + // 折扣比例 + $discountRatio = helper::bcdiv($goods['alone_grade_equity'][$this->user['grade_id']], 10); + } else { + // 折扣比例 + $discountRatio = helper::bcdiv($this->user['grade']['equity']['discount'], 10); + } + if ($discountRatio > 0) { + // 会员折扣后的商品总金额 + $gradeTotalPrice = max(0.01, helper::bcmul($goods['total_price'], $discountRatio)); + helper::setDataAttribute($goods, [ + 'is_user_grade' => true, + 'grade_ratio' => $discountRatio, + 'grade_goods_price' => helper::number2(helper::bcmul($goods['goods_price'], $discountRatio), true), + 'grade_total_money' => helper::number2(helper::bcsub($goods['total_price'], $gradeTotalPrice)), + 'total_price' => $gradeTotalPrice, + ], false); + } + } + return true; + } + + /** + * 设置订单优惠券抵扣信息 + * @param array $couponList 当前用户可用的优惠券列表 + * @param int $couponId 当前选择的优惠券id + * @return bool + * @throws BaseException + */ + private function setOrderCouponMoney($couponList, $couponId) + { + // 设置默认数据:订单信息 + helper::setDataAttribute($this->orderData, [ + 'coupon_id' => 0, // 用户优惠券id + 'coupon_money' => 0, // 优惠券抵扣金额 + ], false); + // 设置默认数据:订单商品列表 + helper::setDataAttribute($this->goodsList, [ + 'coupon_money' => 0, // 优惠券抵扣金额 + ], true); + // 是否开启优惠券折扣 + if (!$this->checkoutRule['is_coupon']) { + return false; + } + // 如果没有可用的优惠券,直接返回 + if ($couponId <= 0 || empty($couponList)) { + return true; + } + // 获取优惠券信息 + $couponInfo = helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId); + if ($couponInfo == false) { + throw new BaseException(['msg' => '未找到优惠券信息']); + } + // 计算订单商品优惠券抵扣金额 + $goodsListTemp = helper::getArrayColumns($this->goodsList, ['total_price']); + $CouponMoney = new GoodsDeductService; + $completed = $CouponMoney->setGoodsCouponMoney($goodsListTemp, $couponInfo['reduced_price']); + // 分配订单商品优惠券抵扣金额 + foreach ($this->goodsList as $key => &$goods) { + $goods['coupon_money'] = $completed[$key]['coupon_money'] / 100; + } + // 记录订单优惠券信息 + $this->orderData['coupon_id'] = $couponId; + $this->orderData['coupon_money'] = helper::number2($CouponMoney->getActualReducedMoney() / 100); + return true; + } + + /** + * 订单配送-快递配送 + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function setOrderExpress() + { + // 设置默认数据:配送费用 + helper::setDataAttribute($this->goodsList, [ + 'express_price' => 0, + ], true); + // 当前用户收货城市id + $cityId = $this->user['address_default'] ? $this->user['address_default']['city_id'] : null; + // 初始化配送服务类 + $ExpressService = new ExpressService($cityId, $this->goodsList, OrderTypeEnum::MASTER); + // 验证商品是否在配送范围 + $isIntraRegion = $ExpressService->isIntraRegion(); + if ($cityId > 0 && $isIntraRegion == false) { + $notInRuleGoodsName = $ExpressService->getNotInRuleGoodsName(); + $this->setError("很抱歉,您的收货地址不在商品 [{$notInRuleGoodsName}] 的配送范围内"); + } + // 订单总运费金额 + $this->orderData['intra_region'] = $isIntraRegion; + $this->orderData['express_price'] = $ExpressService->getDeliveryFee(); + return true; + } + + /** + * 创建新订单 + * @param array $order 订单信息 + * @return bool + * @throws \Exception + */ + public function createOrder($order) + { + // 表单验证 + if (!$this->validateOrderForm($order, $this->param['linkman'], $this->param['phone'])) { + return false; + } + // 创建新的订单 + $status = $this->model->transaction(function () use ($order) { + // 创建订单事件 + return $this->createOrderEvent($order); + }); + // 余额支付标记订单已支付 + if ($status && $order['pay_type'] == PayTypeEnum::BALANCE) { + return $this->model->onPaymentByBalance($this->model['order_no']); + } + return $status; + } + + /** + * 创建订单事件 + * @param $order + * @return bool + * @throws BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function createOrderEvent($order) + { + // 新增订单记录 + $status = $this->add($order, $this->param['remark']); + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + // 记录收货地址 + $this->saveOrderAddress($order['address']); + } elseif ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + // 记录自提信息 + $this->saveOrderExtract($this->param['linkman'], $this->param['phone']); + } + // 保存订单商品信息 + $this->saveOrderGoods($order); + // 更新商品库存 (针对下单减库存的商品) + $this->updateGoodsStockNum($order); + // 设置优惠券使用状态 + UserCouponModel::setIsUse($this->param['coupon_id']); + // 积分抵扣情况下扣除用户积分 + if ($order['is_allow_points'] && $order['is_use_points'] && $order['points_num'] > 0) { + $describe = "用户消费:{$this->model['order_no']}"; + $this->user->setIncPoints(-$order['points_num'], $describe); + } + // 获取订单详情 + $detail = OrderModel::getUserOrderDetail($this->model['order_id'], $this->user['user_id']); + // 记录分销商订单 + $this->checkoutRule['is_dealer'] && DealerOrderModel::createOrder($detail); + return $status; + } + + /** + * 构建支付请求的参数 + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + public function onOrderPayment() + { + return PaymentService::orderPayment($this->user, $this->model, $this->param['pay_type']); + } + + /** + * 表单验证 (订单提交) + * @param array $order 订单信息 + * @param string $linkman 联系人 + * @param string $phone 联系电话 + * @return bool + */ + private function validateOrderForm(&$order, $linkman, $phone) + { + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + if (empty($order['address'])) { + $this->error = '您还没有选择配送地址'; + return false; + } + } + if ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + if (empty($order['extract_shop'])) { + $this->error = '您还没有选择自提门店'; + return false; + } + if (empty($linkman) || empty($phone)) { + $this->error = '您还没有填写联系人和电话'; + return false; + } + } + // 余额支付时判断用户余额是否足够 + if ($order['pay_type'] == PayTypeEnum::BALANCE) { + if ($this->user['balance'] < $order['order_pay_price']) { + $this->error = '您的余额不足,无法使用余额支付'; + return false; + } + } + return true; + } + + /** + * 当前订单是否存在和使用积分抵扣 + * @param $order + * @return bool + */ + private function isExistPointsDeduction($order) + { + return $order['is_allow_points'] && $order['is_use_points']; + } + + /** + * 新增订单记录 + * @param $order + * @param string $remark + * @return false|int + */ + private function add($order, $remark = '') + { + // 当前订单是否存在和使用积分抵扣 + $isExistPointsDeduction = $this->isExistPointsDeduction($order); + // 订单数据 + $data = [ + 'user_id' => $this->user['user_id'], + 'order_no' => $this->model->orderNo(), + 'total_price' => $order['order_total_price'], + 'order_price' => $order['order_price'], + 'coupon_id' => $order['coupon_id'], + 'coupon_money' => $order['coupon_money'], + 'points_money' => $isExistPointsDeduction ? $order['points_money'] : 0.00, + 'points_num' => $isExistPointsDeduction ? $order['points_num'] : 0, + 'pay_price' => $order['order_pay_price'], + 'delivery_type' => $order['delivery'], + 'pay_type' => $order['pay_type'], + 'buyer_remark' => trim($remark), + 'order_source' => $this->orderSource['source'], + 'order_source_id' => $this->orderSource['source_id'], + 'points_bonus' => $order['points_bonus'], + 'wxapp_id' => $this->wxapp_id, + ]; + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + $data['express_price'] = $order['express_price']; + } elseif ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + $data['extract_shop_id'] = $order['extract_shop']['shop_id']; + } + // 保存订单记录 + return $this->model->save($data); + } + + /** + * 保存订单商品信息 + * @param $order + * @return int + */ + private function saveOrderGoods($order) + { + // 当前订单是否存在和使用积分抵扣 + $isExistPointsDeduction = $this->isExistPointsDeduction($order); + // 订单商品列表 + $goodsList = []; + foreach ($order['goods_list'] as $goods) { + /* @var GoodsModel $goods */ + $item = [ + 'user_id' => $this->user['user_id'], + 'wxapp_id' => $this->wxapp_id, + 'goods_id' => $goods['goods_id'], + 'goods_name' => $goods['goods_name'], + 'image_id' => $goods['image'][0]['image_id'], + 'deduct_stock_type' => $goods['deduct_stock_type'], + 'spec_type' => $goods['spec_type'], + 'spec_sku_id' => $goods['goods_sku']['spec_sku_id'], + 'goods_sku_id' => $goods['goods_sku']['goods_sku_id'], + 'goods_attr' => $goods['goods_sku']['goods_attr'], + 'content' => $goods['content'], + 'goods_no' => $goods['goods_sku']['goods_no'], + 'goods_price' => $goods['goods_sku']['goods_price'], + 'line_price' => $goods['goods_sku']['line_price'], + 'goods_weight' => $goods['goods_sku']['goods_weight'], + 'is_user_grade' => (int)$goods['is_user_grade'], + 'grade_ratio' => $goods['grade_ratio'], + 'grade_goods_price' => $goods['grade_goods_price'], + 'grade_total_money' => $goods['grade_total_money'], + 'coupon_money' => $goods['coupon_money'], + 'points_money' => $isExistPointsDeduction ? $goods['points_money'] : 0.00, + 'points_num' => $isExistPointsDeduction ? $goods['points_num'] : 0, + 'points_bonus' => $goods['points_bonus'], + 'total_num' => $goods['total_num'], + 'total_price' => $goods['total_price'], + 'total_pay_price' => $goods['total_pay_price'], + 'is_ind_dealer' => $goods['is_ind_dealer'], + 'dealer_money_type' => $goods['dealer_money_type'], + 'first_money' => $goods['first_money'], + 'second_money' => $goods['second_money'], + 'third_money' => $goods['third_money'], + ]; + // 记录订单商品来源id + $item['goods_source_id'] = isset($goods['goods_source_id']) ? $goods['goods_source_id'] : 0; + $goodsList[] = $item; + } + return $this->model->goods()->saveAll($goodsList); + } + + /** + * 更新商品库存 (针对下单减库存的商品) + * @param $order + * @return mixed + */ + private function updateGoodsStockNum($order) + { + return StockFactory::getFactory($this->model['order_source'])->updateGoodsStock($order['goods_list']); + } + + /** + * 记录收货地址 + * @param $address + * @return false|\think\Model + */ + private function saveOrderAddress($address) + { + if ($address['region_id'] == 0 && !empty($address['district'])) { + $address['detail'] = $address['district'] . ' ' . $address['detail']; + } + return $this->model->address()->save([ + 'user_id' => $this->user['user_id'], + 'wxapp_id' => $this->wxapp_id, + 'name' => $address['name'], + 'phone' => $address['phone'], + 'province_id' => $address['province_id'], + 'city_id' => $address['city_id'], + 'region_id' => $address['region_id'], + 'detail' => $address['detail'], + ]); + } + + /** + * 保存上门自提联系人 + * @param $linkman + * @param $phone + * @return false|\think\Model + */ + private function saveOrderExtract($linkman, $phone) + { + // 记忆上门自提联系人(缓存),用于下次自动填写 + UserService::setLastExtract($this->model['user_id'], trim($linkman), trim($phone)); + // 保存上门自提联系人(数据库) + return $this->model->extract()->save([ + 'linkman' => trim($linkman), + 'phone' => trim($phone), + 'user_id' => $this->model['user_id'], + 'wxapp_id' => $this->wxapp_id, + ]); + } + + /** + * 设置错误信息 + * @param $error + */ + protected function setError($error) + { + empty($this->error) && $this->error = $error; + } + + /** + * 获取错误信息 + * @return mixed + */ + public function getError() + { + return $this->error ?: ''; + } + + /** + * 是否存在错误 + * @return bool + */ + public function hasError() + { + return !empty($this->error); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/PaySuccess.php b/source/application/api/service/order/PaySuccess.php new file mode 100644 index 0000000..2a4443e --- /dev/null +++ b/source/application/api/service/order/PaySuccess.php @@ -0,0 +1,150 @@ +model = OrderModel::getPayDetail($orderNo); + if (!empty($this->model)) { + $this->wxappId = $this->model['wxapp_id']; + } + // 获取用户信息 + $this->user = UserModel::detail($this->model['user_id']); + } + + /** + * 获取订单详情 + * @return OrderModel|null + */ + public function getOrderInfo() + { + return $this->model; + } + + /** + * 订单支付成功业务处理 + * @param $payType + * @param array $payData + * @return bool + */ + public function onPaySuccess($payType, $payData = []) + { + if (empty($this->model)) { + $this->error = '未找到该订单信息'; + return false; + } + // 更新付款状态 + $status = $this->updatePayStatus($payType, $payData); + // 订单支付成功行为 + if ($status == true) { + Hook::listen('order_pay_success', $this->model, OrderTypeEnum::MASTER); + } + return $status; + } + + /** + * 更新付款状态 + * @param $payType + * @param array $payData + * @return bool + */ + private function updatePayStatus($payType, $payData = []) + { + // 验证余额支付时用户余额是否满足 + if ($payType == PayTypeEnum::BALANCE) { + if ($this->user['balance'] < $this->model['pay_price']) { + $this->error = '用户余额不足,无法使用余额支付'; + return false; + } + } + // 事务处理 + $this->model->transaction(function () use ($payType, $payData) { + // 更新订单状态 + $this->updateOrderInfo($payType, $payData); + // 累积用户总消费金额 + $this->user->setIncPayMoney($this->model['pay_price']); + // 记录订单支付信息 + $this->updatePayInfo($payType); + }); + return true; + } + + /** + * 更新订单记录 + * @param $payType + * @param $payData + * @return false|int + * @throws \Exception + */ + private function updateOrderInfo($payType, $payData) + { + // 更新商品库存、销量 + StockFactory::getFactory($this->model['order_source'])->updateStockSales($this->model['goods']); + // 整理订单信息 + $order = [ + 'pay_type' => $payType, + 'pay_status' => 20, + 'pay_time' => time() + ]; + if ($payType == PayTypeEnum::WECHAT) { + $order['transaction_id'] = $payData['transaction_id']; + } + // 更新订单状态 + return $this->model->save($order); + } + + /** + * 记录订单支付信息 + * @param $payType + * @throws \think\Exception + */ + private function updatePayInfo($payType) + { + // 余额支付 + if ($payType == PayTypeEnum::BALANCE) { + // 更新用户余额 + $this->user->setDec('balance', $this->model['pay_price']); + BalanceLogModel::add(SceneEnum::CONSUME, [ + 'user_id' => $this->user['user_id'], + 'money' => -$this->model['pay_price'], + ], ['order_no' => $this->model['order_no']]); + } + // 微信支付 + if ($payType == PayTypeEnum::WECHAT) { + // 更新prepay_id记录 + WxappPrepayIdModel::updatePayStatus($this->model['order_id'], OrderTypeEnum::MASTER); + } + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/Bargain.php b/source/application/api/service/order/source/Bargain.php new file mode 100644 index 0000000..5ef9d16 --- /dev/null +++ b/source/application/api/service/order/source/Bargain.php @@ -0,0 +1,70 @@ +checkOrderStatusOnPayCommon($order)) { + return false; + } + // 判断商品状态、库存 + if (!$this->checkGoodsStatusOnPay($order['goods'])) { + return false; + } + return true; + } + + /** + * 判断商品状态、库存 (未付款订单) + * @param $goodsList + * @return bool + * @throws \think\exception\DbException + */ + protected function checkGoodsStatusOnPay($goodsList) + { + foreach ($goodsList as $goods) { + // 获取商品的sku信息 + $goodsSku = $this->getOrderGoodsSku($goods['goods_id'], $goods['spec_sku_id']); + // sku已不存在 + if (empty($goodsSku)) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单"; + return false; + } + // 付款减库存 + if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['stock_num']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + + /** + * 获取指定的商品sku信息 + * @param $goodsId + * @param $specSkuId + * @return \app\common\model\GoodsSku|null + * @throws \think\exception\DbException + */ + private function getOrderGoodsSku($goodsId, $specSkuId) + { + return GoodsSkuModel::detail($goodsId, $specSkuId); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/Basics.php b/source/application/api/service/order/source/Basics.php new file mode 100644 index 0000000..3d3fdf3 --- /dev/null +++ b/source/application/api/service/order/source/Basics.php @@ -0,0 +1,43 @@ +error = '很抱歉,当前订单不合法,无法支付'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/Factory.php b/source/application/api/service/order/source/Factory.php new file mode 100644 index 0000000..3f29fd6 --- /dev/null +++ b/source/application/api/service/order/source/Factory.php @@ -0,0 +1,36 @@ + 'Master', + OrderSourceEnum::BARGAIN => 'Bargain', + OrderSourceEnum::SHARP => 'Sharp', + ]; + + /** + * 根据订单来源获取商品库存类 + * @param int $orderSource + * @return mixed + */ + public static function getFactory($orderSource = OrderSourceEnum::MASTER) + { + static $classObj = []; + if (!isset($classObj[$orderSource])) { + $className = __NAMESPACE__ . '\\' . static::$class[$orderSource]; + $classObj[$orderSource] = new $className(); + } + return $classObj[$orderSource]; + } +} \ No newline at end of file diff --git a/source/application/api/service/order/source/Master.php b/source/application/api/service/order/source/Master.php new file mode 100644 index 0000000..8d7e6ea --- /dev/null +++ b/source/application/api/service/order/source/Master.php @@ -0,0 +1,78 @@ +checkOrderStatusOnPayCommon($order)) { + return false; + } + // 判断商品状态、库存 + if (!$this->checkGoodsStatusOnPay($order['goods'])) { + return false; + } + return true; + } + + /** + * 判断商品状态、库存 (未付款订单) + * @param $goodsList + * @return bool + * @throws \think\exception\DbException + */ + protected function checkGoodsStatusOnPay($goodsList) + { + foreach ($goodsList as $goods) { + // 判断商品是否下架 + if ( + empty($goods['goods']) + || $goods['goods']['goods_status']['value'] != 10 + ) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架"; + return false; + } + // 获取商品的sku信息 + $goodsSku = $this->getOrderGoodsSku($goods['goods_id'], $goods['spec_sku_id']); + // sku已不存在 + if (empty($goodsSku)) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单"; + return false; + } + // 付款减库存 + if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['stock_num']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + + /** + * 获取指定的商品sku信息 + * @param $goodsId + * @param $specSkuId + * @return \app\common\model\GoodsSku|null + * @throws \think\exception\DbException + */ + private function getOrderGoodsSku($goodsId, $specSkuId) + { + return GoodsSkuModel::detail($goodsId, $specSkuId); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/Sharp.php b/source/application/api/service/order/source/Sharp.php new file mode 100644 index 0000000..eae5dee --- /dev/null +++ b/source/application/api/service/order/source/Sharp.php @@ -0,0 +1,76 @@ +checkOrderStatusOnPayCommon($order)) { + return false; + } + // 判断商品状态、库存 + if (!$this->checkGoodsStatusOnPay($order['goods'])) { + return false; + } + return true; + } + + /** + * 判断商品状态、库存 (未付款订单) + * @param $goodsList + * @return bool + * @throws \think\exception\DbException + */ + protected function checkGoodsStatusOnPay($goodsList) + { + foreach ($goodsList as $goods) { + // 秒杀商品信息 + $sharpGoods = SharpGoodsModel::detail($goods['goods_source_id'], ['sku']); + // 判断商品是否下架 + if (empty($sharpGoods) || $sharpGoods['is_delete'] || !$sharpGoods['status']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 不存在或已下架"; + return false; + } + // 获取秒杀商品的sku信息 + $goodsSku = $this->getOrderGoodsSku($sharpGoods, $goods['spec_sku_id']); + if (empty($goodsSku)) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单"; + return false; + } + // 付款减库存 + if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['seckill_stock']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + + /** + * 获取指定的商品sku信息 + * @param $sharpGoods + * @param $specSkuId + * @return bool + */ + private function getOrderGoodsSku($sharpGoods, $specSkuId) + { + return helper::getArrayItemByColumn($sharpGoods['sku'], 'spec_sku_id', $specSkuId); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/checkout/Bargain.php b/source/application/api/service/order/source/checkout/Bargain.php new file mode 100644 index 0000000..41d1c84 --- /dev/null +++ b/source/application/api/service/order/source/checkout/Bargain.php @@ -0,0 +1,28 @@ +goodsList as $goods) { + // 判断商品库存 + if ($goods['total_num'] > $goods['goods_sku']['stock_num']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/checkout/Basics.php b/source/application/api/service/order/source/checkout/Basics.php new file mode 100644 index 0000000..107c5e3 --- /dev/null +++ b/source/application/api/service/order/source/checkout/Basics.php @@ -0,0 +1,38 @@ +user = $user; + $this->goodsList = $goodsList; + } + + /** + * 验证商品列表 + * @return mixed + */ + abstract public function validateGoodsList(); + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/checkout/Factory.php b/source/application/api/service/order/source/checkout/Factory.php new file mode 100644 index 0000000..02dd8af --- /dev/null +++ b/source/application/api/service/order/source/checkout/Factory.php @@ -0,0 +1,35 @@ + 'Master', + OrderSourceEnum::BARGAIN => 'Bargain', + OrderSourceEnum::SHARP => 'Sharp', + ]; + + /** + * 根据订单来源获取商品库存类 + * @param $user + * @param $goodsList + * @param int $orderSource + * @return mixed + */ + public static function getFactory($user, $goodsList, $orderSource = OrderSourceEnum::MASTER) + { + $className = __NAMESPACE__ . '\\' . static::$class[$orderSource]; + return new $className($user, $goodsList); + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/checkout/Master.php b/source/application/api/service/order/source/checkout/Master.php new file mode 100644 index 0000000..4a134c4 --- /dev/null +++ b/source/application/api/service/order/source/checkout/Master.php @@ -0,0 +1,33 @@ +goodsList as $goods) { + // 判断商品是否下架 + if ($goods['goods_status']['value'] != 10) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架"; + return false; + } + // 判断商品库存 + if ($goods['total_num'] > $goods['goods_sku']['stock_num']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/order/source/checkout/Sharp.php b/source/application/api/service/order/source/checkout/Sharp.php new file mode 100644 index 0000000..da8f573 --- /dev/null +++ b/source/application/api/service/order/source/checkout/Sharp.php @@ -0,0 +1,96 @@ +validateGoodsStatus()) { + return false; + } + // 验证商品限购 + if (!$this->validateLimitNum()) { + return false; + } + // 判断商品库存 + if (!$this->validateGoodsSeckillStock()) { + return false; + } + return true; + } + + /** + * 判断商品是否下架 + * @return bool + */ + private function validateGoodsStatus() + { + foreach ($this->goodsList as $goods) { + if ($goods['is_delete'] || !$goods['status']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架"; + return false; + } + } + return true; + } + + /** + * 判断商品是否下架 + * @return bool + */ + private function validateGoodsSeckillStock() + { + foreach ($this->goodsList as $goods) { + if ($goods['total_num'] > $goods['goods_sku']['seckill_stock']) { + $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足"; + return false; + } + } + return true; + } + + /** + * 验证商品限购 + * @return bool + */ + public function validateLimitNum() + { + foreach ($this->goodsList as $goods) { + // 不限购 + if ($goods['limit_num'] <= 0) return true; + // 获取用户已下单的件数(未取消 订单来源) + $alreadyBuyNum = SharpOrderService::getAlreadyBuyNum($this->user['user_id'], $goods['goods_id']); + // 情况1: 已购买0件, 实际想购买5件 + if ($alreadyBuyNum == 0 && $goods['total_num'] > $goods['limit_num']) { + $this->error = "很抱歉,该商品限购{$goods['limit_num']}件,请修改购买数量"; + return false; + } + // 情况2: 已购买3件, 实际想购买1件 + if ($alreadyBuyNum >= $goods['limit_num']) { + $this->error = "很抱歉,该商品限购{$goods['limit_num']}件,您当前已下单{$alreadyBuyNum}件,无法购买"; + return false; + } + // 情况3: 已购买2件, 实际想购买2件 + if (($alreadyBuyNum + $goods['total_num']) > $goods['limit_num']) { + $diffNum = ($alreadyBuyNum + $goods['total_num']) - $goods['limit_num']; + $this->error = "很抱歉,该商品限购{$goods['limit_num']}件,您最多能再购买{$diffNum}件"; + return false; + } + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/points/GoodsDeduct.php b/source/application/api/service/points/GoodsDeduct.php new file mode 100644 index 0000000..7577127 --- /dev/null +++ b/source/application/api/service/points/GoodsDeduct.php @@ -0,0 +1,82 @@ +goodsList = $goodsList; + } + + public function setGoodsPoints($maxPointsNumCount, $actualPointsNum) + { + // 计算实际积分抵扣数量 + $this->setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum); + // 总抵扣数量 + $totalPointsNum = helper::getArrayColumnSum($this->goodsList, 'points_num'); + // 填充余数 + $this->setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum); + $this->setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum); + // 计算实际积分抵扣金额 + $this->setGoodsListPointsMoney(); + return true; + } + + /** + * 计算实际积分抵扣数量 + * @param $maxPointsNumCount + * @param $actualPointsNum + */ + private function setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum) + { + foreach ($this->goodsList as &$goods) { + if (!$goods['is_points_discount']) continue; + $goods['points_num'] = floor($goods['max_points_num'] / $maxPointsNumCount * $actualPointsNum); + } + } + + /** + * 计算实际积分抵扣金额 + */ + private function setGoodsListPointsMoney() + { + $setting = SettingModel::getItem('points'); + foreach ($this->goodsList as &$goods) { + if (!$goods['is_points_discount']) continue; + $goods['points_money'] = helper::bcmul($goods['points_num'], $setting['discount']['discount_ratio']); + } + } + + private function setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum) + { + if ($totalPointsNum === 0) { + $temReducedMoney = $actualPointsNum; + foreach ($this->goodsList as &$goods) { + if (!$goods['is_points_discount']) continue; + if ($temReducedMoney === 0) break; + $goods['points_num'] = 1; + $temReducedMoney--; + } + } + return true; + } + + private function setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum) + { + $tempDiff = $actualPointsNum - $totalPointsNum; + foreach ($this->goodsList as &$goods) { + if (!$goods['is_points_discount']) continue; + if ($tempDiff < 1) break; + $goods['points_num'] = $goods['points_num'] + 1; + $tempDiff--; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/recharge/PaySuccess.php b/source/application/api/service/recharge/PaySuccess.php new file mode 100644 index 0000000..1efd758 --- /dev/null +++ b/source/application/api/service/recharge/PaySuccess.php @@ -0,0 +1,79 @@ +model = OrderModel::getPayDetail($orderNo); + $this->wxappId = $this->model['wxapp_id']; + // 获取用户信息 + $this->user = UserModel::detail($this->model['user_id']); + } + + /** + * 获取订单详情 + * @return OrderModel|null + */ + public function getOrderInfo() + { + return $this->model; + } + + /** + * 订单支付成功业务处理 + * @param int $payType 支付类型 + * @param array $payData 支付回调数据 + * @return bool + */ + public function onPaySuccess($payType, $payData) + { + return $this->model->transaction(function () use ($payType, $payData) { + // 更新订单状态 + $this->model->save([ + 'pay_status' => PayStatusEnum::SUCCESS, + 'pay_time' => time(), + 'transaction_id' => $payData['transaction_id'] + ]); + // 累积用户余额 + $this->user->setInc('balance', $this->model['actual_money']); + // 用户余额变动明细 + BalanceLogModel::add(SceneEnum::RECHARGE, [ + 'user_id' => $this->user['user_id'], + 'money' => $this->model['actual_money'], + 'wxapp_id' => $this->wxappId, + ], ['order_no' => $this->model['order_no']]); + // 更新prepay_id记录 + if ($payType == PayTypeEnum::WECHAT) { + WxappPrepayIdModel::updatePayStatus($this->model['order_id'], OrderTypeEnum::RECHARGE); + } + return true; + }); + } + +} \ No newline at end of file diff --git a/source/application/api/service/sharing/order/Checkout.php b/source/application/api/service/sharing/order/Checkout.php new file mode 100644 index 0000000..c5234a0 --- /dev/null +++ b/source/application/api/service/sharing/order/Checkout.php @@ -0,0 +1,887 @@ + 0, // 参与的拼单id + 'delivery' => null, // 配送方式 + 'shop_id' => 0, // 自提门店id + 'linkman' => '', // 自提联系人 + 'phone' => '', // 自提联系电话 + 'coupon_id' => 0, // 优惠券id + 'is_use_points' => 0, // 是否使用积分抵扣 + 'remark' => '', // 买家留言 + 'pay_type' => PayTypeEnum::WECHAT, // 支付方式 + 'order_type' => 20, // 下单类型 10 => 单独购买,20 => 拼团 + ]; + + /** + * 订单结算的规则 + * @var array + */ + private $checkoutRule = [ + 'is_user_grade' => true, // 会员等级折扣 + 'is_coupon' => true, // 优惠券抵扣 + 'is_use_points' => true, // 是否使用积分抵扣 + 'is_dealer' => true, // 是否开启分销 + ]; + + /** + * 订单来源 + * @var array + */ + private $orderSource = [ + 'source' => OrderSourceEnum::MASTER, + 'source_id' => 0, + ]; + + /** + * 订单结算数据 + * @var array + */ + private $orderData = []; + + /** + * 构造函数 + * Checkout constructor. + */ + public function __construct() + { + $this->model = new OrderModel; + $this->wxapp_id = OrderModel::$wxapp_id; + } + + /** + * 设置结算台请求的参数 + * @param $param + * @return array + */ + public function setParam($param) + { + $this->param = array_merge($this->param, $param); + return $this->getParam(); + } + + /** + * 获取结算台请求的参数 + * @return array + */ + public function getParam() + { + return $this->param; + } + + /** + * 订单结算的规则 + * @param $data + */ + public function setCheckoutRule($data) + { + $this->checkoutRule = array_merge($this->checkoutRule, $data); + } + + /** + * 设置订单来源(普通订单、砍价订单) + * @param $data + */ + public function setOrderSource($data) + { + $this->orderSource = array_merge($this->orderSource, $data); + } + + /** + * 订单确认-砍价活动 + * @param $user + * @param $goodsList + * @return array + * @throws BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function onCheckout($user, $goodsList) + { + $this->user = $user; + $this->goodsList = $goodsList; + // 订单确认-立即购买 + return $this->checkout(); + } + + /** + * 订单结算台 + * @return array + * @throws BaseException + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function checkout() + { + // 整理订单数据 + $this->orderData = $this->getOrderData(); + // 验证商品状态, 是否允许购买 + $this->validateGoodsList(); + // 订单商品总数量 + $orderTotalNum = helper::getArrayColumnSum($this->goodsList, 'total_num'); + // 设置订单商品会员折扣价 + $this->setOrderGoodsGradeMoney(); + // 设置订单商品总金额(不含优惠折扣) + $this->setOrderTotalPrice(); + // 计算可用积分抵扣 + $this->setOrderPoints(); + // 当前用户可用的优惠券列表 + $couponList = $this->getUserCouponList($this->orderData['order_total_price']); + // 计算优惠券抵扣 + $this->setOrderCouponMoney($couponList, $this->param['coupon_id']); + // 计算订单商品的实际付款金额 + $this->setOrderGoodsPayPrice(); + // 设置默认配送方式 + !$this->param['delivery'] && $this->param['delivery'] = current(SettingModel::getItem('store')['delivery_type']); + // 处理配送方式 + if ($this->param['delivery'] == DeliveryTypeEnum::EXPRESS) { + $this->setOrderExpress(); + } elseif ($this->param['delivery'] == DeliveryTypeEnum::EXTRACT) { + $this->param['shop_id'] > 0 && $this->orderData['extract_shop'] = ShopModel::detail($this->param['shop_id']); + } + // 计算订单最终金额 + $this->setOrderPayPrice(); + // 计算订单积分赠送数量 + $this->setOrderPointsBonus(); + // 返回订单数据 + return array_merge([ + 'goods_list' => array_values($this->goodsList), // 商品信息 + 'order_total_num' => $orderTotalNum, // 商品总数量 + 'coupon_list' => array_values($couponList), // 优惠券列表 + 'has_error' => $this->hasError(), + 'error_msg' => $this->getError(), + ], $this->orderData); + } + + /** + * 计算订单可用积分抵扣 + * @return bool + */ + private function setOrderPoints() + { + // 设置默认的商品积分抵扣信息 + $this->setDefaultGoodsPoints(); + // 积分设置 + $setting = SettingModel::getItem('points'); + // 条件:后台开启下单使用积分抵扣 + if (!$setting['is_shopping_discount'] || !$this->checkoutRule['is_use_points']) { + return false; + } + // 条件:订单金额满足[?]元 + if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['order_total_price']) === 1) { + return false; + } + // 计算订单商品最多可抵扣的积分数量 + $this->setOrderGoodsMaxPointsNum(); + // 订单最多可抵扣的积分总数量 + $maxPointsNumCount = helper::getArrayColumnSum($this->goodsList, 'max_points_num'); + // 实际可抵扣的积分数量 + $actualPointsNum = min($maxPointsNumCount, $this->user['points']); + if ($actualPointsNum < 1) { + return false; + } + // 计算订单商品实际抵扣的积分数量和金额 + $GoodsDeduct = new PointsDeductService($this->goodsList); + $GoodsDeduct->setGoodsPoints($maxPointsNumCount, $actualPointsNum); + // 积分抵扣总金额 + $orderPointsMoney = helper::getArrayColumnSum($this->goodsList, 'points_money'); + $this->orderData['points_money'] = helper::number2($orderPointsMoney); + // 积分抵扣总数量 + $this->orderData['points_num'] = $actualPointsNum; + // 允许积分抵扣 + $this->orderData['is_allow_points'] = true; + return true; + } + + /** + * 计算订单商品最多可抵扣的积分数量 + * @return bool + */ + private function setOrderGoodsMaxPointsNum() + { + // 积分设置 + $setting = SettingModel::getItem('points'); + foreach ($this->goodsList as &$goods) { + // 商品不允许积分抵扣 + if (!$goods['is_points_discount']) continue; + // 积分抵扣比例 + $deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100); + // 最多可抵扣的金额 + $maxPointsMoney = helper::bcmul($goods['total_price'], $deductionRatio); + // 最多可抵扣的积分数量 + $goods['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0); + } + return true; + } + + /** + * 设置默认的商品积分抵扣信息 + * @return bool + */ + private function setDefaultGoodsPoints() + { + foreach ($this->goodsList as &$goods) { + // 最多可抵扣的积分数量 + $goods['max_points_num'] = 0; + // 实际抵扣的积分数量 + $goods['points_num'] = 0; + // 实际抵扣的金额 + $goods['points_money'] = 0.00; + } + return true; + } + + /** + * 整理订单数据(结算台初始化) + * @return array + */ + private function getOrderData() + { + // 系统支持的配送方式 (后台设置) + $deliveryType = SettingModel::getItem('store')['delivery_type']; + // 积分设置 + $pointsSetting = SettingModel::getItem('points'); + return [ + // 订单类型 + 'order_type' => $this->param['order_type'], + // 配送类型 + 'delivery' => $this->param['delivery'] > 0 ? $this->param['delivery'] : $deliveryType[0], + // 默认地址 + 'address' => $this->user['address_default'], + // 是否存在收货地址 + 'exist_address' => $this->user['address_id'] > 0, + // 配送费用 + 'express_price' => 0.00, + // 当前用户收货城市是否存在配送规则中 + 'intra_region' => true, + // 自提门店信息 + 'extract_shop' => [], + // 是否允许使用积分抵扣 + 'is_allow_points' => false, + // 是否使用积分抵扣 + 'is_use_points' => $this->param['is_use_points'], + // 积分抵扣金额 + 'points_money' => 0.00, + // 赠送的积分数量 + 'points_bonus' => 0, + // 支付方式 + 'pay_type' => $this->param['pay_type'], + // 系统设置 + 'setting' => [ + 'delivery' => $deliveryType, // 支持的配送方式 + 'points_name' => $pointsSetting['points_name'], // 积分名称 + 'points_describe' => $pointsSetting['describe'], // 积分说明 + ], + // 记忆的自提联系方式 + 'last_extract' => UserService::getLastExtract($this->user['user_id']), + // todo: 兼容处理 + 'deliverySetting' => $deliveryType, + ]; + } + + /** + * 当前用户可用的优惠券列表 + * @param $orderTotalPrice + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getUserCouponList($orderTotalPrice) + { + // 是否开启优惠券折扣 + if (!$this->checkoutRule['is_coupon'] || !SharingSettingModel::getItem('basic')['is_coupon']) { + return []; + } + return UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice); + } + + /** + * 验证订单商品的状态 + */ + private function validateGoodsList() + { + foreach ($this->goodsList as $goods) { + // 判断商品是否下架 + if ($goods['goods_status']['value'] != 10) { + $this->setError("很抱歉,商品 [{$goods['goods_name']}] 已下架"); + } + // 判断商品库存 + if ($goods['total_num'] > $goods['goods_sku']['stock_num']) { + $this->setError("很抱歉,商品 [{$goods['goods_name']}] 库存不足"); + } + } + } + + /** + * 设置订单的商品总金额(不含优惠折扣) + */ + private function setOrderTotalPrice() + { + // 订单商品的总金额(不含优惠券折扣) + $this->orderData['order_total_price'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_price')); + } + + /** + * 设置订单的实际支付金额(含配送费) + */ + private function setOrderPayPrice() + { + // 订单金额(含优惠折扣) + $this->orderData['order_price'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_pay_price')); + // 订单实付款金额(订单金额 + 运费) + $this->orderData['order_pay_price'] = helper::number2(helper::bcadd($this->orderData['order_price'], $this->orderData['express_price'])); + } + + /** + * 计算订单积分赠送数量 + * @return bool + */ + private function setOrderPointsBonus() + { + // 初始化商品积分赠送数量 + foreach ($this->goodsList as &$goods) { + $goods['points_bonus'] = 0; + } + // 积分设置 + $setting = SettingModel::getItem('points'); + // 条件:后台开启开启购物送积分 + if (!$setting['is_shopping_gift']) { + return false; + } + // 设置商品积分赠送数量 + foreach ($this->goodsList as &$goods) { + // 积分赠送比例 + $ratio = $setting['gift_ratio'] / 100; + // 计算抵扣积分数量 + $goods['points_bonus'] = $goods['is_points_gift'] ? helper::bcmul($goods['total_pay_price'], $ratio, 0) : 0; + } + // 订单积分赠送数量 + $this->orderData['points_bonus'] = helper::getArrayColumnSum($this->goodsList, 'points_bonus'); + return true; + } + + /** + * 计算订单商品的实际付款金额 + * @return bool + */ + private function setOrderGoodsPayPrice() + { + // 商品总价 - 优惠抵扣 + foreach ($this->goodsList as &$goods) { + // 减去优惠券抵扣金额 + $value = helper::bcsub($goods['total_price'], $goods['coupon_money']); + // 减去积分抵扣金额 + if ($this->orderData['is_allow_points'] && $this->orderData['is_use_points']) { + $value = helper::bcsub($value, $goods['points_money']); + } + $goods['total_pay_price'] = helper::number2($value); + } + return true; + } + + /** + * 设置订单商品会员折扣价 + * @return bool + */ + private function setOrderGoodsGradeMoney() + { + // 设置默认数据 + helper::setDataAttribute($this->goodsList, [ + // 标记参与会员折扣 + 'is_user_grade' => false, + // 会员等级抵扣的金额 + 'grade_ratio' => 0, + // 会员折扣的商品单价 + 'grade_goods_price' => 0.00, + // 会员折扣的总额差 + 'grade_total_money' => 0.00, + ], true); + + // 是否开启会员等级折扣 + if (!$this->checkoutRule['is_user_grade']) { + return false; + } + // 会员等级状态 + if (!( + $this->user['grade_id'] > 0 && !empty($this->user['grade']) + && !$this->user['grade']['is_delete'] && $this->user['grade']['status'] + )) { + return false; + } + // 计算抵扣金额 + foreach ($this->goodsList as &$goods) { + // 判断商品是否参与会员折扣 + if (!$goods['is_enable_grade']) { + continue; + } + // 商品单独设置了会员折扣 + if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$this->user['grade_id']])) { + // 折扣比例 + $discountRatio = helper::bcdiv($goods['alone_grade_equity'][$this->user['grade_id']], 10); + } else { + // 折扣比例 + $discountRatio = helper::bcdiv($this->user['grade']['equity']['discount'], 10); + } + if ($discountRatio > 0) { + // 会员折扣后的商品总金额 + $gradeTotalPrice = max(0.01, helper::bcmul($goods['total_price'], $discountRatio)); + helper::setDataAttribute($goods, [ + 'is_user_grade' => true, + 'grade_ratio' => $discountRatio, + 'grade_goods_price' => helper::number2(helper::bcmul($goods['goods_price'], $discountRatio), true), + 'grade_total_money' => helper::number2(helper::bcsub($goods['total_price'], $gradeTotalPrice)), + 'total_price' => $gradeTotalPrice, + ], false); + } + } + return true; + } + + /** + * 设置订单优惠券抵扣信息 + * @param array $couponList 当前用户可用的优惠券列表 + * @param int $couponId 当前选择的优惠券id + * @return bool + * @throws BaseException + */ + private function setOrderCouponMoney($couponList, $couponId) + { + // 设置默认数据:订单信息 + helper::setDataAttribute($this->orderData, [ + 'coupon_id' => 0, // 用户优惠券id + 'coupon_money' => 0, // 优惠券抵扣金额 + ], false); + // 设置默认数据:订单商品列表 + helper::setDataAttribute($this->goodsList, [ + 'coupon_money' => 0, // 优惠券抵扣金额 + ], true); + // 是否开启优惠券折扣 + if (!$this->checkoutRule['is_coupon']) { + return false; + } + // 如果没有可用的优惠券,直接返回 + if ($couponId <= 0 || empty($couponList)) { + return true; + } + // 获取优惠券信息 + $couponInfo = helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId); + if ($couponInfo == false) { + throw new BaseException(['msg' => '未找到优惠券信息']); + } + // 计算订单商品优惠券抵扣金额 + $goodsListTemp = helper::getArrayColumns($this->goodsList, ['total_price']); + $CouponMoney = new GoodsDeductService; + $completed = $CouponMoney->setGoodsCouponMoney($goodsListTemp, $couponInfo['reduced_price']); + // 分配订单商品优惠券抵扣金额 + foreach ($this->goodsList as $key => &$goods) { + $goods['coupon_money'] = $completed[$key]['coupon_money'] / 100; + } + // 记录订单优惠券信息 + $this->orderData['coupon_id'] = $couponId; + $this->orderData['coupon_money'] = helper::number2($CouponMoney->getActualReducedMoney() / 100); + return true; + } + + /** + * 订单配送-快递配送 + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function setOrderExpress() + { + // 设置默认数据:配送费用 + helper::setDataAttribute($this->goodsList, [ + 'express_price' => 0, + ], true); + // 当前用户收货城市id + $cityId = $this->user['address_default'] ? $this->user['address_default']['city_id'] : null; + // 初始化配送服务类 + $ExpressService = new ExpressService($cityId, $this->goodsList, OrderTypeEnum::SHARING); + // 验证商品是否在配送范围 + $isIntraRegion = $ExpressService->isIntraRegion(); + if ($cityId > 0 && $isIntraRegion == false) { + $notInRuleGoodsName = $ExpressService->getNotInRuleGoodsName(); + $this->setError("很抱歉,您的收货地址不在商品 [{$notInRuleGoodsName}] 的配送范围内"); + } + // 订单总运费金额 + $this->orderData['intra_region'] = $isIntraRegion; + $this->orderData['express_price'] = $ExpressService->getDeliveryFee(); + return true; + } + + /** + * 创建新订单 + * @param array $order 订单信息 + * @return bool + * @throws \Exception + */ + public function createOrder($order) + { + // 如果是参与拼单,则记录拼单id + $order['active_id'] = $this->param['active_id']; + // 表单验证 + if (!$this->validateOrderForm($order, $this->param['linkman'], $this->param['phone'])) { + return false; + } + // 创建新的订单 + $status = $this->model->transaction(function () use ($order) { + // 创建订单事件 + return $this->createOrderEvent($order); + }); + // 余额支付标记订单已支付 + if ($status && $order['pay_type'] == PayTypeEnum::BALANCE) { + return $this->model->onPaymentByBalance($this->model['order_no']); + } + return $status; + } + + /** + * 创建订单事件 + * @param $order + * @return bool + * @throws BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function createOrderEvent($order) + { + // 新增订单记录 + $status = $this->add($order, $this->param['remark']); + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + // 记录收货地址 + $this->saveOrderAddress($order['address']); + } elseif ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + // 记录自提信息 + $this->saveOrderExtract($this->param['linkman'], $this->param['phone']); + } + // 保存订单商品信息 + $this->saveOrderGoods($order); + // 更新商品库存 (针对下单减库存的商品) + $this->updateGoodsStockNum($order['goods_list']); + // 设置优惠券使用状态 + UserCouponModel::setIsUse($this->param['coupon_id']); + // 积分抵扣情况下扣除用户积分 + if ($order['is_allow_points'] && $order['is_use_points'] && $order['points_num'] > 0) { + $describe = "用户消费:{$this->model['order_no']}"; + $this->user->setIncPoints(-$order['points_num'], $describe); + } + // 获取订单详情 + $detail = OrderModel::getUserOrderDetail($this->model['order_id'], $this->user['user_id']); + // 记录分销商订单 + if ($this->checkoutRule['is_dealer'] && SharingSettingModel::getItem('basic')['is_dealer']) { + DealerOrderModel::createOrder($detail, OrderTypeEnum::SHARING); + } + return $status; + } + + /** + * 构建支付请求的参数 + * @return array + * @throws BaseException + * @throws \think\exception\DbException + */ + public function onOrderPayment() + { + return PaymentService::orderPayment($this->user, $this->model, $this->param['pay_type']); + } + + /** + * 表单验证 (订单提交) + * @param array $order 订单信息 + * @param string $linkman 联系人 + * @param string $phone 联系电话 + * @return bool + * @throws \think\exception\DbException + */ + private function validateOrderForm(&$order, $linkman, $phone) + { + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + if (empty($order['address'])) { + $this->error = '您还没有选择配送地址'; + return false; + } + } + if ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + if (empty($order['extract_shop'])) { + $this->error = '您还没有选择自提门店'; + return false; + } + if (empty($linkman) || empty($phone)) { + $this->error = '您还没有填写联系人和电话'; + return false; + } + } + // 余额支付时判断用户余额是否足够 + if ($order['pay_type'] == PayTypeEnum::BALANCE) { + if ($this->user['balance'] < $order['order_pay_price']) { + $this->error = '您的余额不足,无法使用余额支付'; + return false; + } + } + // 验证拼单id是否合法 + if ($order['active_id'] > 0) { + // 拼单详情 + $detail = ActiveModel::detail($order['active_id']); + if (empty($detail)) { + $this->error = '很抱歉,拼单不存在'; + return false; + } + // 验证当前拼单是否允许加入新成员 + if (!$detail->checkAllowJoin()) { + $this->error = $detail->getError(); + return false; + } + } + return true; + } + + /** + * 当前订单是否存在和使用积分抵扣 + * @param $order + * @return bool + */ + private function isExistPointsDeduction($order) + { + return $order['is_allow_points'] && $order['is_use_points']; + } + + /** + * 新增订单记录 + * @param $order + * @param string $remark + * @return false|int + */ + private function add(&$order, $remark = '') + { + // 当前订单是否存在和使用积分抵扣 + $isExistPointsDeduction = $this->isExistPointsDeduction($order); + // 订单数据 + $data = [ + 'user_id' => $this->user['user_id'], + 'order_type' => $order['order_type'], + 'active_id' => $order['active_id'], + 'order_no' => $this->model->orderNo(), + 'total_price' => $order['order_total_price'], + 'order_price' => $order['order_price'], + 'coupon_id' => $order['coupon_id'], + 'coupon_money' => $order['coupon_money'], + 'points_money' => $isExistPointsDeduction ? $order['points_money'] : 0.00, + 'points_num' => $isExistPointsDeduction ? $order['points_num'] : 0, + 'pay_price' => $order['order_pay_price'], + 'delivery_type' => $order['delivery'], + 'pay_type' => $order['pay_type'], + 'buyer_remark' => trim($remark), +// 'order_source' => $this->orderSource['source'], +// 'order_source_id' => $this->orderSource['source_id'], + 'points_bonus' => $order['points_bonus'], + 'wxapp_id' => $this->wxapp_id, + ]; + if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) { + $data['express_price'] = $order['express_price']; + } elseif ($order['delivery'] == DeliveryTypeEnum::EXTRACT) { + $data['extract_shop_id'] = $order['extract_shop']['shop_id']; + } + // 保存订单记录 + return $this->model->save($data); + } + + /** + * 保存订单商品信息 + * @param $order + * @return int + */ + private function saveOrderGoods(&$order) + { + // 当前订单是否存在和使用积分抵扣 + $isExistPointsDeduction = $this->isExistPointsDeduction($order); + // 订单商品列表 + $goodsList = []; + foreach ($order['goods_list'] as $goods) { + /* @var GoodsModel $goods */ + $goodsList[] = [ + 'user_id' => $this->user['user_id'], + 'wxapp_id' => $this->wxapp_id, + 'goods_id' => $goods['goods_id'], + 'goods_name' => $goods['goods_name'], + 'image_id' => $goods['image'][0]['image_id'], + 'people' => $goods['people'], + 'group_time' => $goods['group_time'], + 'is_alone' => $goods['is_alone'], + 'deduct_stock_type' => $goods['deduct_stock_type'], + 'spec_type' => $goods['spec_type'], + 'spec_sku_id' => $goods['goods_sku']['spec_sku_id'], + 'goods_sku_id' => $goods['goods_sku']['goods_sku_id'], + 'goods_attr' => $goods['goods_sku']['goods_attr'], + 'content' => $goods['content'], + 'goods_no' => $goods['goods_sku']['goods_no'], + 'goods_price' => $goods['goods_sku']['goods_price'], + 'line_price' => $goods['goods_sku']['line_price'], + 'goods_weight' => $goods['goods_sku']['goods_weight'], + 'is_user_grade' => (int)$goods['is_user_grade'], + 'grade_ratio' => $goods['grade_ratio'], + 'grade_goods_price' => $goods['grade_goods_price'], + 'grade_total_money' => $goods['grade_total_money'], + 'coupon_money' => $goods['coupon_money'], + 'points_money' => $isExistPointsDeduction ? $goods['points_money'] : 0.00, + 'points_num' => $isExistPointsDeduction ? $goods['points_num'] : 0, + 'points_bonus' => $goods['points_bonus'], + 'total_num' => $goods['total_num'], + 'total_price' => $goods['total_price'], + 'total_pay_price' => $goods['total_pay_price'], + 'is_ind_dealer' => $goods['is_ind_dealer'], + 'dealer_money_type' => $goods['dealer_money_type'], + 'first_money' => $goods['first_money'], + 'second_money' => $goods['second_money'], + 'third_money' => $goods['third_money'], + ]; + } + return $this->model->goods()->saveAll($goodsList); + } + + /** + * 更新商品库存 (针对下单减库存的商品) + * @param $goods_list + * @throws \Exception + */ + private function updateGoodsStockNum($goods_list) + { + $deductStockData = []; + foreach ($goods_list as $goods) { + // 下单减库存 + $goods['deduct_stock_type'] == 10 && $deductStockData[] = [ + 'goods_sku_id' => $goods['goods_sku']['goods_sku_id'], + 'stock_num' => ['dec', $goods['total_num']] + ]; + } + !empty($deductStockData) && (new GoodsSkuModel)->isUpdate()->saveAll($deductStockData); + } + + /** + * 记录收货地址 + * @param $address + * @return false|\think\Model + */ + private function saveOrderAddress($address) + { + if ($address['region_id'] == 0 && !empty($address['district'])) { + $address['detail'] = $address['district'] . ' ' . $address['detail']; + } + return $this->model->address()->save([ + 'user_id' => $this->user['user_id'], + 'wxapp_id' => $this->wxapp_id, + 'name' => $address['name'], + 'phone' => $address['phone'], + 'province_id' => $address['province_id'], + 'city_id' => $address['city_id'], + 'region_id' => $address['region_id'], + 'detail' => $address['detail'], + ]); + } + + /** + * 保存上门自提联系人 + * @param $linkman + * @param $phone + * @return false|\think\Model + */ + private function saveOrderExtract($linkman, $phone) + { + // 记忆上门自提联系人(缓存),用于下次自动填写 + UserService::setLastExtract($this->model['user_id'], trim($linkman), trim($phone)); + // 保存上门自提联系人(数据库) + return $this->model->extract()->save([ + 'linkman' => trim($linkman), + 'phone' => trim($phone), + 'user_id' => $this->model['user_id'], + 'wxapp_id' => $this->wxapp_id, + ]); + } + + /** + * 设置错误信息 + * @param $error + */ + protected function setError($error) + { + empty($this->error) && $this->error = $error; + } + + /** + * 获取错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + + /** + * 是否存在错误 + * @return bool + */ + public function hasError() + { + return !empty($this->error); + } + +} \ No newline at end of file diff --git a/source/application/api/service/sharing/order/PaySuccess.php b/source/application/api/service/sharing/order/PaySuccess.php new file mode 100644 index 0000000..6c8017d --- /dev/null +++ b/source/application/api/service/sharing/order/PaySuccess.php @@ -0,0 +1,152 @@ +model = OrderModel::getPayDetail($orderNo); + if (!empty($this->model)) { + $this->wxappId = $this->model['wxapp_id']; + } + // 获取用户信息 + $this->user = UserModel::detail($this->model['user_id']); + } + + /** + * 获取订单详情 + * @return OrderModel|null + */ + public function getOrderInfo() + { + return $this->model; + } + + /** + * 订单支付成功业务处理 + * @param $payType + * @param array $payData + * @return bool + */ + public function onPaySuccess($payType, $payData = []) + { + if (empty($this->model)) { + $this->error = '未找到该订单信息'; + return false; + } + // 更新付款状态 + $status = $this->updatePayStatus($payType, $payData); + // 订单支付成功行为 + if ($status == true) { + Hook::listen('order_pay_success', $this->model, OrderTypeEnum::SHARING); + } + return $status; + } + + /** + * 更新付款状态 + * @param $payType + * @param $payData + * @return bool + */ + private function updatePayStatus($payType, $payData) + { + // 验证余额支付时用户余额是否满足 + if ($payType == PayTypeEnum::BALANCE) { + if ($this->user['balance'] < $this->model['pay_price']) { + $this->error = '用户余额不足,无法使用余额支付'; + return false; + } + } + $this->model->transaction(function () use ($payType, $payData) { + // 更新商品库存、销量 + (new GoodsModel)->updateStockSales($this->model['goods']); + // 更新拼单记录 + $this->saveSharingActive($this->model['goods'][0]); + // 整理订单信息 + $order = ['pay_type' => $payType, 'pay_status' => 20, 'pay_time' => time()]; + if ($payType == PayTypeEnum::WECHAT) { + $order['transaction_id'] = $payData['transaction_id']; + } + // 更新订单状态 + $this->model->save($order); + // 累积用户总消费金额 + $this->user->setIncPayMoney($this->model['pay_price']); + // 余额支付 + if ($payType == PayTypeEnum::BALANCE) { + // 更新用户余额 + $this->user->setDec('balance', $this->model['pay_price']); + BalanceLogModel::add(SceneEnum::CONSUME, [ + 'user_id' => $this->user['user_id'], + 'money' => -$this->model['pay_price'], + ], ['order_no' => $this->model['order_no']]); + } + // 微信支付 + if ($payType == PayTypeEnum::WECHAT) { + // 更新prepay_id记录 + WxappPrepayIdModel::updatePayStatus($this->model['order_id'], OrderTypeEnum::SHARING); + } + }); + return true; + } + + /** + * 更新拼单记录 + * @param $goods + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function saveSharingActive($goods) + { + // 新增/更新拼单记录 + if ($this->model['order_type']['value'] != 20) { + return false; + } + // 拼单模型 + $ActiveModel = new ActiveModel; + // 参与他人的拼单, 更新拼单记录 + if ($this->model['active_id'] > 0) { + $ActiveModel = $ActiveModel::detail($this->model['active_id']); + return $ActiveModel->onUpdate($this->model['user_id'], $this->model['order_id']); + } + // 自己发起的拼单, 新增拼单记录 + $ActiveModel->onCreate($this->model['user_id'], $this->model['order_id'], $goods); + // 记录拼单id + $this->model['active_id'] = $ActiveModel['active_id']; + return true; + } + +} \ No newline at end of file diff --git a/source/application/api/service/sharp/Active.php b/source/application/api/service/sharp/Active.php new file mode 100644 index 0000000..2210bbe --- /dev/null +++ b/source/application/api/service/sharp/Active.php @@ -0,0 +1,357 @@ +ActiveModel = new ActiveModel; + $this->ActiveTimeModel = new ActiveTimeModel; + } + + /** + * 获取秒杀活动会场首页数据 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getHallIndex() + { + // 获取秒杀首页顶部菜单 + $tabbar = $this->getActiveTabbar(); + if (empty($tabbar)) return ['tabbar' => [], 'goodsList' => []]; + // 获取活动商品 + $goodsList = $this->getGoodsListByActiveTimeId($tabbar[0]['active_time_id']); + return compact('tabbar', 'goodsList'); + } + + /** + * 获取秒杀活动组件数据 + * @param array $goodsParm + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getSharpModular($goodsParm = []) + { + // 获取秒杀活动列表 + $tabbar = $this->getActiveTabbar(); + if (empty($tabbar)) return ['active' => null, 'goodsList' => []]; + return [ + // 秒杀活动 + 'active' => $tabbar[0], + // 活动商品列表 + 'goodsList' => $this->getGoodsListByActiveTimeId($tabbar[0]['active_time_id'], $goodsParm), + ]; + } + + /** + * 根据活动场次ID获取商品列表 + * @param int $activeTimeId + * @param array $goodsParm + * @return false|\PDOStatement|string|\think\Collection + */ + public function getGoodsListByActiveTimeId($activeTimeId, $goodsParm = []) + { + return ActiveGoodsModel::getGoodsListByActiveTimeId($activeTimeId, $goodsParm); + } + + /** + * 获取活动商品详情 + * @param $activeTimeId + * @param $sharpGoodsId + * @return array|bool + * @throws \think\exception\DbException + */ + public function getyActiveGoodsDetail($activeTimeId, $sharpGoodsId) + { + // 活动详情 + $active = $this->getGoodsActive($activeTimeId, $sharpGoodsId); + if (empty($active)) return false; + // 商品详情 + $model = new ActiveGoodsModel; + $goods = $model->getGoodsActiveDetail($active, $sharpGoodsId, true); + if (empty($goods)) { + $this->error = $model->getError(); + return false; + } + // 商品多规格信息 + $goods['goods_multi_spec'] = (new SharpGoodsModel)->getSpecData($goods, $goods['sku']); + return compact('active', 'goods'); + } + + /** + * 获取订单提交的商品列表 + * @param $activeTimeId + * @param $sharpGoodsId + * @param $goodsSkuId + * @param $goodsNum + * @return array|bool + * @throws \think\exception\DbException + */ + public function getCheckoutGoodsList($activeTimeId, $sharpGoodsId, $goodsSkuId, $goodsNum) + { + // 活动详情 + $active = $this->getGoodsActive($activeTimeId, $sharpGoodsId); + if (empty($active)) return false; + // 商品详情 + $model = new ActiveGoodsModel; + $goods = $model->getGoodsActiveDetail($active, $sharpGoodsId, false); + if (empty($goods)) return false; + // 商品sku信息 + $goods['goods_sku'] = GoodsModel::getGoodsSku($goods, $goodsSkuId); + // 商品列表 + $goodsList = [$goods->hidden(['category', 'content', 'image', 'sku'])]; + foreach ($goodsList as &$item) { + // 商品价格 + $item['goods_price'] = $item['goods_sku']['seckill_price']; + $item['line_price'] = $item['goods_sku']['original_price']; + // 商品id + $item['spec_sku_id'] = $item['goods_sku']['spec_sku_id']; + $item['goods_source_id'] = $item['sharp_goods_id']; + // 商品购买数量 + $item['total_num'] = $goodsNum; + // 商品购买总金额 + $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum); + } + return $goodsList; + } + + /** + * 活动详情 + * @param $activeTimeId + * @param $sharpGoodsId + * @return array|bool + */ + private function getGoodsActive($activeTimeId, $sharpGoodsId) + { + // 获取活动商品的关联信息 + $model = $this->getActiveGoods($activeTimeId, $sharpGoodsId); + if (empty($model) || !($model['active']['status'] && $model['active_time']['status'])) { + $this->error = '很抱歉,该活动不存在或已结束'; + return false; + } + // 整理数据 + $startTime = $model['active']['active_date'] + ($model->active_time->getData('active_time') * 60 * 60); + $endTime = $startTime + (1 * 60 * 60); + $activeStatus = $this->getActivcGoodsStatus($startTime, $endTime); + $data = [ + 'active_id' => $model['active_id'], + 'active_time_id' => $model['active_time_id'], + 'active_time' => $model['active_time']['active_time'], + 'sales_actual' => $model['sales_actual'], + 'start_time' => $this->onFormatTime($startTime), + 'end_time' => $this->onFormatTime($endTime), + 'active_status' => $activeStatus, + 'count_down_time' => $this->getGoodsActiveCountDownTime($activeStatus, $startTime, $endTime), + 'wxapp_id' => $model['wxapp_id'], + ]; + return $data; + } + + /** + * 获取活动商品的关联信息 + * @param $activeTimeId + * @param $sharpGoodsId + * @return mixed + */ + public function getActiveGoods($activeTimeId, $sharpGoodsId) + { + static $data = []; + if (!isset($data["{$activeTimeId}_{$sharpGoodsId}"])) { + $model = ActiveGoodsModel::getGoodsActive($activeTimeId, $sharpGoodsId); + !empty($model) && $data["{$activeTimeId}_{$sharpGoodsId}"] = $model; + } + return $data["{$activeTimeId}_{$sharpGoodsId}"]; + } + + /** + * 活动商品倒计时 + * @param $activeStatus + * @param $startTime + * @param $endTime + * @return bool|false|string + */ + private function getGoodsActiveCountDownTime($activeStatus, $startTime, $endTime) + { + if ($activeStatus == GoodsStatusEnum::STATE_BEGIN) { + return $this->onFormatTime($startTime); + } + if ($activeStatus == GoodsStatusEnum::STATE_SOON) { + return $this->onFormatTime($endTime); + } + return false; + } + + /** + * 活动商品状态 + * @param $startTime + * @param $endTime + * @return int + */ + private function getActivcGoodsStatus($startTime, $endTime) + { + $nowTime = time(); + if ($nowTime < $startTime) { + return GoodsStatusEnum::STATE_SOON; + } + if ($nowTime >= $startTime && $nowTime < $endTime) { + return GoodsStatusEnum::STATE_BEGIN; + } + return GoodsStatusEnum::STATE_END; + } + + /** + * 获取秒杀首页顶部菜单 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getActiveTabbar() + { + // 当天的活动 + $todyActive = $this->ActiveModel->getNowActive(); + $data = []; + if (!empty($todyActive)) { + // 当前进行中的活动 + $data[] = $this->getBeginActive($todyActive); + // 获取即将开始的活动 + $data = array_merge($data, $this->getSoonActive($todyActive)); + } + // 获取预告的活动 + $data[] = $this->getNoticeActive(); + return array_values(array_filter($data)); + } + + /** + * 获取当前进行中的活动 + * @param $todyActive + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getBeginActive($todyActive) + { + // 当前的时间点 + $model = $this->ActiveTimeModel->getNowActiveTime($todyActive['active_id']); + if (empty($model)) return []; + // 整理数据 + $startTime = $todyActive['active_date'] + ($model->getData('active_time') * 60 * 60); + $endTime = $startTime + (1 * 60 * 60); + return [ + 'active_id' => $todyActive['active_id'], + 'active_time_id' => $model['active_time_id'], + 'active_time' => $model['active_time'], + 'start_time' => $this->onFormatTime($startTime), + 'end_time' => $this->onFormatTime($endTime), + 'count_down_time' => $this->onFormatTime($endTime), + 'status' => ActiveStatusEnum::ACTIVE_STATE_BEGIN, + 'status_text' => '已开抢', + 'status_text2' => '正在疯抢', + 'sharp_modular_text' => '正在疯抢', + ]; + } + + /** + * 获取即将开始的活动 + * @param $todyActive + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getSoonActive($todyActive) + { + // 当前的时间点 + $list = $this->ActiveTimeModel->getNextActiveTimes($todyActive['active_id']); + if (empty($list) || $list->isEmpty()) return []; + // 整理数据 + $data = []; + foreach ($list as $item) { + $startTime = $todyActive['active_date'] + ($item->getData('active_time') * 60 * 60); + $endTime = $startTime + (1 * 60 * 60); + $data[] = [ + 'active_id' => $todyActive['active_id'], + 'active_time_id' => $item['active_time_id'], + 'active_time' => $item['active_time'], + 'start_time' => $this->onFormatTime($startTime), + 'end_time' => $this->onFormatTime($endTime), + 'count_down_time' => $this->onFormatTime($startTime), + 'status' => ActiveStatusEnum::ACTIVE_STATE_SOON, + 'status_text' => '即将开抢', + 'status_text2' => '即将开抢', + 'sharp_modular_text' => "{$item['active_time']} 场预告", + ]; + } + return $data; + } + + /** + * 获取预告的活动 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getNoticeActive() + { + // 下一场活动 + $nextActive = $this->ActiveModel->getNextActive(); + if (empty($nextActive)) return []; + // 第一个时间点 + $model = $this->ActiveTimeModel->getRecentActiveTime($nextActive['active_id']); + if (empty($model)) return []; + // 整理数据 + $startTime = $nextActive['active_date'] + ($model->getData('active_time') * 60 * 60); + $endTime = $startTime + (1 * 60 * 60); + return [ + 'active_id' => $nextActive['active_id'], + 'active_time_id' => $model['active_time_id'], + 'active_time' => $model['active_time'], + 'start_time' => $this->onFormatTime($startTime), + 'end_time' => $this->onFormatTime($endTime), + 'count_down_time' => $this->onFormatTime($startTime), + 'status' => ActiveStatusEnum::ACTIVE_STATE_NOTICE, + 'status_text' => '预告', + 'status_text2' => $this->onFormatTime($startTime) . ' 开始', + 'sharp_modular_text' => $this->onFormatTime($startTime) . ' 开始', + ]; + } + + /** + * 将时间戳格式化为日期时间 + * @param $timeStamp + * @return false|string + */ + private function onFormatTime($timeStamp) + { + return date('Y-m-d H:i', $timeStamp); + } + +} \ No newline at end of file diff --git a/source/application/api/service/sharp/Order.php b/source/application/api/service/sharp/Order.php new file mode 100644 index 0000000..1f3def8 --- /dev/null +++ b/source/application/api/service/sharp/Order.php @@ -0,0 +1,38 @@ +setBaseQuery('order', [ + ['order_goods', 'order_id'], + ]) + ->where('order_goods.user_id', '=', $userId) + ->where('order_goods.goods_id', '=', $goodsId) + ->where('order.order_source', '=', OrderSourceEnum::SHARP) + ->where('order.order_status', '<>', OrderStatusEnum::CANCELLED) + ->where('order.is_delete', '=', 0) + ->sum('order_goods.total_num'); + return $totalNum; + } +} \ No newline at end of file diff --git a/source/application/api/service/sharp/order/PaySuccess.php b/source/application/api/service/sharp/order/PaySuccess.php new file mode 100644 index 0000000..3f22029 --- /dev/null +++ b/source/application/api/service/sharp/order/PaySuccess.php @@ -0,0 +1,47 @@ +updateActiveGoodsAales($activeTimeId, $order['goods']); + } + + /** + * 更新活动会场的商品实际销量 + * @param $activeTimeId + * @param $goodsList + * @return bool + */ + private function updateActiveGoodsAales($activeTimeId, $goodsList) + { + $data = []; + foreach ($goodsList as $goods) { + $data[] = [ + 'data' => ['sales_actual' => ['inc', $goods['total_num']]], + 'where' => [ + 'active_time_id' => $activeTimeId, + 'sharp_goods_id' => $goods['goods_source_id'], + ], + ]; + } + return !empty($data) && (new ActiveGoodsModel)->updateAll($data); + } +} \ No newline at end of file diff --git a/source/application/api/tags.php b/source/application/api/tags.php new file mode 100644 index 0000000..8c80ff4 --- /dev/null +++ b/source/application/api/tags.php @@ -0,0 +1,20 @@ + +// +---------------------------------------------------------------------- + +// 应用行为扩展定义文件 +return [ + + // 订单支付成功行为管理 + 'order_pay_success' => [ + 'app\\api\\behavior\\order\\PaySuccess' + ], + +]; diff --git a/source/application/api/validate/order/Checkout.php b/source/application/api/validate/order/Checkout.php new file mode 100644 index 0000000..a94e2ab --- /dev/null +++ b/source/application/api/validate/order/Checkout.php @@ -0,0 +1,50 @@ + [ + 'require', + 'number', + 'gt' => 0 + ], + + // 购买数量 + 'goods_num' => [ + 'require', + 'number', + 'gt' => 0 + ], + + // 商品sku_id + 'goods_sku_id' => [ + 'require', + ], + +// // 购物车id集 +// 'cart_ids' => [ +// 'require', +// ], + + ]; + + /** + * 验证场景 + * @var array + */ + protected $scene = [ + 'buyNow' => ['goods_id', 'goods_num', 'goods_sku_id'], +// 'cart' => ['cart_ids'], + ]; + +} diff --git a/source/application/api/validate/sharing/order/Checkout.php b/source/application/api/validate/sharing/order/Checkout.php new file mode 100644 index 0000000..58fe2fa --- /dev/null +++ b/source/application/api/validate/sharing/order/Checkout.php @@ -0,0 +1,44 @@ + [ + 'require', + 'number', + 'gt' => 0 + ], + + // 购买数量 + 'goods_num' => [ + 'require', + 'number', + 'gt' => 0 + ], + + // 商品sku_id + 'goods_sku_id' => [ + 'require', + ], + + ]; + + /** + * 验证场景 + * @var array + */ + protected $scene = [ + 'buyNow' => ['goods_id', 'goods_num', 'goods_sku_id'], + ]; + +} diff --git a/source/application/common.php b/source/application/common.php new file mode 100644 index 0000000..26a8e61 --- /dev/null +++ b/source/application/common.php @@ -0,0 +1,348 @@ +' . print_r($content, true); + $is_die && die(); +} + +/** + * 驼峰命名转下划线命名 + * @param $str + * @return string + */ +function toUnderScore($str) +{ + $dstr = preg_replace_callback('/([A-Z]+)/', function ($matchs) { + return '_' . strtolower($matchs[0]); + }, $str); + return trim(preg_replace('/_{2,}/', '_', $dstr), '_'); +} + +/** + * 生成密码hash值 + * @param $password + * @return string + */ +function yoshop_hash($password) +{ + return md5(md5($password) . 'yoshop_salt_SmTRx'); +} + +/** + * 获取当前域名及根路径 + * @return string + */ +function base_url() +{ + static $baseUrl = ''; + if (empty($baseUrl)) { + $request = Request::instance(); + $subDir = str_replace('\\', '/', dirname($request->server('PHP_SELF'))); + $baseUrl = $request->scheme() . '://' . $request->host() . $subDir . ($subDir === '/' ? '' : '/'); + } + return $baseUrl; +} + +/** + * 写入日志 (废弃) + * @param string|array $values + * @param string $dir + * @return bool|int + */ +//function write_log($values, $dir) +//{ +// if (is_array($values)) +// $values = print_r($values, true); +// // 日志内容 +// $content = '[' . date('Y-m-d H:i:s') . ']' . PHP_EOL . $values . PHP_EOL . PHP_EOL; +// try { +// // 文件路径 +// $filePath = $dir . '/logs/'; +// // 路径不存在则创建 +// !is_dir($filePath) && mkdir($filePath, 0755, true); +// // 写入文件 +// return file_put_contents($filePath . date('Ymd') . '.log', $content, FILE_APPEND); +// } catch (\Exception $e) { +// return false; +// } +//} + +/** + * 写入日志 (使用tp自带驱动记录到runtime目录中) + * @param $value + * @param string $type + */ +function log_write($value, $type = 'yoshop-info') +{ + $msg = is_string($value) ? $value : var_export($value, true); + Log::record($msg, $type); +} + +/** + * curl请求指定url (get) + * @param $url + * @param array $data + * @return mixed + */ +function curl($url, $data = []) +{ + // 处理get数据 + if (!empty($data)) { + $url = $url . '?' . http_build_query($data); + } + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//这个是重点。 + $result = curl_exec($curl); + curl_close($curl); + return $result; +} + +/** + * curl请求指定url (post) + * @param $url + * @param array $data + * @return mixed + */ +function curlPost($url, $data = []) +{ + $ch = curl_init(); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + curl_close($ch); + return $result; +} + +if (!function_exists('array_column')) { + /** + * array_column 兼容低版本php + * (PHP < 5.5.0) + * @param $array + * @param $columnKey + * @param null $indexKey + * @return array + */ + function array_column($array, $columnKey, $indexKey = null) + { + $result = array(); + foreach ($array as $subArray) { + if (is_null($indexKey) && array_key_exists($columnKey, $subArray)) { + $result[] = is_object($subArray) ? $subArray->$columnKey : $subArray[$columnKey]; + } elseif (array_key_exists($indexKey, $subArray)) { + if (is_null($columnKey)) { + $index = is_object($subArray) ? $subArray->$indexKey : $subArray[$indexKey]; + $result[$index] = $subArray; + } elseif (array_key_exists($columnKey, $subArray)) { + $index = is_object($subArray) ? $subArray->$indexKey : $subArray[$indexKey]; + $result[$index] = is_object($subArray) ? $subArray->$columnKey : $subArray[$columnKey]; + } + } + } + return $result; + } +} + +/** + * 多维数组合并 + * @param $array1 + * @param $array2 + * @return array + */ +function array_merge_multiple($array1, $array2) +{ + $merge = $array1 + $array2; + $data = []; + foreach ($merge as $key => $val) { + if ( + isset($array1[$key]) + && is_array($array1[$key]) + && isset($array2[$key]) + && is_array($array2[$key]) + ) { + $data[$key] = array_merge_multiple($array1[$key], $array2[$key]); + } else { + $data[$key] = isset($array2[$key]) ? $array2[$key] : $array1[$key]; + } + } + return $data; +} + +/** + * 二维数组排序 + * @param $arr + * @param $keys + * @param bool $desc + * @return mixed + */ +function array_sort($arr, $keys, $desc = false) +{ + $key_value = $new_array = array(); + foreach ($arr as $k => $v) { + $key_value[$k] = $v[$keys]; + } + if ($desc) { + arsort($key_value); + } else { + asort($key_value); + } + reset($key_value); + foreach ($key_value as $k => $v) { + $new_array[$k] = $arr[$k]; + } + return $new_array; +} + +/** + * 数据导出到excel(csv文件) + * @param $fileName + * @param array $tileArray + * @param array $dataArray + */ +function export_excel($fileName, $tileArray = [], $dataArray = []) +{ + ini_set('memory_limit', '512M'); + ini_set('max_execution_time', 0); + ob_end_clean(); + ob_start(); + header("Content-Type: text/csv"); + header("Content-Disposition:filename=" . $fileName); + $fp = fopen('php://output', 'w'); + fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));// 转码 防止乱码(比如微信昵称) + fputcsv($fp, $tileArray); + $index = 0; + foreach ($dataArray as $item) { + if ($index == 1000) { + $index = 0; + ob_flush(); + flush(); + } + $index++; + fputcsv($fp, $item); + } + ob_flush(); + flush(); + ob_end_clean(); +} + +/** + * 隐藏敏感字符 + * @param $value + * @return string + */ +function substr_cut($value) +{ + $strlen = mb_strlen($value, 'utf-8'); + if ($strlen <= 1) return $value; + $firstStr = mb_substr($value, 0, 1, 'utf-8'); + $lastStr = mb_substr($value, -1, 1, 'utf-8'); + return $strlen == 2 ? $firstStr . str_repeat('*', $strlen - 1) : $firstStr . str_repeat("*", $strlen - 2) . $lastStr; +} + +/** + * 获取当前系统版本号 + * @return mixed|null + * @throws Exception + */ +function get_version() +{ + static $version = null; + if ($version) { + return $version; + } + $file = dirname(ROOT_PATH) . '/version.json'; + if (!file_exists($file)) { + throw new Exception('version.json not found'); + } + $version = json_decode(file_get_contents($file), true); + if (!is_array($version)) { + throw new Exception('version cannot be decoded'); + } + return $version['version']; +} + +/** + * 获取全局唯一标识符 + * @param bool $trim + * @return string + */ +function getGuidV4($trim = true) +{ + // Windows + if (function_exists('com_create_guid') === true) { + $charid = com_create_guid(); + return $trim == true ? trim($charid, '{}') : $charid; + } + // OSX/Linux + if (function_exists('openssl_random_pseudo_bytes') === true) { + $data = openssl_random_pseudo_bytes(16); + $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100 + $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + // Fallback (PHP 4.2+) + mt_srand((double)microtime() * 10000); + $charid = strtolower(md5(uniqid(rand(), true))); + $hyphen = chr(45); // "-" + $lbrace = $trim ? "" : chr(123); // "{" + $rbrace = $trim ? "" : chr(125); // "}" + $guidv4 = $lbrace . + substr($charid, 0, 8) . $hyphen . + substr($charid, 8, 4) . $hyphen . + substr($charid, 12, 4) . $hyphen . + substr($charid, 16, 4) . $hyphen . + substr($charid, 20, 12) . + $rbrace; + return $guidv4; +} + +/** + * 时间戳转换日期 + * @param $timeStamp + * @return false|string + */ +function format_time($timeStamp) +{ + return date('Y-m-d H:i:s', $timeStamp); +} + +/** + * 左侧填充0 + * @param $value + * @param int $padLength + * @return string + */ +function pad_left($value, $padLength = 2) +{ + return \str_pad($value, $padLength, "0", STR_PAD_LEFT); +} + +/** + * 过滤emoji表情 + * @param $text + * @return null|string|string[] + */ +function filter_emoji($text) +{ + // 此处的preg_replace用于过滤emoji表情 + // 如需支持emoji表情, 需将mysql的编码改为utf8mb4 + return preg_replace('/[\xf0-\xf7].{3}/', '', $text); +} diff --git a/source/application/common/behavior/App.php b/source/application/common/behavior/App.php new file mode 100644 index 0000000..2f6e12d --- /dev/null +++ b/source/application/common/behavior/App.php @@ -0,0 +1,29 @@ +header(), true), 'begin'); + Log::record('[ PARAM ] ' . var_export($request->param(), true), 'begin'); + } + } +} \ No newline at end of file diff --git a/source/application/common/enum/DeliveryType.php b/source/application/common/enum/DeliveryType.php new file mode 100644 index 0000000..53b5d53 --- /dev/null +++ b/source/application/common/enum/DeliveryType.php @@ -0,0 +1,36 @@ + [ + 'name' => '快递配送', + 'value' => self::EXPRESS, + ], + self::EXTRACT => [ + 'name' => '上门自提', + 'value' => self::EXTRACT, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/EnumBasics.php b/source/application/common/enum/EnumBasics.php new file mode 100644 index 0000000..0a22e23 --- /dev/null +++ b/source/application/common/enum/EnumBasics.php @@ -0,0 +1,15 @@ + [ + 'name' => '商城订单', + 'value' => self::MASTER, + ], + self::SHARING => [ + 'name' => '拼团订单', + 'value' => self::SHARING, + ], + self::RECHARGE => [ + 'name' => '余额充值', + 'value' => self::RECHARGE, + ], + ]; + } + + /** + * 获取订单类型名称 + * @return array + */ + public static function getTypeName() + { + static $names = []; + if (empty($names)) { + foreach (self::data() as $item) + $names[$item['value']] = $item['name']; + } + return $names; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/PrinterType.php b/source/application/common/enum/PrinterType.php new file mode 100644 index 0000000..27f512e --- /dev/null +++ b/source/application/common/enum/PrinterType.php @@ -0,0 +1,30 @@ + '飞鹅打印机', + self::PRINT_CENTER => '365云打印', + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/Setting.php b/source/application/common/enum/Setting.php new file mode 100644 index 0000000..61da965 --- /dev/null +++ b/source/application/common/enum/Setting.php @@ -0,0 +1,85 @@ + [ + 'value' => self::STORE, + 'describe' => '商城设置', + ], + self::TRADE => [ + 'value' => self::TRADE, + 'describe' => '交易设置', + ], + self::SMS => [ + 'value' => self::SMS, + 'describe' => '短信通知', + ], + self::TPL_MSG => [ + 'value' => self::TPL_MSG, + 'describe' => '模板消息', + ], + self::STORAGE => [ + 'value' => self::STORAGE, + 'describe' => '上传设置', + ], + self::PRINTER => [ + 'value' => self::PRINTER, + 'describe' => '小票打印', + ], + self::FULL_FREE => [ + 'value' => self::FULL_FREE, + 'describe' => '满额包邮设置', + ], + self::RECHARGE => [ + 'value' => self::RECHARGE, + 'describe' => '充值设置', + ], + self::POINTS => [ + 'value' => self::POINTS, + 'describe' => '积分设置', + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/goods/DeductStockType.php b/source/application/common/enum/goods/DeductStockType.php new file mode 100644 index 0000000..44c1db8 --- /dev/null +++ b/source/application/common/enum/goods/DeductStockType.php @@ -0,0 +1,39 @@ + [ + 'name' => '下单减库存', + 'value' => self::CREATE, + ], + self::PAYMENT => [ + 'name' => '付款减库存', + 'value' => self::PAYMENT, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/order/OrderSource.php b/source/application/common/enum/order/OrderSource.php new file mode 100644 index 0000000..9796ad9 --- /dev/null +++ b/source/application/common/enum/order/OrderSource.php @@ -0,0 +1,45 @@ + [ + 'name' => '普通订单', + 'value' => self::MASTER, + ], + self::BARGAIN => [ + 'name' => '砍价订单', + 'value' => self::BARGAIN, + ], + self::SHARP => [ + 'name' => '秒杀订单', + 'value' => self::SHARP, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/order/PayStatus.php b/source/application/common/enum/order/PayStatus.php new file mode 100644 index 0000000..ca321c2 --- /dev/null +++ b/source/application/common/enum/order/PayStatus.php @@ -0,0 +1,38 @@ + [ + 'name' => '待付款', + 'value' => self::PENDING, + ], + self::SUCCESS => [ + 'name' => '已付款', + 'value' => self::SUCCESS, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/order/PayType.php b/source/application/common/enum/order/PayType.php new file mode 100644 index 0000000..7711d26 --- /dev/null +++ b/source/application/common/enum/order/PayType.php @@ -0,0 +1,38 @@ + [ + 'name' => '余额支付', + 'value' => self::BALANCE, + ], + self::WECHAT => [ + 'name' => '微信支付', + 'value' => self::WECHAT, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/order/Status.php b/source/application/common/enum/order/Status.php new file mode 100644 index 0000000..6190219 --- /dev/null +++ b/source/application/common/enum/order/Status.php @@ -0,0 +1,47 @@ + [ + 'name' => '进行中', + 'value' => self::NORMAL, + ], + self::CANCELLED => [ + 'name' => '已取消', + 'value' => self::CANCELLED, + ], + self::APPLY_CANCEL => [ + 'name' => '待取消', + 'value' => self::APPLY_CANCEL, + ], + self::COMPLETED => [ + 'name' => '已完成', + 'value' => self::COMPLETED, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/recharge/order/PayStatus.php b/source/application/common/enum/recharge/order/PayStatus.php new file mode 100644 index 0000000..1d20bfc --- /dev/null +++ b/source/application/common/enum/recharge/order/PayStatus.php @@ -0,0 +1,38 @@ + [ + 'name' => '未支付', + 'value' => self::PENDING, + ], + self::SUCCESS => [ + 'name' => '已支付', + 'value' => self::SUCCESS, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/recharge/order/RechargeType.php b/source/application/common/enum/recharge/order/RechargeType.php new file mode 100644 index 0000000..b599e32 --- /dev/null +++ b/source/application/common/enum/recharge/order/RechargeType.php @@ -0,0 +1,38 @@ + [ + 'name' => '自定义金额', + 'value' => self::CUSTOM, + ], + self::PLAN => [ + 'name' => '套餐充值', + 'value' => self::PLAN, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/sharp/ActiveStatus.php b/source/application/common/enum/sharp/ActiveStatus.php new file mode 100644 index 0000000..2120103 --- /dev/null +++ b/source/application/common/enum/sharp/ActiveStatus.php @@ -0,0 +1,45 @@ + [ + 'name' => '已开始', + 'value' => self::ACTIVE_STATE_BEGIN, + ], + self::ACTIVE_STATE_SOON => [ + 'name' => '即将开始', + 'value' => self::ACTIVE_STATE_SOON, + ], + self::ACTIVE_STATE_NOTICE => [ + 'name' => '预告', + 'value' => self::ACTIVE_STATE_SOON, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/sharp/GoodsStatus.php b/source/application/common/enum/sharp/GoodsStatus.php new file mode 100644 index 0000000..ea35347 --- /dev/null +++ b/source/application/common/enum/sharp/GoodsStatus.php @@ -0,0 +1,45 @@ + [ + 'name' => '已开始', + 'value' => self::STATE_BEGIN, + ], + self::STATE_SOON => [ + 'name' => '未开始', + 'value' => self::STATE_SOON, + ], + self::STATE_END => [ + 'name' => '已结束', + 'value' => self::STATE_END, + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/user/balanceLog/Scene.php b/source/application/common/enum/user/balanceLog/Scene.php new file mode 100644 index 0000000..4e9eba6 --- /dev/null +++ b/source/application/common/enum/user/balanceLog/Scene.php @@ -0,0 +1,56 @@ + [ + 'name' => '用户充值', + 'value' => self::RECHARGE, + 'describe' => '用户充值:%s', + ], + self::CONSUME => [ + 'name' => '用户消费', + 'value' => self::CONSUME, + 'describe' => '用户消费:%s', + ], + self::ADMIN => [ + 'name' => '管理员操作', + 'value' => self::ADMIN, + 'describe' => '后台管理员 [%s] 操作', + ], + self::REFUND => [ + 'name' => '订单退款', + 'value' => self::REFUND, + 'describe' => '订单退款:%s', + ], + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/enum/user/grade/log/ChangeType.php b/source/application/common/enum/user/grade/log/ChangeType.php new file mode 100644 index 0000000..e3ec4d9 --- /dev/null +++ b/source/application/common/enum/user/grade/log/ChangeType.php @@ -0,0 +1,20 @@ + 变更类型 + * Class ChangeType + * @package app\common\enum\user\grade\log + */ +class ChangeType extends EnumBasics +{ + // 后台管理员设置 + const ADMIN_USER = 10; + + // 自动升级 + const AUTO_UPGRADE = 20; + +} \ No newline at end of file diff --git a/source/application/common/exception/BaseException.php b/source/application/common/exception/BaseException.php new file mode 100644 index 0000000..2cb378e --- /dev/null +++ b/source/application/common/exception/BaseException.php @@ -0,0 +1,33 @@ +code = $params['code']; + } + if (array_key_exists('msg', $params)) { + $this->message = $params['msg']; + } + } +} + diff --git a/source/application/common/exception/ExceptionHandler.php b/source/application/common/exception/ExceptionHandler.php new file mode 100644 index 0000000..ebf4b3d --- /dev/null +++ b/source/application/common/exception/ExceptionHandler.php @@ -0,0 +1,49 @@ +code = $e->code; + $this->message = $e->message; + } else { + if (config('app_debug')) { + return parent::render($e); + } + $this->code = 0; + $this->message = $e->getMessage() ?: '很抱歉,服务器内部错误'; + $this->recordErrorLog($e); + } + return json(['msg' => $this->message, 'code' => $this->code]); + } + + /** + * 将异常写入日志 + * @param Exception $e + */ + private function recordErrorLog(Exception $e) + { + Log::record($e->getMessage(), 'error'); + Log::record($e->getTraceAsString(), 'error'); + } +} diff --git a/source/application/common/library/Lock.php b/source/application/common/library/Lock.php new file mode 100644 index 0000000..2e6b49b --- /dev/null +++ b/source/application/common/library/Lock.php @@ -0,0 +1,61 @@ +config = $config; + } + + /** + * 执行查询 + * @param $express_code + * @param $express_no + * @return bool + */ + public function query($express_code, $express_no) + { + // 缓存索引 + $cacheIndex = 'express_' . $express_code . '_' . $express_no; + if ($data = Cache::get($cacheIndex)) { + return $data; + } + // 参数设置 + $postData = [ + 'customer' => $this->config['customer'], + 'param' => json_encode([ + 'resultv2' => '1', + 'com' => $express_code, + 'num' => $express_no + ]) + ]; + $postData['sign'] = strtoupper(md5($postData['param'] . $this->config['key'] . $postData['customer'])); + // 请求快递100 api + $url = 'http://poll.kuaidi100.com/poll/query.do'; + $result = curlPost($url, http_build_query($postData)); + $express = json_decode($result, true); + // 记录错误信息 + if (isset($express['returnCode']) || !isset($express['data'])) { + $this->error = isset($express['message']) ? $express['message'] : '查询失败'; + return false; + } + // 记录缓存, 时效5分钟 + Cache::set($cacheIndex, $express['data'], 300); + return $express['data']; + } + + /** + * 返回错误信息 + * @return string + */ + public function getError() + { + return $this->error; + } + +} \ No newline at end of file diff --git a/source/application/common/library/helper.php b/source/application/common/library/helper.php new file mode 100644 index 0000000..89901ac --- /dev/null +++ b/source/application/common/library/helper.php @@ -0,0 +1,147 @@ + $value) { + $item[$key] = $value; + } + } + return $source; + } + + public static function bcsub($leftOperand, $rightOperand, $scale = 2) + { + return \bcsub($leftOperand, $rightOperand, $scale); + } + + public static function bcadd($leftOperand, $rightOperand, $scale = 2) + { + return \bcadd($leftOperand, $rightOperand, $scale); + } + + public static function bcmul($leftOperand, $rightOperand, $scale = 2) + { + return \bcmul($leftOperand, $rightOperand, $scale); + } + + public static function bcdiv($leftOperand, $rightOperand, $scale = 2) + { + return \bcdiv($leftOperand, $rightOperand, $scale); + } + + public static function bccomp($leftOperand, $rightOperand, $scale = 2) + { + return \bccomp($leftOperand, $rightOperand, $scale); + } + + public static function bcequal($leftOperand, $rightOperand, $scale = 2) + { + return self::bccomp($leftOperand, $rightOperand, $scale) === 0; + } + + /** + * 数组转义为json + * @param array|\think\Collection $data + * @return string + */ + public static function jsonEncode($data) + { + return json_encode($data, JSON_UNESCAPED_UNICODE); + } + + /** + * json转义为数组 + * @param $json + * @return array + */ + public static function jsonDecode($json) + { + return json_decode($json, true); + } + +} \ No newline at end of file diff --git a/source/application/common/library/printer/Driver.php b/source/application/common/library/printer/Driver.php new file mode 100644 index 0000000..2e26eb3 --- /dev/null +++ b/source/application/common/library/printer/Driver.php @@ -0,0 +1,72 @@ + 'Feie', + PrinterTypeEnum::PRINT_CENTER => 'PrintCenter', + ]; + + /** + * 构造方法 + * Driver constructor. + * @param $printer + * @throws BaseException + */ + public function __construct($printer) + { + // 当前打印机 + $this->printer = $printer; + // 实例化当前打印机引擎 + $this->engine = $this->getEngineClass(); + } + + /** + * 执行打印请求 + * @param $content + * @return bool + */ + public function printTicket($content) + { + return $this->engine->printTicket($content); + } + + /** + * 获取错误信息 + * @return mixed + */ + public function getError() + { + return $this->engine->getError(); + } + + /** + * 获取当前的打印机引擎类 + * @return mixed + * @throws BaseException + */ + private function getEngineClass() + { + $engineName = self::$engineList[$this->printer['printer_type']['value']]; + $classSpace = __NAMESPACE__ . "\\engine\\{$engineName}"; + if (!class_exists($classSpace)) { + throw new BaseException("未找到打印机引擎类: {$engineName}"); + } + return new $classSpace($this->printer['printer_config'], $this->printer['print_times']); + } + +} diff --git a/source/application/common/library/printer/engine/Basics.php b/source/application/common/library/printer/engine/Basics.php new file mode 100644 index 0000000..64c3c75 --- /dev/null +++ b/source/application/common/library/printer/engine/Basics.php @@ -0,0 +1,55 @@ +config = $config; + $this->times = $times; + } + + /** + * 执行打印请求 + * @param $content + * @return mixed + */ + abstract protected function printTicket($content); + + /** + * 返回错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + + /** + * 创建打印的内容 + * @return string + */ + private function setContentText() + { + return ''; + } + + +} \ No newline at end of file diff --git a/source/application/common/library/printer/engine/Feie.php b/source/application/common/library/printer/engine/Feie.php new file mode 100644 index 0000000..23affd3 --- /dev/null +++ b/source/application/common/library/printer/engine/Feie.php @@ -0,0 +1,68 @@ +getParams($content); + // API请求:开始打印 + $client = new FeieHttpClient(self::IP, self::PORT); + if (!$client->post(self::PATH, $params)) { + $this->error = $client->getError(); + return false; + } + // 处理返回结果 + $result = json_decode($client->getContent()); + log_write($result); + // 返回状态 + if ($result->ret != 0) { + $this->error = $result->msg; + return false; + } + return true; + } + + /** + * 构建Api请求参数 + * @param $content + * @return array + */ + private function getParams(&$content) + { + $time = time(); + return [ + 'user' => $this->config['USER'], + 'stime' => $time, + 'sig' => sha1("{$this->config['USER']}{$this->config['UKEY']}{$time}"), + 'apiname' => 'Open_printMsg', + 'sn' => $this->config['SN'], + 'content' => $content, + 'times' => $this->times // 打印次数 + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/library/printer/engine/PrintCenter.php b/source/application/common/library/printer/engine/PrintCenter.php new file mode 100644 index 0000000..5551713 --- /dev/null +++ b/source/application/common/library/printer/engine/PrintCenter.php @@ -0,0 +1,44 @@ + [ + 'header' => "Content-type: application/x-www-form-urlencoded ", + 'method' => 'POST', + 'content' => http_build_query([ + 'deviceNo' => $this->config['deviceNo'], + 'key' => $this->config['key'], + 'printContent' => $content, + 'times' => $this->times + ]), + ] + ]); + // API请求:开始打印 + $result = file_get_contents(self::API, false, $context); + // 处理返回结果 + $result = json_decode($result); + log_write($result); + // 返回状态 + if ($result->responseCode != 0) { + $this->error = $result->msg; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/library/printer/party/FeieHttpClient.php b/source/application/common/library/printer/party/FeieHttpClient.php new file mode 100644 index 0000000..7af16b6 --- /dev/null +++ b/source/application/common/library/printer/party/FeieHttpClient.php @@ -0,0 +1,368 @@ +host = $host; + $this->port = $port; + } + + function get($path, $data = false) + { + $this->path = $path; + $this->method = 'GET'; + if ($data) { + $this->path .= '?' . $this->buildQueryString($data); + } + return $this->doRequest(); + } + + function post($path, $data) + { + $this->path = $path; + $this->method = 'POST'; + $this->postdata = $this->buildQueryString($data); + return $this->doRequest(); + } + + function buildQueryString($data) + { + $querystring = ''; + if (is_array($data)) { + foreach ($data as $key => $val) { + if (is_array($val)) { + foreach ($val as $val2) { + $querystring .= urlencode($key) . '=' . urlencode($val2) . '&'; + } + } else { + $querystring .= urlencode($key) . '=' . urlencode($val) . '&'; + } + } + $querystring = substr($querystring, 0, -1); // Eliminate unnecessary & + } else { + $querystring = $data; + } + return $querystring; + } + + function doRequest() + { + if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) { + switch ($errno) { + case -3: + $this->errormsg = 'Socket creation failed (-3)'; + break; + case -4: + $this->errormsg = 'DNS lookup failure (-4)'; + break; + case -5: + $this->errormsg = 'Connection refused or timed out (-5)'; + break; + default: + $this->errormsg = 'Connection failed (' . $errno . ')'; + $this->errormsg .= ' ' . $errstr; + $this->debug($this->errormsg); + } + return false; + } + socket_set_timeout($fp, $this->timeout); + $request = $this->buildRequest(); + $this->debug('Request', $request); + fwrite($fp, $request); + $this->headers = []; + $this->content = ''; + $this->errormsg = ''; + $inHeaders = true; + $atStart = true; + while (!feof($fp)) { + $line = fgets($fp, 4096); + if ($atStart) { + $atStart = false; + if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { + $this->errormsg = "Status code line invalid: " . htmlentities($line); + $this->debug($this->errormsg); + return false; + } +// $http_version = $m[1]; + $this->status = $m[2]; +// $status_string = $m[3]; + $this->debug(trim($line)); + continue; + } + if ($inHeaders) { + if (trim($line) == '') { + $inHeaders = false; + $this->debug('Received Headers', $this->headers); + if ($this->headers_only) { + break; + } + continue; + } + if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { + continue; + } + $key = strtolower(trim($m[1])); + $val = trim($m[2]); + if (isset($this->headers[$key])) { + if (is_array($this->headers[$key])) { + $this->headers[$key][] = $val; + } else { + $this->headers[$key] = [$this->headers[$key], $val]; + } + } else { + $this->headers[$key] = $val; + } + continue; + } + $this->content .= $line; + } + fclose($fp); + if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { + $this->debug('Content is gzip encoded, unzipping it'); + $this->content = substr($this->content, 10); + $this->content = gzinflate($this->content); + } + if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) { + $cookies = $this->headers['set-cookie']; + if (!is_array($cookies)) { + $cookies = [$cookies]; + } + foreach ($cookies as $cookie) { + if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { + $this->cookies[$m[1]] = $m[2]; + } + } + $this->cookie_host = $this->host; + } + if ($this->persist_referers) { + $this->debug('Persisting referer: ' . $this->getRequestURL()); + $this->referer = $this->getRequestURL(); + } + if ($this->handle_redirects) { + if (++$this->redirect_count >= $this->max_redirects) { + $this->errormsg = 'Number of redirects exceeded maximum (' . $this->max_redirects . ')'; + $this->debug($this->errormsg); + $this->redirect_count = 0; + return false; + } + $location = isset($this->headers['location']) ? $this->headers['location'] : ''; + $uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; + if ($location || $uri) { + $url = parse_url($location . $uri); + return $this->get($url['path']); + } + } + return true; + } + + function buildRequest() + { + $headers = []; + $headers[] = "{$this->method} {$this->path} HTTP/1.0"; + $headers[] = "Host: {$this->host}"; + $headers[] = "User-Agent: {$this->user_agent}"; + $headers[] = "Accept: {$this->accept}"; + if ($this->use_gzip) { + $headers[] = "Accept-encoding: {$this->accept_encoding}"; + } + $headers[] = "Accept-language: {$this->accept_language}"; + if ($this->referer) { + $headers[] = "Referer: {$this->referer}"; + } + if ($this->cookies) { + $cookie = 'Cookie: '; + foreach ($this->cookies as $key => $value) { + $cookie .= "$key=$value; "; + } + $headers[] = $cookie; + } + if ($this->username && $this->password) { + $headers[] = 'Authorization: BASIC ' . base64_encode($this->username . ':' . $this->password); + } + if ($this->postdata) { + $headers[] = 'Content-Type: application/x-www-form-urlencoded'; + $headers[] = 'Content-Length: ' . strlen($this->postdata); + } + $request = implode("\r\n", $headers) . "\r\n\r\n" . $this->postdata; + return $request; + } + + function getStatus() + { + return $this->status; + } + + function getContent() + { + return $this->content; + } + + function getHeaders() + { + return $this->headers; + } + + function getHeader($header) + { + $header = strtolower($header); + if (isset($this->headers[$header])) { + return $this->headers[$header]; + } else { + return false; + } + } + + function getError() + { + return $this->errormsg; + } + + function getCookies() + { + return $this->cookies; + } + + function getRequestURL() + { + $url = 'http://' . $this->host; + if ($this->port != 80) { + $url .= ':' . $this->port; + } + $url .= $this->path; + return $url; + } + + function setUserAgent($string) + { + $this->user_agent = $string; + } + + function setAuthorization($username, $password) + { + $this->username = $username; + $this->password = $password; + } + + function setCookies($array) + { + $this->cookies = $array; + } + + function useGzip($boolean) + { + $this->use_gzip = $boolean; + } + + function setPersistCookies($boolean) + { + $this->persist_cookies = $boolean; + } + + function setPersistReferers($boolean) + { + $this->persist_referers = $boolean; + } + + function setHandleRedirects($boolean) + { + $this->handle_redirects = $boolean; + } + + function setMaxRedirects($num) + { + $this->max_redirects = $num; + } + + function setHeadersOnly($boolean) + { + $this->headers_only = $boolean; + } + + function setDebug($boolean) + { + $this->debug = $boolean; + } + + function quickGet($url) + { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + if (isset($bits['query'])) { + $path .= '?' . $bits['query']; + } + $client = new self($host, $port); + if (!$client->get($path)) { + return false; + } else { + return $client->getContent(); + } + } + + function quickPost($url, $data) + { + $bits = parse_url($url); + $host = $bits['host']; + $port = isset($bits['port']) ? $bits['port'] : 80; + $path = isset($bits['path']) ? $bits['path'] : '/'; + $client = new self($host, $port); + if (!$client->post($path, $data)) { + return false; + } else { + return $client->getContent(); + } + } + + function debug($msg, $object = false) + { + if ($this->debug) { + print '
HttpClient Debug: ' . $msg; + if ($object) { + ob_start(); + print_r($object); + $content = htmlentities(ob_get_contents()); + ob_end_clean(); + print '
' . $content . '
'; + } + print '
'; + } + } +} diff --git a/source/application/common/library/sms/Driver.php b/source/application/common/library/sms/Driver.php new file mode 100644 index 0000000..ea5743d --- /dev/null +++ b/source/application/common/library/sms/Driver.php @@ -0,0 +1,73 @@ +config = $config; + // 当前引擎名称 + $this->engineName = $this->config['default']; + // 实例化当前存储引擎 + $this->engine = $this->getEngineClass(); + } + + /** + * 发送短信通知 + * @param $msgType + * @param $templateParams + * @param bool $isTest + * @return bool + */ + public function sendSms($msgType, $templateParams, $isTest = false) + { + if ($isTest === false + && $this->config['engine'][$this->engineName][$msgType]['is_enable'] == '0') { + return false; + } + return $this->engine->sendSms($msgType, $templateParams); + } + + /** + * 获取错误信息 + * @return mixed + */ + public function getError() + { + return $this->engine->getError(); + } + + /** + * 获取当前的存储引擎 + * @return mixed + * @throws Exception + */ + private function getEngineClass() + { + $classSpace = __NAMESPACE__ . '\\engine\\' . ucfirst($this->engineName); + if (!class_exists($classSpace)) { + throw new Exception('未找到存储引擎类: ' . $this->engineName); + } + return new $classSpace($this->config['engine'][$this->engineName]); + } + +} diff --git a/source/application/common/library/sms/engine/Aliyun.php b/source/application/common/library/sms/engine/Aliyun.php new file mode 100644 index 0000000..3cfdf2d --- /dev/null +++ b/source/application/common/library/sms/engine/Aliyun.php @@ -0,0 +1,86 @@ +config = $config; + } + + /** + * 发送短信通知 + * @param $msgType + * @param $templateParams + * @return bool|\stdClass + */ + public function sendSms($msgType, $templateParams) + { + $params = []; + // *** 需用户填写部分 *** + + // 必填: 短信接收号码 + $params["PhoneNumbers"] = $this->config[$msgType]['accept_phone']; + + // 必填: 短信签名,应严格按"签名名称"填写,请参考: https://dysms.console.aliyun.com/dysms.htm#/develop/sign + $params["SignName"] = $this->config['sign']; + + // 必填: 短信模板Code,应严格按"模板CODE"填写, 请参考: https://dysms.console.aliyun.com/dysms.htm#/develop/template + $params["TemplateCode"] = $this->config[$msgType]['template_code']; + + // 可选: 设置模板参数, 假如模板中存在变量需要替换则为必填项 + $params['TemplateParam'] = $templateParams; + + // 可选: 设置发送短信流水号 + // $params['OutId'] = "12345"; + + // 可选: 上行短信扩展码, 扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段 + // $params['SmsUpExtendCode'] = "1234567"; + + // *** 需用户填写部分结束, 以下代码若无必要无需更改 *** + if (!empty($params["TemplateParam"]) && is_array($params["TemplateParam"])) { + $params["TemplateParam"] = json_encode($params["TemplateParam"], JSON_UNESCAPED_UNICODE); + } + + // 初始化SignatureHelper实例用于设置参数,签名以及发送请求 + $helper = new SignatureHelper; + + // 此处可能会抛出异常,注意catch + $response = $helper->request( + $this->config['AccessKeyId'] + , $this->config['AccessKeySecret'] + , "dysmsapi.aliyuncs.com" + , array_merge($params, [ + "RegionId" => "cn-hangzhou", + "Action" => "SendSms", + "Version" => "2017-05-25", + ]) + // 选填: 启用https + , true + ); + // 记录日志 + log_write([ + 'config' => $this->config, + 'params' => $params + ]); + log_write($response); + $this->error = $response->Message; + return $response->Code === 'OK'; + } + +} diff --git a/source/application/common/library/sms/engine/Server.php b/source/application/common/library/sms/engine/Server.php new file mode 100644 index 0000000..041a1dc --- /dev/null +++ b/source/application/common/library/sms/engine/Server.php @@ -0,0 +1,19 @@ +error; + } + +} diff --git a/source/application/common/library/sms/package/aliyun/SignatureHelper.php b/source/application/common/library/sms/package/aliyun/SignatureHelper.php new file mode 100644 index 0000000..ec81131 --- /dev/null +++ b/source/application/common/library/sms/package/aliyun/SignatureHelper.php @@ -0,0 +1,88 @@ + "HMAC-SHA1", + "SignatureNonce" => uniqid(mt_rand(0, 0xffff), true), + "SignatureVersion" => "1.0", + "AccessKeyId" => $accessKeyId, + "Timestamp" => gmdate("Y-m-d\TH:i:s\Z"), + "Format" => "JSON", + ), $params); + ksort($apiParams); + + $sortedQueryStringTmp = ""; + foreach ($apiParams as $key => $value) { + $sortedQueryStringTmp .= "&" . $this->encode($key) . "=" . $this->encode($value); + } + + $stringToSign = "GET&%2F&" . $this->encode(substr($sortedQueryStringTmp, 1)); + + $sign = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true)); + + $signature = $this->encode($sign); + + $url = ($security ? 'https' : 'http') . "://{$domain}/?Signature={$signature}{$sortedQueryStringTmp}"; + + try { + $content = $this->fetchContent($url); + return json_decode($content); + } catch (\Exception $e) { + return false; + } + } + + private function encode($str) + { + $res = urlencode($str); + $res = preg_replace("/\+/", "%20", $res); + $res = preg_replace("/\*/", "%2A", $res); + $res = preg_replace("/%7E/", "~", $res); + return $res; + } + + private function fetchContent($url) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + "x-sdk-client" => "php/2.0.0" + )); + + if (substr($url, 0, 5) == 'https') { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + } + + $rtn = curl_exec($ch); + + if ($rtn === false) { + trigger_error("[CURL_" . curl_errno($ch) . "]: " . curl_error($ch), E_USER_ERROR); + } + curl_close($ch); + + return $rtn; + } +} diff --git a/source/application/common/library/storage/Driver.php b/source/application/common/library/storage/Driver.php new file mode 100644 index 0000000..94c8da6 --- /dev/null +++ b/source/application/common/library/storage/Driver.php @@ -0,0 +1,112 @@ +config = $config; + // 实例化当前存储引擎 + $this->engine = $this->getEngineClass($storage); + } + + /** + * 设置上传的文件信息 + * @param string $name + * @return mixed + */ + public function setUploadFile($name = 'iFile') + { + return $this->engine->setUploadFile($name); + } + + /** + * 设置上传的文件信息 + * @param string $filePath + * @return mixed + */ + public function setUploadFileByReal($filePath) + { + return $this->engine->setUploadFileByReal($filePath); + } + + /** + * 执行文件上传 + */ + public function upload() + { + return $this->engine->upload(); + } + + /** + * 执行文件删除 + * @param $fileName + * @return mixed + */ + public function delete($fileName) + { + return $this->engine->delete($fileName); + } + + /** + * 获取错误信息 + * @return mixed + */ + public function getError() + { + return $this->engine->getError(); + } + + /** + * 获取文件路径 + * @return mixed + */ + public function getFileName() + { + return $this->engine->getFileName(); + } + + /** + * 返回文件信息 + * @return mixed + */ + public function getFileInfo() + { + return $this->engine->getFileInfo(); + } + + /** + * 获取当前的存储引擎 + * @param null|string $storage 指定存储方式,如不指定则为系统默认 + * @return mixed + * @throws Exception + */ + private function getEngineClass($storage = null) + { + $engineName = is_null($storage) ? $this->config['default'] : $storage; + $classSpace = __NAMESPACE__ . '\\engine\\' . ucfirst($engineName); + if (!class_exists($classSpace)) { + throw new Exception('未找到存储引擎类: ' . $engineName); + } + return new $classSpace($this->config['engine'][$engineName]); + } + +} diff --git a/source/application/common/library/storage/engine/Aliyun.php b/source/application/common/library/storage/engine/Aliyun.php new file mode 100644 index 0000000..3dec707 --- /dev/null +++ b/source/application/common/library/storage/engine/Aliyun.php @@ -0,0 +1,84 @@ +config = $config; + } + + /** + * 执行上传 + * @return bool|mixed + */ + public function upload() + { + try { + $ossClient = new OssClient( + $this->config['access_key_id'], + $this->config['access_key_secret'], + $this->config['domain'], + true + ); + $result = $ossClient->uploadFile( + $this->config['bucket'], + $this->fileName, + $this->getRealPath() + ); + } catch (OssException $e) { + $this->error = $e->getMessage(); + return false; + } + return true; + } + + /** + * 删除文件 + * @param $fileName + * @return bool|mixed + */ + public function delete($fileName) + { + try { + $ossClient = new OssClient( + $this->config['access_key_id'], + $this->config['access_key_secret'], + $this->config['domain'], + true + ); + $ossClient->deleteObject($this->config['bucket'], $fileName); + } catch (OssException $e) { + $this->error = $e->getMessage(); + return false; + } + return true; + } + + /** + * 返回文件路径 + * @return mixed + */ + public function getFileName() + { + return $this->fileName; + } + +} diff --git a/source/application/common/library/storage/engine/Local.php b/source/application/common/library/storage/engine/Local.php new file mode 100644 index 0000000..a1d4082 --- /dev/null +++ b/source/application/common/library/storage/engine/Local.php @@ -0,0 +1,86 @@ +isInternal ? $this->uploadByInternal() : $this->uploadByExternal(); + } + + /** + * 外部上传(指用户上传,需验证文件类型、大小) + * @return bool + */ + private function uploadByExternal() + { + // 上传目录 + $uplodDir = WEB_PATH . 'uploads'; + // 验证文件并上传 + $info = $this->file->validate([ + 'size' => 4 * 1024 * 1024, + 'ext' => 'jpg,jpeg,png,gif' + ])->move($uplodDir, $this->fileName); + if (empty($info)) { + $this->error = $this->file->getError(); + return false; + } + return true; + } + + /** + * 内部上传(指系统上传,信任模式) + * @return bool + */ + private function uploadByInternal() + { + // 上传目录 + $uplodDir = WEB_PATH . 'uploads'; + // 要上传图片的本地路径 + $realPath = $this->getRealPath(); + if (!rename($realPath, "{$uplodDir}/$this->fileName")) { + $this->error = 'upload write error'; + return false; + } + return true; + } + + /** + * 删除文件 + * @param $fileName + * @return bool|mixed + */ + public function delete($fileName) + { + // 文件所在目录 + $filePath = WEB_PATH . "uploads/{$fileName}"; + return !file_exists($filePath) ?: unlink($filePath); + } + + /** + * 返回文件路径 + * @return mixed + */ + public function getFileName() + { + return $this->fileName; + } + +} diff --git a/source/application/common/library/storage/engine/Qcloud.php b/source/application/common/library/storage/engine/Qcloud.php new file mode 100644 index 0000000..e0124b1 --- /dev/null +++ b/source/application/common/library/storage/engine/Qcloud.php @@ -0,0 +1,93 @@ +config = $config; + // 创建COS控制类 + $this->createCosClient(); + } + + /** + * 创建COS控制类 + */ + private function createCosClient() + { + $this->cosClient = new Client([ + 'region' => $this->config['region'], + 'credentials' => [ + 'secretId' => $this->config['secret_id'], + 'secretKey' => $this->config['secret_key'], + ], + ]); + } + + /** + * 执行上传 + * @return bool|mixed + */ + public function upload() + { + // 上传文件 + // putObject(上传接口,最大支持上传5G文件) + try { + $result = $this->cosClient->putObject([ + 'Bucket' => $this->config['bucket'], + 'Key' => $this->fileName, + 'Body' => fopen($this->getRealPath(), 'rb') + ]); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + } + + /** + * 删除文件 + * @param $fileName + * @return bool|mixed + */ + public function delete($fileName) + { + try { + $result = $this->cosClient->deleteObject(array( + 'Bucket' => $this->config['bucket'], + 'Key' => $fileName + )); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + } + + /** + * 返回文件路径 + * @return mixed + */ + public function getFileName() + { + return $this->fileName; + } + +} diff --git a/source/application/common/library/storage/engine/Qiniu.php b/source/application/common/library/storage/engine/Qiniu.php new file mode 100644 index 0000000..e01ae84 --- /dev/null +++ b/source/application/common/library/storage/engine/Qiniu.php @@ -0,0 +1,88 @@ +config = $config; + } + + /** + * 执行上传 + * @return bool|mixed + * @throws \Exception + */ + public function upload() + { + // 要上传图片的本地路径 + $realPath = $this->getRealPath(); + + // 构建鉴权对象 + $auth = new Auth($this->config['access_key'], $this->config['secret_key']); + + // 要上传的空间 + $token = $auth->uploadToken($this->config['bucket']); + + // 初始化 UploadManager 对象并进行文件的上传 + $uploadMgr = new UploadManager(); + + // 调用 UploadManager 的 putFile 方法进行文件的上传 + list(, $error) = $uploadMgr->putFile($token, $this->fileName, $realPath); + + /* @var $error \Qiniu\Http\Error */ + if ($error !== null) { + $this->error = $error->message(); + return false; + } + return true; + } + + /** + * 删除文件 + * @param $fileName + * @return bool|mixed + */ + public function delete($fileName) + { + // 构建鉴权对象 + $auth = new Auth($this->config['access_key'], $this->config['secret_key']); + // 初始化 UploadManager 对象并进行文件的上传 + $bucketMgr = new BucketManager($auth); + /* @var $error \Qiniu\Http\Error */ + $error = $bucketMgr->delete($this->config['bucket'], $fileName); + if ($error !== null) { + $this->error = $error->message(); + return false; + } + return true; + } + + /** + * 返回文件路径 + * @return mixed + */ + public function getFileName() + { + return $this->fileName; + } + +} diff --git a/source/application/common/library/storage/engine/Server.php b/source/application/common/library/storage/engine/Server.php new file mode 100644 index 0000000..fb530eb --- /dev/null +++ b/source/application/common/library/storage/engine/Server.php @@ -0,0 +1,125 @@ +file = Request::instance()->file($name); + if (empty($this->file)) { + throw new Exception('未找到上传文件的信息'); + } + // 文件信息 + $this->fileInfo = $this->file->getInfo(); + // 生成保存文件名 + $this->fileName = $this->buildSaveName(); + } + + /** + * 设置上传的文件信息 + * @param string $filePath + */ + public function setUploadFileByReal($filePath) + { + // 设置为系统内部上传 + $this->isInternal = true; + // 文件信息 + $this->fileInfo = [ + 'name' => basename($filePath), + 'size' => filesize($filePath), + 'tmp_name' => $filePath, + 'error' => 0, + ]; + // 生成保存文件名 + $this->fileName = $this->buildSaveName(); + } + + /** + * 文件上传 + * @return mixed + */ + abstract protected function upload(); + + /** + * 文件删除 + * @param $fileName + * @return mixed + */ + abstract protected function delete($fileName); + + /** + * 返回上传后文件路径 + * @return mixed + */ + abstract public function getFileName(); + + /** + * 返回文件信息 + * @return mixed + */ + public function getFileInfo() + { + return $this->fileInfo; + } + + protected function getRealPath() + { + return $this->getFileInfo()['tmp_name']; + } + + /** + * 返回错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + + /** + * 生成保存文件名 + */ + private function buildSaveName() + { + // 要上传图片的本地路径 + $realPath = $this->getRealPath(); + // 扩展名 + $ext = pathinfo($this->getFileInfo()['name'], PATHINFO_EXTENSION); + // 自动生成文件名 + return date('YmdHis') . substr(md5($realPath), 0, 5) + . str_pad(rand(0, 9999), 4, '0', STR_PAD_LEFT) . ".{$ext}"; + } + +} diff --git a/source/application/common/library/wechat/Qrcode.php b/source/application/common/library/wechat/Qrcode.php new file mode 100644 index 0000000..2dd9134 --- /dev/null +++ b/source/application/common/library/wechat/Qrcode.php @@ -0,0 +1,49 @@ +getAccessToken(); + $url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={$access_token}"; + // 构建请求 + $data = compact('scene', 'width'); + !is_null($page) && $data['page'] = $page; + // 返回结果 + $result = $this->post($url, json_encode($data, JSON_UNESCAPED_UNICODE)); + // 记录日志 + log_write([ + 'describe' => '获取小程序码', + 'params' => $data, + 'result' => strpos($result, 'errcode') ? $result : 'image' + ]); + if (!strpos($result, 'errcode')) { + return $result; + } + $data = json_decode($result, true); + $error = '小程序码获取失败 ' . $data['errmsg']; + if ($data['errcode'] == 41030) { + $error = '小程序页面不存在,请先发布上线后再生成'; + } + throw new BaseException(['msg' => $error]); + } + +} \ No newline at end of file diff --git a/source/application/common/library/wechat/WxBase.php b/source/application/common/library/wechat/WxBase.php new file mode 100644 index 0000000..d1e18ad --- /dev/null +++ b/source/application/common/library/wechat/WxBase.php @@ -0,0 +1,155 @@ +setConfig($appId, $appSecret); + } + + protected function setConfig($appId = null, $appSecret = null) + { + !empty($appId) && $this->appId = $appId; + !empty($appSecret) && $this->appSecret = $appSecret; + } + + /** + * 写入日志记录 + * @param $values + * @return bool|int + */ + protected function doLogs($values) + { + return log_write($values); + } + + /** + * 获取access_token + * @return mixed + * @throws BaseException + */ + protected function getAccessToken() + { + $cacheKey = $this->appId . '@access_token'; + if (!Cache::get($cacheKey)) { + // 请求API获取 access_token + $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}"; + $result = $this->get($url); + $data = $this->jsonDecode($result); + if (array_key_exists('errcode', $data)) { + throw new BaseException(['msg' => "access_token获取失败,错误信息:{$result}"]); + } + // 记录日志 + $this->doLogs([ + 'describe' => '获取access_token', + 'url' => $url, + 'appId' => $this->appId, + 'result' => $result + ]); + // 写入缓存 + Cache::set($cacheKey, $data['access_token'], 6000); // 7000 + } + return Cache::get($cacheKey); + } + + /** + * 模拟GET请求 HTTPS的页面 + * @param string $url 请求地址 + * @return string $result + */ + protected function get($url) + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts + $result = curl_exec($curl); + curl_close($curl); + return $result; + } + + /** + * 模拟POST请求 + * @param $url + * @param array $data + * @param bool $useCert + * @param array $sslCert + * @return mixed + */ + protected function post($url, $data = [], $useCert = false, $sslCert = []) + { + $header = [ + 'Content-type: application/json;' + ]; + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HTTPHEADER, $header); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_POST, TRUE); + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); + if ($useCert == true) { + // 设置证书:cert 与 key 分别属于两个.pem文件 + curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM'); + curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem']); + curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); + curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem']); + } + $result = curl_exec($curl); + curl_close($curl); + return $result; + } + + /** + * 数组转json + * @param $data + * @return string + */ + protected function jsonEncode($data) + { + return json_encode($data, JSON_UNESCAPED_UNICODE); + } + + /** + * json转数组 + * @param $json + * @return mixed + */ + protected function jsonDecode($json) + { + return json_decode($json, true); + } + + /** + * 返回错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + +} diff --git a/source/application/common/library/wechat/WxPay.php b/source/application/common/library/wechat/WxPay.php new file mode 100644 index 0000000..66c1836 --- /dev/null +++ b/source/application/common/library/wechat/WxPay.php @@ -0,0 +1,400 @@ + 'app\api\service\order\PaySuccess', + OrderTypeEnum::SHARING => 'app\api\service\sharing\order\PaySuccess', + OrderTypeEnum::RECHARGE => 'app\api\service\recharge\PaySuccess', + ]; + + /** + * 构造函数 + * WxPay constructor. + * @param $config + */ + public function __construct($config = false) + { + parent::__construct(); + $this->config = $config; + $this->config !== false && $this->setConfig($this->config['app_id'], $this->config['app_secret']); + } + + /** + * 统一下单API + * @param $order_no + * @param $openid + * @param $totalFee + * @param int $orderType 订单类型 + * @return array + * @throws BaseException + */ + public function unifiedorder($order_no, $openid, $totalFee, $orderType = OrderTypeEnum::MASTER) + { + // 当前时间 + $time = time(); + // 生成随机字符串 + $nonceStr = md5($time . $openid); + // API参数 + $params = [ + 'appid' => $this->appId, + 'attach' => json_encode(['order_type' => $orderType]), + 'body' => $order_no, + 'mch_id' => $this->config['mchid'], + 'nonce_str' => $nonceStr, + 'notify_url' => base_url() . 'notice.php', // 异步通知地址 + 'openid' => $openid, + 'out_trade_no' => $order_no, + 'spbill_create_ip' => \request()->ip(), + 'total_fee' => $totalFee * 100, // 价格:单位分 + 'trade_type' => 'JSAPI', + ]; + // 生成签名 + $params['sign'] = $this->makeSign($params); + // 请求API + $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; + $result = $this->post($url, $this->toXml($params)); + $prepay = $this->fromXml($result); + // 请求失败 + if ($prepay['return_code'] === 'FAIL') { + throw new BaseException(['msg' => "微信支付api:{$prepay['return_msg']}", 'code' => -10]); + } + if ($prepay['result_code'] === 'FAIL') { + throw new BaseException(['msg' => "微信支付api:{$prepay['err_code_des']}", 'code' => -10]); + } + // 生成 nonce_str 供前端使用 + $paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time); + return [ + 'prepay_id' => $prepay['prepay_id'], + 'nonceStr' => $nonceStr, + 'timeStamp' => (string)$time, + 'paySign' => $paySign + ]; + } + + /** + * 支付成功异步通知 + * @throws BaseException + * @throws \Exception + * @throws \think\exception\DbException + */ + public function notify() + { +// $xml = << +// +// +// +// +// +// +// +// +// +// +// +// +// +//1 +// +// +// +//EOF; + if (!$xml = file_get_contents('php://input')) { + $this->returnCode(false, 'Not found DATA'); + } + // 将服务器返回的XML数据转化为数组 + $data = $this->fromXml($xml); + // 记录日志 + $this->doLogs($xml); + $this->doLogs($data); + // 实例化订单模型 + $model = $this->getOrderModel($data['out_trade_no'], $data['attach']); + // 订单信息 + $order = $model->getOrderInfo(); + empty($order) && $this->returnCode(false, '订单不存在'); + // 小程序配置信息 + $wxConfig = WxappModel::getWxappCache($order['wxapp_id']); + // 设置支付秘钥 + $this->config['apikey'] = $wxConfig['apikey']; + // 保存微信服务器返回的签名sign + $dataSign = $data['sign']; + // sign不参与签名算法 + unset($data['sign']); + // 生成签名 + $sign = $this->makeSign($data); + // 判断签名是否正确 判断支付状态 + if ( + ($sign !== $dataSign) + || ($data['return_code'] !== 'SUCCESS') + || ($data['result_code'] !== 'SUCCESS') + ) { + $this->returnCode(false, '签名失败'); + } + // 订单支付成功业务处理 + $status = $model->onPaySuccess(PayTypeEnum::WECHAT, $data); + if ($status == false) { + $this->returnCode(false, $model->getError()); + } + // 返回状态 + $this->returnCode(true, 'OK'); + } + + /** + * 申请退款API + * @param $transaction_id + * @param double $total_fee 订单总金额 + * @param double $refund_fee 退款金额 + * @return bool + * @throws BaseException + */ + public function refund($transaction_id, $total_fee, $refund_fee) + { + // 当前时间 + $time = time(); + // 生成随机字符串 + $nonceStr = md5($time . $transaction_id . $total_fee . $refund_fee); + // API参数 + $params = [ + 'appid' => $this->appId, + 'mch_id' => $this->config['mchid'], + 'nonce_str' => $nonceStr, + 'transaction_id' => $transaction_id, + 'out_refund_no' => $time, + 'total_fee' => $total_fee * 100, + 'refund_fee' => $refund_fee * 100, + ]; + // 生成签名 + $params['sign'] = $this->makeSign($params); + // 请求API + $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; + $result = $this->post($url, $this->toXml($params), true, $this->getCertPem()); + // 请求失败 + if (empty($result)) { + throw new BaseException(['msg' => '微信退款api请求失败']); + } + // 格式化返回结果 + $prepay = $this->fromXml($result); + // 请求失败 + if ($prepay['return_code'] === 'FAIL') { + throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]); + } + if ($prepay['result_code'] === 'FAIL') { + throw new BaseException(['msg' => 'err_code_des: ' . $prepay['err_code_des']]); + } + return true; + } + + /** + * 企业付款到零钱API + * @param $order_no + * @param $openid + * @param $amount + * @param $desc + * @return bool + * @throws BaseException + */ + public function transfers($order_no, $openid, $amount, $desc) + { + // API参数 + $params = [ + 'mch_appid' => $this->appId, + 'mchid' => $this->config['mchid'], + 'nonce_str' => md5(uniqid()), + 'partner_trade_no' => $order_no, + 'openid' => $openid, + 'check_name' => 'NO_CHECK', + 'amount' => $amount * 100, + 'desc' => $desc, + 'spbill_create_ip' => \request()->ip(), + ]; + // 生成签名 + $params['sign'] = $this->makeSign($params); + // 请求API + $url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; + $result = $this->post($url, $this->toXml($params), true, $this->getCertPem()); + // 请求失败 + if (empty($result)) { + throw new BaseException(['msg' => '微信退款api请求失败']); + } + // 格式化返回结果 + $prepay = $this->fromXml($result); + // 请求失败 + if ($prepay['return_code'] === 'FAIL') { + throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]); + } + if ($prepay['result_code'] === 'FAIL') { + throw new BaseException(['msg' => 'err_code_des: ' . $prepay['err_code_des']]); + } + return true; + } + + /** + * 获取cert证书文件 + * @return array + * @throws BaseException + */ + private function getCertPem() + { + if (empty($this->config['cert_pem']) || empty($this->config['key_pem'])) { + throw new BaseException(['msg' => '请先到后台小程序设置填写微信支付证书文件']); + } + // cert目录 + $filePath = __DIR__ . '/cert/' . $this->config['wxapp_id'] . '/'; + return [ + 'certPem' => $filePath . 'cert.pem', + 'keyPem' => $filePath . 'key.pem' + ]; + } + + /** + * 实例化订单模型 (根据attach判断) + * @param $orderNo + * @param null $attach + * @return mixed + */ + private function getOrderModel($orderNo, $attach = null) + { + $attach = json_decode($attach, true); + // 判断订单类型返回对应的订单模型 + $model = $this->modelClass[$attach['order_type']]; + return new $model($orderNo); + } + + /** + * 返回状态给微信服务器 + * @param boolean $returnCode + * @param string $msg + */ + private function returnCode($returnCode = true, $msg = null) + { + // 返回状态 + $return = [ + 'return_code' => $returnCode ? 'SUCCESS' : 'FAIL', + 'return_msg' => $msg ?: 'OK', + ]; + // 记录日志 + log_write([ + 'describe' => '返回微信支付状态', + 'data' => $return + ]); + die($this->toXml($return)); + } + + /** + * 生成paySign + * @param $nonceStr + * @param $prepay_id + * @param $timeStamp + * @return string + */ + private function makePaySign($nonceStr, $prepay_id, $timeStamp) + { + $data = [ + 'appId' => $this->appId, + 'nonceStr' => $nonceStr, + 'package' => 'prepay_id=' . $prepay_id, + 'signType' => 'MD5', + 'timeStamp' => $timeStamp, + ]; + // 签名步骤一:按字典序排序参数 + ksort($data); + $string = $this->toUrlParams($data); + // 签名步骤二:在string后加入KEY + $string = $string . '&key=' . $this->config['apikey']; + // 签名步骤三:MD5加密 + $string = md5($string); + // 签名步骤四:所有字符转为大写 + $result = strtoupper($string); + return $result; + } + + /** + * 将xml转为array + * @param $xml + * @return mixed + */ + private function fromXml($xml) + { + // 禁止引用外部xml实体 + libxml_disable_entity_loader(true); + return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); + } + + /** + * 生成签名 + * @param $values + * @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 + */ + private function makeSign($values) + { + //签名步骤一:按字典序排序参数 + ksort($values); + $string = $this->toUrlParams($values); + //签名步骤二:在string后加入KEY + $string = $string . '&key=' . $this->config['apikey']; + //签名步骤三:MD5加密 + $string = md5($string); + //签名步骤四:所有字符转为大写 + $result = strtoupper($string); + return $result; + } + + /** + * 格式化参数格式化成url参数 + * @param $values + * @return string + */ + private function toUrlParams($values) + { + $buff = ''; + foreach ($values as $k => $v) { + if ($k != 'sign' && $v != '' && !is_array($v)) { + $buff .= $k . '=' . $v . '&'; + } + } + return trim($buff, '&'); + } + + /** + * 输出xml字符 + * @param $values + * @return bool|string + */ + private function toXml($values) + { + if (!is_array($values) + || count($values) <= 0 + ) { + return false; + } + + $xml = ""; + foreach ($values as $key => $val) { + if (is_numeric($val)) { + $xml .= "<" . $key . ">" . $val . ""; + } else { + $xml .= "<" . $key . ">"; + } + } + $xml .= ""; + return $xml; + } + +} diff --git a/source/application/common/library/wechat/WxTplMsg.php b/source/application/common/library/wechat/WxTplMsg.php new file mode 100644 index 0000000..c1f2a5c --- /dev/null +++ b/source/application/common/library/wechat/WxTplMsg.php @@ -0,0 +1,64 @@ +getAccessToken(); + $url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={$accessToken}"; + // 构建请求 + $params = [ + 'touser' => $param['touser'], + 'template_id' => $param['template_id'], + 'page' => $param['page'], + 'form_id' => $param['form_id'], + 'data' => $this->createData($param['data']) + ]; + $result = $this->post($url, $this->jsonEncode($params)); + // 记录日志 + $this->doLogs(['describe' => '发送模板消息', 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + + /** + * 生成关键字数据 + * @param $data + * @return array + */ + private function createData($data) + { + $params = []; + foreach ($data as $key => $value) { + $params[$key] = [ + 'value' => $value, + 'color' => '#333333' + ]; + } + return $params; + } + +} \ No newline at end of file diff --git a/source/application/common/library/wechat/WxUser.php b/source/application/common/library/wechat/WxUser.php new file mode 100644 index 0000000..a627916 --- /dev/null +++ b/source/application/common/library/wechat/WxUser.php @@ -0,0 +1,38 @@ + $this->appId, + 'secret' => $this->appSecret, + 'grant_type' => 'authorization_code', + 'js_code' => $code + ]), true); + if (isset($result['errcode'])) { + $this->error = $result['errmsg']; + return false; + } + return $result; + } + +} \ No newline at end of file diff --git a/source/application/common/library/wechat/cert/.gitignore b/source/application/common/library/wechat/cert/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/source/application/common/library/wechat/cert/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/source/application/common/library/wechat/logs/.gitignore b/source/application/common/library/wechat/logs/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/source/application/common/library/wechat/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/source/application/common/library/wechat/wow/Order.php b/source/application/common/library/wechat/wow/Order.php new file mode 100644 index 0000000..8984de8 --- /dev/null +++ b/source/application/common/library/wechat/wow/Order.php @@ -0,0 +1,121 @@ + '导入订单', + 'update' => '更新订单信息', + 'delete' => '删除订单', + ]; + + /** + * 导入订单 + * @param $orderList + * @param int $isHistory + * @return bool + * @throws \app\common\exception\BaseException + */ + public function import($orderList, $isHistory = 0) + { + // 微信接口url + $accessToken = $this->getAccessToken(); + $url = "https://api.weixin.qq.com/mall/importorder?action=add-order&is_history={$isHistory}&access_token={$accessToken}"; + // 请求参数 + $params = $this->jsonEncode(['order_list' => $orderList]); + // 执行请求 + $result = $this->post($url, $params); + // 记录日志 + $this->doLogs(['describe' => $this->describe['import'], 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + + /** + * 更新订单 + * @param $orderList + * @param int $isHistory + * @return bool + * @throws \app\common\exception\BaseException + */ + public function update($orderList, $isHistory = 0) + { + // 微信接口url + $accessToken = $this->getAccessToken(); + $url = "https://api.weixin.qq.com/mall/importorder?action=update-order&is_history={$isHistory}&access_token={$accessToken}"; + // 请求参数 + $params = $this->jsonEncode(['order_list' => $orderList]); + // 执行请求 + $result = $this->post($url, $params); + // 记录日志 + $this->doLogs(['describe' => $this->describe['update'], 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + + /** + * 删除商品收藏 + * @param string $openId + * @param string $orderId + * @return bool + * @throws \app\common\exception\BaseException + */ + public function delete($openId, $orderId) + { + // 微信接口url + $accessToken = $this->getAccessToken(); + $url = "https://api.weixin.qq.com/mall/deleteorder?access_token={$accessToken}"; + // 请求参数 + $params = [ + 'user_open_id' => $openId, + 'order_id' => $orderId + ]; + // 执行请求 + $result = $this->post($url, $this->jsonEncode($params)); + // 记录日志 + $this->doLogs(['describe' => $this->describe['delete'], 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/library/wechat/wow/Shoping.php b/source/application/common/library/wechat/wow/Shoping.php new file mode 100644 index 0000000..40e4629 --- /dev/null +++ b/source/application/common/library/wechat/wow/Shoping.php @@ -0,0 +1,83 @@ +getAccessToken(); + $url = "https://api.weixin.qq.com/mall/addshoppinglist?access_token={$accessToken}"; + // 请求参数 + $params = $this->jsonEncode([ + 'user_open_id' => $openId, + 'sku_product_list' => $productList + ]); + // 执行请求 + $result = $this->post($url, $params); + // 记录日志 + $this->doLogs(['describe' => '新增好物圈商品收藏', 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + + /** + * 删除商品收藏 + * @param $openId + * @param $productList + * @return bool + * @throws \app\common\exception\BaseException + */ + public function delete($openId, $productList) + { + // 微信接口url + $accessToken = $this->getAccessToken(); + $url = "https://api.weixin.qq.com/mall/deleteshoppinglist?access_token={$accessToken}"; + // 请求参数 + $params = [ + 'user_open_id' => $openId, + 'sku_product_list' => $productList + ]; + // 执行请求 + $result = $this->post($url, $this->jsonEncode($params)); + // 记录日志 + $this->doLogs(['describe' => '删除好物圈商品收藏', 'url' => $url, 'params' => $params, 'result' => $result]); + // 返回结果 + $response = $this->jsonDecode($result); + if (!isset($response['errcode'])) { + $this->error = 'not found errcode'; + return false; + } + if ($response['errcode'] != 0) { + $this->error = $response['errmsg']; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/model/Article.php b/source/application/common/model/Article.php new file mode 100644 index 0000000..b97bdf2 --- /dev/null +++ b/source/application/common/model/Article.php @@ -0,0 +1,56 @@ +hasOne('uploadFile', 'file_id', 'image_id'); + } + + /** + * 关联文章分类表 + * @return \think\model\relation\BelongsTo + */ + public function category() + { + $module = self::getCalledModule() ?: 'common'; + return $this->BelongsTo("app\\{$module}\\model\\article\\Category"); + } + + /** + * 展示的浏览次数 + * @param $value + * @param $data + * @return mixed + */ + public function getShowViewsAttr($value, $data) + { + return $data['virtual_views'] + $data['actual_views']; + } + + /** + * 文章详情 + * @param $article_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($article_id) + { + return self::get($article_id, ['image', 'category']); + } + +} diff --git a/source/application/common/model/BaseModel.php b/source/application/common/model/BaseModel.php new file mode 100644 index 0000000..f4b8454 --- /dev/null +++ b/source/application/common/model/BaseModel.php @@ -0,0 +1,143 @@ +param('wxapp_id'); + } + + /** + * 定义全局的查询范围 + * @param \think\db\Query $query + */ + protected function base($query) + { + if (self::$wxapp_id > 0) { + $query->where($query->getTable() . '.wxapp_id', self::$wxapp_id); + } + } + + /** + * 设置默认的检索数据 + * @param $query + * @param array $default + * @return array + */ + protected function setQueryDefaultValue(&$query, $default = []) + { + $data = array_merge($default, $query); + foreach ($query as $key => $val) { + if (empty($val) && isset($default[$key])) { + $data[$key] = $default[$key]; + } + } + return $data; + } + + /** + * 设置基础查询条件(用于简化基础alias和field) + * @test 2019-4-25 + * @param string $alias + * @param array $join + * @return BaseModel + */ + public function setBaseQuery($alias = '', $join = []) + { + // 设置别名 + $aliasValue = $alias ?: $this->alias; + $model = $this->alias($aliasValue)->field("{$aliasValue}.*"); + // join条件 + if (!empty($join)) : foreach ($join as $item): + $model->join($item[0], "{$item[0]}.{$item[1]}={$aliasValue}." + . (isset($item[2]) ? $item[2] : $item[1])); + endforeach; endif; + return $model; + } + + /** + * 批量更新数据(支持带where条件) + * @param array $data [0 => ['data'=>[], 'where'=>[]]] + * @return \think\Collection + */ + public function updateAll($data) + { + return $this->transaction(function () use ($data) { + $result = []; + foreach ($data as $key => $item) { + $result[$key] = self::update($item['data'], $item['where']); + } + return $this->toCollection($result); + }); + } + +} diff --git a/source/application/common/model/Category.php b/source/application/common/model/Category.php new file mode 100644 index 0000000..2aeb791 --- /dev/null +++ b/source/application/common/model/Category.php @@ -0,0 +1,122 @@ +hasOne('uploadFile', 'file_id', 'image_id'); + } + + /** + * 所有分类 + * @return mixed + */ + public static function getALL() + { + $model = new static; + if (!Cache::get('category_' . $model::$wxapp_id)) { + $data = $model->with(['image'])->order(['sort' => 'asc', 'create_time' => 'asc'])->select(); + $all = !empty($data) ? $data->toArray() : []; + $tree = []; + foreach ($all as $first) { + if ($first['parent_id'] != 0) continue; + $twoTree = []; + foreach ($all as $two) { + if ($two['parent_id'] != $first['category_id']) continue; + $threeTree = []; + foreach ($all as $three) + $three['parent_id'] == $two['category_id'] + && $threeTree[$three['category_id']] = $three; + !empty($threeTree) && $two['child'] = $threeTree; + $twoTree[$two['category_id']] = $two; + } + if (!empty($twoTree)) { + array_multisort(array_column($twoTree, 'sort'), SORT_ASC, $twoTree); + $first['child'] = $twoTree; + } + $tree[$first['category_id']] = $first; + } + Cache::tag('cache')->set('category_' . $model::$wxapp_id, compact('all', 'tree')); + } + return Cache::get('category_' . $model::$wxapp_id); + } + + /** + * 获取所有分类 + * @return mixed + */ + public static function getCacheAll() + { + return self::getALL()['all']; + } + + /** + * 获取所有分类(树状结构) + * @return mixed + */ + public static function getCacheTree() + { + return self::getALL()['tree']; + } + + /** + * 获取所有分类(树状结构) + * @return string + */ + public static function getCacheTreeJson() + { + return json_encode(static::getCacheTree()); + } + + /** + * 获取指定分类下的所有子分类id + * @param $parent_id + * @param array $all + * @return array + */ + public static function getSubCategoryId($parent_id, $all = []) + { + $arrIds = [$parent_id]; + empty($all) && $all = self::getCacheAll(); + foreach ($all as $key => $item) { + if ($item['parent_id'] == $parent_id) { + unset($all[$key]); + $subIds = self::getSubCategoryId($item['category_id'], $all); + !empty($subIds) && $arrIds = array_merge($arrIds, $subIds); + } + } + return $arrIds; + } + + /** + * 指定的分类下是否存在子分类 + * @param $parentId + * @return bool + */ + protected static function hasSubCategory($parentId) + { + $all = self::getCacheAll(); + foreach ($all as $item) { + if ($item['parent_id'] == $parentId) { + return true; + } + } + return false; + } + +} diff --git a/source/application/common/model/Comment.php b/source/application/common/model/Comment.php new file mode 100644 index 0000000..4a53cf1 --- /dev/null +++ b/source/application/common/model/Comment.php @@ -0,0 +1,113 @@ +belongsTo('Order'); + } + + /** + * 订单商品 + * @return \think\model\relation\BelongsTo + */ + public function OrderGoods() + { + return $this->belongsTo('OrderGoods'); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + + /** + * 关联评价图片表 + * @return \think\model\relation\HasMany + */ + public function image() + { + return $this->hasMany('CommentImage')->order(['id' => 'asc']); + } + + /** + * 评价详情 + * @param $comment_id + * @return Comment|null + * @throws \think\exception\DbException + */ + public static function detail($comment_id) + { + return self::get($comment_id, ['user', 'orderM', 'OrderGoods', 'image.file']); + } + + /** + * 更新记录 + * @param $data + * @return bool + */ + public function edit($data) + { + return $this->transaction(function () use ($data) { + // 删除评价图片 + $this->image()->delete(); + // 添加评论图片 + isset($data['images']) && $this->addCommentImages($data['images']); + // 是否为图片评价 + $data['is_picture'] = !$this->image()->select()->isEmpty(); + // 更新评论记录 + return $this->allowField(true)->save($data); + }); + } + + /** + * 添加评论图片 + * @param $images + * @return int + */ + private function addCommentImages($images) + { + $data = array_map(function ($image_id) { + return [ + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + }, $images); + return $this->image()->saveAll($data); + } + + /** + * 获取评价列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->with(['user', 'orderM', 'OrderGoods']) + ->where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/common/model/CommentImage.php b/source/application/common/model/CommentImage.php new file mode 100644 index 0000000..7147cc7 --- /dev/null +++ b/source/application/common/model/CommentImage.php @@ -0,0 +1,25 @@ +belongsTo('UploadFile', 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/Coupon.php b/source/application/common/model/Coupon.php new file mode 100644 index 0000000..c63bc26 --- /dev/null +++ b/source/application/common/model/Coupon.php @@ -0,0 +1,117 @@ + '已领取', 'value' => 0]; + } + if ($data['total_num'] > -1 && $data['receive_num'] >= $data['total_num']) { + return ['text' => '已抢光', 'value' => 0]; + } + if ($data['expire_type'] == 20 && ($data['end_time'] + 86400) < time()) { + return ['text' => '已过期', 'value' => 0]; + } + return ['text' => '', 'value' => 1]; + } + + /** + * 优惠券颜色 + * @param $value + * @return mixed + */ + public function getColorAttr($value) + { + $status = [10 => 'blue', 20 => 'red', 30 => 'violet', 40 => 'yellow']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 优惠券类型 + * @param $value + * @return mixed + */ + public function getCouponTypeAttr($value) + { + $status = [10 => '满减券', 20 => '折扣券']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 折扣率 + * @param $value + * @return mixed + */ + public function getDiscountAttr($value) + { + return $value / 10; + } + + /** + * 有效期-开始时间 + * @param $value + * @return mixed + */ + public function getStartTimeAttr($value) + { + return ['text' => date('Y/m/d', $value), 'value' => $value]; + } + + /** + * 有效期-结束时间 + * @param $value + * @return mixed + */ + public function getEndTimeAttr($value) + { + return ['text' => date('Y/m/d', $value), 'value' => $value]; + } + + /** + * 修改器:折扣率 + * @param $value + * @return mixed + */ + public function setDiscountAttr($value) + { + return helper::bcmul($value, 10, 0); + } + + /** + * 优惠券详情 + * @param $coupon_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($coupon_id) + { + return self::get($coupon_id); + } + +} diff --git a/source/application/common/model/Delivery.php b/source/application/common/model/Delivery.php new file mode 100644 index 0000000..60342ed --- /dev/null +++ b/source/application/common/model/Delivery.php @@ -0,0 +1,87 @@ +hasMany('DeliveryRule'); + } + + /** + * 计费方式 + * @param $value + * @return mixed + */ + public function getMethodAttr($value) + { + $method = [10 => '按件数', 20 => '按重量']; + return ['text' => $method[$value], 'value' => $value]; + } + + /** + * 获取全部 + * @return mixed + */ + public static function getAll() + { + $model = new static; + return $model->order(['sort' => 'asc'])->select(); + } + + /** + * 获取列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->with(['rule']) + ->order(['sort' => 'asc']) + ->paginate(15, false, [ + 'query' => Request::instance()->request() + ]); + } + + /** + * 运费模板详情 + * @param $delivery_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($delivery_id) + { + return self::get($delivery_id, ['rule']); + } + + /** + * 获取列表(根据模板id集) + * @param $deliveryIds + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($deliveryIds) + { + return $this->with(['rule']) + ->where('delivery_id', 'in', $deliveryIds) + ->order(['sort' => 'asc']) + ->select(); + } + +} diff --git a/source/application/common/model/DeliveryRule.php b/source/application/common/model/DeliveryRule.php new file mode 100644 index 0000000..49b3413 --- /dev/null +++ b/source/application/common/model/DeliveryRule.php @@ -0,0 +1,32 @@ +order(['sort' => 'asc'])->select(); + } + + /** + * 获取列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->order(['sort' => 'asc']) + ->paginate(15, false, [ + 'query' => Request::instance()->request() + ]); + } + + /** + * 物流公司详情 + * @param $express_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($express_id) + { + return self::get($express_id); + } + + /** + * 获取物流动态信息 + * @param $express_name + * @param $express_code + * @param $express_no + * @return array|bool + */ + public function dynamic($express_name, $express_code, $express_no) + { + $data = [ + 'express_name' => $express_name, + 'express_no' => $express_no + ]; + // 实例化快递100类 + $config = Setting::getItem('store'); + $Kuaidi100 = new Kuaidi100($config['kuaidi100']); + // 请求查询接口 + $data['list'] = $Kuaidi100->query($express_code, $express_no); + if ($data['list'] === false) { + $this->error = $Kuaidi100->getError(); + return false; + } + return $data; + } + +} diff --git a/source/application/common/model/Goods.php b/source/application/common/model/Goods.php new file mode 100644 index 0000000..0d3f48b --- /dev/null +++ b/source/application/common/model/Goods.php @@ -0,0 +1,350 @@ +belongsTo('Category'); + } + + /** + * 关联商品规格表 + * @return \think\model\relation\HasMany + */ + public function sku() + { + return $this->hasMany('GoodsSku')->order(['goods_sku_id' => 'asc']); + } + + /** + * 关联商品规格关系表 + * @return \think\model\relation\BelongsToMany + */ + public function specRel() + { + return $this->belongsToMany('SpecValue', 'GoodsSpecRel')->order(['id' => 'asc']); + } + + /** + * 关联商品图片表 + * @return \think\model\relation\HasMany + */ + public function image() + { + return $this->hasMany('GoodsImage')->order(['id' => 'asc']); + } + + /** + * 关联运费模板表 + * @return \think\model\relation\BelongsTo + */ + public function delivery() + { + return $this->BelongsTo('Delivery'); + } + + /** + * 关联订单评价表 + * @return \think\model\relation\HasMany + */ + public function commentData() + { + return $this->hasMany('Comment'); + } + + /** + * 计费方式 + * @param $value + * @return mixed + */ + public function getGoodsStatusAttr($value) + { + $status = [10 => '上架', 20 => '下架']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 获取商品列表 + * @param $param + * @return mixed + * @throws \think\exception\DbException + */ + public function getList($param) + { + // 商品列表获取条件 + $params = array_merge([ + 'status' => 10, // 商品状态 + 'category_id' => 0, // 分类id + 'search' => '', // 搜索关键词 + 'sortType' => 'all', // 排序类型 + 'sortPrice' => false, // 价格排序 高低 + 'listRows' => 15, // 每页数量 + ], $param); + // 筛选条件 + $filter = []; + $params['category_id'] > 0 && $filter['category_id'] = ['IN', Category::getSubCategoryId($params['category_id'])]; + $params['status'] > 0 && $filter['goods_status'] = $params['status']; + !empty($params['search']) && $filter['goods_name'] = ['like', '%' . trim($params['search']) . '%']; + // 排序规则 + $sort = []; + if ($params['sortType'] === 'all') { + $sort = ['goods_sort', 'goods_id' => 'desc']; + } elseif ($params['sortType'] === 'sales') { + $sort = ['goods_sales' => 'desc']; + } elseif ($params['sortType'] === 'price') { + $sort = $params['sortPrice'] ? ['goods_max_price' => 'desc'] : ['goods_min_price']; + } + // 商品表名称 + $tableName = $this->getTable(); + // 多规格商品 最高价与最低价 + $GoodsSku = new GoodsSku; + $minPriceSql = $GoodsSku->field(['MIN(goods_price)']) + ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql(); + $maxPriceSql = $GoodsSku->field(['MAX(goods_price)']) + ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql(); + // 执行查询 + $list = $this + ->field(['*', '(sales_initial + sales_actual) as goods_sales', + "$minPriceSql AS goods_min_price", + "$maxPriceSql AS goods_max_price" + ]) + ->with(['category', 'image.file', 'sku']) + ->where('is_delete', '=', 0) + ->where($filter) + ->order($sort) + ->paginate($params['listRows'], false, [ + 'query' => \request()->request() + ]); + // 整理列表数据并返回 + return $this->setGoodsListData($list, true); + } + + /** + * 设置商品展示的数据 + * @param $data + * @param bool $isMultiple + * @param callable $callback + * @return mixed + */ + protected function setGoodsListData($data, $isMultiple = true, callable $callback = null) + { + if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data; + // 整理商品列表数据 + foreach ($dataSource as &$goods) { + // 商品主图 + $goods['goods_image'] = $goods['image'][0]['file_path']; + // 商品默认规格 + $goods['goods_sku'] = $goods['sku'][0]; + // 回调函数 + is_callable($callback) && call_user_func($callback, $goods); + } + return $data; + } + + /** + * 根据商品id集获取商品列表 + * @param array $goodsIds + * @param null $status + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($goodsIds, $status = null) + { + // 筛选条件 + $filter = ['goods_id' => ['in', $goodsIds]]; + $status > 0 && $filter['goods_status'] = $status; + if (!empty($goodsIds)) { + $this->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')'); + } + // 获取商品列表数据 +// ['category', 'image.file', 'sku', 'spec_rel.spec', 'delivery.rule'] + $data = $this->field(['content'], true) + ->with(['category', 'image.file', 'sku']) + ->where($filter) + ->select(); + // 整理列表数据并返回 + return $this->setGoodsListData($data, true); + } + + /** + * 商品多规格信息 + * @param \think\Collection $specRel + * @param \think\Collection $skuData + * @return array + */ + public function getManySpecData($specRel, $skuData) + { + + // spec_attr + $specAttrData = []; + foreach ($specRel as $item) { + if (!isset($specAttrData[$item['spec_id']])) { + $specAttrData[$item['spec_id']] = [ + 'group_id' => $item['spec']['spec_id'], + 'group_name' => $item['spec']['spec_name'], + 'spec_items' => [], + ]; + } + $specAttrData[$item['spec_id']]['spec_items'][] = [ + 'item_id' => $item['spec_value_id'], + 'spec_value' => $item['spec_value'], + ]; + } + + // spec_list + $specListData = []; + foreach ($skuData as $item) { + $image = (isset($item['image']) && !empty($item['image'])) ? $item['image'] : ['file_id' => 0, 'file_path' => '']; + $specListData[] = [ + 'goods_sku_id' => $item['goods_sku_id'], + 'spec_sku_id' => $item['spec_sku_id'], + 'rows' => [], + 'form' => [ + 'image_id' => $image['file_id'], + 'image_path' => $image['file_path'], + 'goods_no' => $item['goods_no'], + 'goods_price' => $item['goods_price'], + 'goods_weight' => $item['goods_weight'], + 'line_price' => $item['line_price'], + 'stock_num' => $item['stock_num'], + ], + ]; + } + return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData]; + } + + /** + * 多规格表格数据 + * @param $goods + * @return array + */ + public function getManySpecTable(&$goods) + { + $specData = $this->getManySpecData($goods['spec_rel'], $goods['sku']); + $totalRow = count($specData['spec_list']); + foreach ($specData['spec_list'] as $i => &$sku) { + $rowData = []; + $rowCount = 1; + foreach ($specData['spec_attr'] as $attr) { + $skuValues = $attr['spec_items']; + $rowCount *= count($skuValues); + $anInterBankNum = ($totalRow / $rowCount); + $point = (($i / $anInterBankNum) % count($skuValues)); + if (0 === ($i % $anInterBankNum)) { + $rowData[] = [ + 'rowspan' => $anInterBankNum, + 'item_id' => $skuValues[$point]['item_id'], + 'spec_value' => $skuValues[$point]['spec_value'] + ]; + } + } + $sku['rows'] = $rowData; + } + return $specData; + } + + /** + * 获取商品详情 + * @param $goodsId + * @return static + */ + public static function detail($goodsId) + { + /* @var $model self */ + $model = (new static)->with([ + 'category', + 'image.file', + 'sku.image', + 'spec_rel.spec', + ])->where('goods_id', '=', $goodsId) + ->find(); + if (empty($model)) { + return $model; + } + // 整理商品数据并返回 + return $model->setGoodsListData($model, false); + } + + /** + * 指定的商品规格信息 + * @param static $goods 商品详情 + * @param int $specSkuId + * @return array|bool + */ + public static function getGoodsSku($goods, $specSkuId) + { + // 获取指定的sku + $goodsSku = []; + foreach ($goods['sku'] as $item) { + if ($item['spec_sku_id'] == $specSkuId) { + $goodsSku = $item; + break; + } + } + if (empty($goodsSku)) { + return false; + } + // 多规格文字内容 + $goodsSku['goods_attr'] = ''; + if ($goods['spec_type'] == 20) { + $specRelData = helper::arrayColumn2Key($goods['spec_rel'], 'spec_value_id'); + $attrs = explode('_', $goodsSku['spec_sku_id']); + foreach ($attrs as $specValueId) { + $goodsSku['goods_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':' + . $specRelData[$specValueId]['spec_value'] . '; '; + } + } + return $goodsSku; + } + +} diff --git a/source/application/common/model/GoodsImage.php b/source/application/common/model/GoodsImage.php new file mode 100644 index 0000000..06b4e33 --- /dev/null +++ b/source/application/common/model/GoodsImage.php @@ -0,0 +1,25 @@ +belongsTo('UploadFile', 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/GoodsSku.php b/source/application/common/model/GoodsSku.php new file mode 100644 index 0000000..f0e53d5 --- /dev/null +++ b/source/application/common/model/GoodsSku.php @@ -0,0 +1,35 @@ +hasOne('uploadFile', 'file_id', 'image_id'); + } + + /** + * 获取sku信息详情 + * @param $goodsId + * @param $specSkuId + * @return GoodsSku|null + * @throws \think\exception\DbException + */ + public static function detail($goodsId, $specSkuId) + { + return static::get(['goods_id' => $goodsId, 'spec_sku_id' => $specSkuId]); + } + +} diff --git a/source/application/common/model/GoodsSpecRel.php b/source/application/common/model/GoodsSpecRel.php new file mode 100644 index 0000000..410fdb4 --- /dev/null +++ b/source/application/common/model/GoodsSpecRel.php @@ -0,0 +1,24 @@ +belongsTo('Spec'); + } + +} diff --git a/source/application/common/model/Order.php b/source/application/common/model/Order.php new file mode 100644 index 0000000..a3729f2 --- /dev/null +++ b/source/application/common/model/Order.php @@ -0,0 +1,360 @@ +hasMany("app\\{$module}\\model\\OrderGoods"); + } + + /** + * 关联订单收货地址表 + * @return \think\model\relation\HasOne + */ + public function address() + { + $module = self::getCalledModule() ?: 'common'; + return $this->hasOne("app\\{$module}\\model\\OrderAddress"); + } + + /** + * 关联订单收货地址表 + * @return \think\model\relation\HasOne + */ + public function extract() + { + $module = self::getCalledModule() ?: 'common'; + return $this->hasOne("app\\{$module}\\model\\OrderExtract"); + } + + /** + * 关联自提门店表 + * @return \think\model\relation\BelongsTo + */ + public function extractShop() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\store\\Shop", 'extract_shop_id'); + } + + /** + * 关联门店店员表 + * @return \think\model\relation\BelongsTo + */ + public function extractClerk() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\store\\shop\\Clerk", 'extract_clerk_id'); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联物流公司表 + * @return \think\model\relation\BelongsTo + */ + public function express() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\Express"); + } + + /** + * 订单状态文字描述 + * @param $value + * @param $data + * @return string + */ + public function getStateTextAttr($value, $data) + { + // 订单状态 + if (in_array($data['order_status'], [20, 30])) { + $orderStatus = [20 => '已取消', 30 => '已完成']; + return $orderStatus[$data['order_status']]; + } + // 付款状态 + if ($data['pay_status'] == 10) { + return '待付款'; + } + // 订单类型:单独购买 + if ($data['delivery_status'] == 10) { + return '已付款,待发货'; + } + if ($data['receipt_status'] == 10) { + return '已发货,待收货'; + } + return $value; + } + + /** + * 获取器:订单金额(含优惠折扣) + * @param $value + * @param $data + * @return string + */ + public function getOrderPriceAttr($value, $data) + { + // 兼容旧数据:订单金额 + if ($value == 0) { + return helper::bcadd(helper::bcsub($data['total_price'], $data['coupon_money']), $data['update_price']); + } + return $value; + } + + /** + * 改价金额(差价) + * @param $value + * @return array + */ + public function getUpdatePriceAttr($value) + { + return [ + 'symbol' => $value < 0 ? '-' : '+', + 'value' => sprintf('%.2f', abs($value)) + ]; + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getPayTypeAttr($value) + { + return ['text' => PayTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getPayStatusAttr($value) + { + return ['text' => PayStatusEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 发货状态 + * @param $value + * @return array + */ + public function getDeliveryStatusAttr($value) + { + $status = [10 => '待发货', 20 => '已发货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 收货状态 + * @param $value + * @return array + */ + public function getReceiptStatusAttr($value) + { + $status = [10 => '待收货', 20 => '已收货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 收货状态 + * @param $value + * @return array + */ + public function getOrderStatusAttr($value) + { + $status = [10 => '进行中', 20 => '已取消', 21 => '待取消', 30 => '已完成']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 配送方式 + * @param $value + * @return array + */ + public function getDeliveryTypeAttr($value) + { + return ['text' => DeliveryTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 生成订单号 + * @return string + */ + public function orderNo() + { + return OrderService::createOrderNo(); + } + + /** + * 订单详情 + * @param array|int $where + * @param array $with + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($where, $with = [ + 'user', + 'address', + 'goods' => ['image'], + 'extract', + 'express', + 'extract_shop.logo', + 'extract_clerk' + ]) + { + is_array($where) ? $filter = $where : $filter['order_id'] = (int)$where; + return self::get($filter, $with); + } + + /** + * 批量获取订单列表 + * @param $orderIds + * @param array $with 关联查询 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($orderIds, $with = []) + { + $data = $this->getListByInArray('order_id', $orderIds, $with); + return helper::arrayColumn2Key($data, 'order_id'); + } + + /** + * 批量获取订单列表 + * @param string $field + * @param array $data + * @param array $with + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getListByInArray($field, $data, $with = []) + { + return $this->with($with) + ->where($field, 'in', $data) + ->where('is_delete', '=', 0) + ->select(); + } + + /** + * 根据订单号批量查询 + * @param $orderNos + * @param array $with + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByOrderNos($orderNos, $with = []) + { + return $this->getListByInArray('order_no', $orderNos, $with); + } + + /** + * 批量更新订单 + * @param $orderIds + * @param $data + * @return false|int + */ + public function onBatchUpdate($orderIds, $data) + { + return $this->isUpdate(true)->save($data, ['order_id' => ['in', $orderIds]]); + } + + /** + * 确认核销(自提订单) + * @param int $extractClerkId 核销员id + * @return bool|false|int + */ + public function verificationOrder($extractClerkId) + { + if ( + $this['pay_status']['value'] != 20 + || $this['delivery_type']['value'] != DeliveryTypeEnum::EXTRACT + || $this['delivery_status']['value'] == 20 + || in_array($this['order_status']['value'], [20, 21]) + ) { + $this->error = '该订单不满足核销条件'; + return false; + } + return $this->transaction(function () use ($extractClerkId) { + // 更新订单状态:已发货、已收货 + $status = $this->save([ + 'extract_clerk_id' => $extractClerkId, // 核销员 + 'delivery_status' => 20, + 'delivery_time' => time(), + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); + // 新增订单核销记录 + ShopOrder::add( + $this['order_id'], + $this['extract_shop_id'], + $this['extract_clerk_id'], + OrderTypeEnum::MASTER + ); + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::MASTER); + $OrderCompleteService->complete([$this], static::$wxapp_id); + return $status; + }); + } + +} diff --git a/source/application/common/model/OrderAddress.php b/source/application/common/model/OrderAddress.php new file mode 100644 index 0000000..9029704 --- /dev/null +++ b/source/application/common/model/OrderAddress.php @@ -0,0 +1,45 @@ + Region::getNameById($data['province_id']), + 'city' => Region::getNameById($data['city_id']), + 'region' => $data['region_id'] == 0 ? '' : Region::getNameById($data['region_id']), + ]; + } + + /** + * 获取完整地址 + * @return string + */ + public function getFullAddress() + { + return $this['region']['province'] . $this['region']['city'] . $this['region']['region'] . $this['detail']; + } + +} diff --git a/source/application/common/model/OrderExtract.php b/source/application/common/model/OrderExtract.php new file mode 100644 index 0000000..344a077 --- /dev/null +++ b/source/application/common/model/OrderExtract.php @@ -0,0 +1,15 @@ +belongsTo($model, 'image_id', 'file_id'); + } + + /** + * 关联商品表 + * @return \think\model\relation\BelongsTo + */ + public function goods() + { + return $this->belongsTo('Goods'); + } + + /** + * 关联商品sku表 + * @return \think\model\relation\BelongsTo + */ +// public function sku() +// { +// return $this->belongsTo('GoodsSku', 'spec_sku_id', 'spec_sku_id'); +// } + + /** + * 关联订单主表 + * @return \think\model\relation\BelongsTo + */ + public function orderM() + { + return $this->belongsTo('Order'); + } + + /** + * 售后单记录表 + * @return \think\model\relation\HasOne + */ + public function refund() + { + return $this->hasOne('OrderRefund'); + } + + /** + * 订单商品详情 + * @param $where + * @return OrderGoods|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where, ['image', 'refund']); + } + +} diff --git a/source/application/common/model/OrderRefund.php b/source/application/common/model/OrderRefund.php new file mode 100644 index 0000000..3ee8205 --- /dev/null +++ b/source/application/common/model/OrderRefund.php @@ -0,0 +1,113 @@ +belongsTo('User'); + } + + /** + * 关联订单主表 + * @return \think\model\relation\BelongsTo + */ + public function orderMaster() + { + return $this->belongsTo('Order'); + } + + /** + * 关联订单商品表 + * @return \think\model\relation\BelongsTo + */ + public function orderGoods() + { + return $this->belongsTo('OrderGoods'); + } + + /** + * 关联图片记录表 + * @return \think\model\relation\HasMany + */ + public function image() + { + return $this->hasMany('OrderRefundImage'); + } + + /** + * 关联物流公司表 + * @return \think\model\relation\BelongsTo + */ + public function express() + { + return $this->belongsTo('Express'); + } + + /** + * 关联用户表 + * @return \think\model\relation\HasOne + */ + public function address() + { + return $this->hasOne('OrderRefundAddress'); + } + + /** + * 售后类型 + * @param $value + * @return array + */ + public function getTypeAttr($value) + { + $status = [10 => '退货退款', 20 => '换货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 商家是否同意售后 + * @param $value + * @return array + */ + public function getIsAgreeAttr($value) + { + $status = [0 => '待审核', 10 => '已同意', 20 => '已拒绝']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 售后单状态 + * @param $value + * @return array + */ + public function getStatusAttr($value) + { + $status = [0 => '进行中', 10 => '已拒绝', 20 => '已完成', 30 => '已取消']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 售后单详情 + * @param $where + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where, ['order_master', 'image.file', 'order_goods.image', 'express', 'address']); + } + +} \ No newline at end of file diff --git a/source/application/common/model/OrderRefundAddress.php b/source/application/common/model/OrderRefundAddress.php new file mode 100644 index 0000000..cd23fa4 --- /dev/null +++ b/source/application/common/model/OrderRefundAddress.php @@ -0,0 +1,15 @@ +belongsTo('UploadFile', 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/Printer.php b/source/application/common/model/Printer.php new file mode 100644 index 0000000..3a59ed6 --- /dev/null +++ b/source/application/common/model/Printer.php @@ -0,0 +1,96 @@ + $value, 'text' => $printerType[$value]]; + } + + /** + * 自动转换printer_config为array格式 + * @param $value + * @return string + */ + public function getPrinterConfigAttr($value) + { + return json_decode($value, true); + } + + /** + * 自动转换printer_config为json格式 + * @param $value + * @return string + */ + public function setPrinterConfigAttr($value) + { + return json_encode($value); + } + + /** + * 获取全部 + * @return mixed + */ + public static function getAll() + { + return (new static)->where('is_delete', '=', 0) + ->order(['sort' => 'asc'])->select(); + } + + /** + * 获取列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->where('is_delete', '=', 0) + ->order(['sort' => 'asc']) + ->paginate(15, false, [ + 'query' => Request::instance()->request() + ]); + } + + /** + * 物流公司详情 + * @param $printer_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($printer_id) + { + return self::get($printer_id); + } + +} diff --git a/source/application/common/model/Region.php b/source/application/common/model/Region.php new file mode 100644 index 0000000..32ca8bf --- /dev/null +++ b/source/application/common/model/Region.php @@ -0,0 +1,208 @@ + 'integer', + 'pid' => 'integer', + 'level' => 'integer', + ]; + + // 当前数据版本号 + private static $version = '1.2.3'; + + // 县级市别名 (兼容微信端命名) + private static $county = [ + '省直辖县级行政区划', + '自治区直辖县级行政区划', + ]; + + /** + * 根据id获取地区名称 + * @param $id + * @return string + */ + public static function getNameById($id) + { + return $id > 0 ? self::getCacheAll()[$id]['name'] : '其他'; + } + + /** + * 根据名称获取地区id + * @param $name + * @param int $level + * @param int $pid + * @return mixed + */ + public static function getIdByName($name, $level = 0, $pid = 0) + { + // 兼容:微信端"省直辖县级行政区划" + if (in_array($name, static::$county)) { + $name = '直辖县级'; + } + $data = self::getCacheAll(); + foreach ($data as $item) { + if ($item['name'] == $name && $item['level'] == $level && $item['pid'] == $pid) + return $item['id']; + } + return 0; + } + + /** + * 获取所有地区(树状结构) + * @return mixed + */ + public static function getCacheTree() + { + return static::getCacheData('tree'); + } + + /** + * 获取所有地区列表 + * @return mixed + */ + public static function getCacheAll() + { + return static::getCacheData('all'); + } + + /** + * 获取所有地区的总数 + * @return mixed + */ + public static function getCacheCounts() + { + return static::getCacheData('counts'); + } + + /** + * 获取缓存中的数据(存入静态变量) + * @param null $item + * @return array|mixed + */ + private static function getCacheData($item = null) + { + static $cacheData = []; + if (empty($cacheData)) { + $static = new static; + $cacheData = $static->regionCache(); + } + if (is_null($item)) { + return $cacheData; + } + return $cacheData[$item]; + } + + /** + * 获取地区缓存 + * @return array|mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function regionCache() + { + // 缓存的数据 + $complete = Cache::get('region'); + // 如果存在缓存则返回缓存的数据,否则从数据库中查询 + // 条件1: 获取缓存数据 + // 条件2: 数据版本号要与当前一致 + if ( + !empty($complete) + && isset($complete['version']) + && $complete['version'] == self::$version + ) { + return $complete; + } + // 所有地区 + $allList = $tempList = $this->getAllList(); + // 已完成的数据 + $complete = [ + 'all' => $allList, + 'tree' => $this->getTreeList($allList), + 'counts' => $this->getCount($allList), + 'version' => self::$version, + ]; + // 写入缓存 + Cache::tag('cache')->set('region', $complete); + return $complete; + } + + private static function getCount($allList) + { + $counts = [ + 'total' => count($allList), + 'province' => 0, + 'city' => 0, + 'region' => 0, + ]; + $level = [1 => 'province', 2 => 'city', 3 => 'region']; + foreach ($allList as $item) { + $counts[$level[$item['level']]]++; + } + return $counts; + } + + /** + * 格式化为树状格式 + * @param $allList + * @return array + */ + private function getTreeList($allList) + { + $treeList = []; + foreach ($allList as $pKey => $province) { + if ($province['level'] == 1) { // 省份 + $treeList[$province['id']] = $province; + unset($allList[$pKey]); + foreach ($allList as $cKey => $city) { + if ($city['level'] == 2 && $city['pid'] == $province['id']) { // 城市 + $treeList[$province['id']]['city'][$city['id']] = $city; + unset($allList[$cKey]); + foreach ($allList as $rKey => $region) { + if ($region['level'] == 3 && $region['pid'] == $city['id']) { // 地区 + $treeList[$province['id']]['city'][$city['id']]['region'][$region['id']] = $region; + unset($allList[$rKey]); + } + } + } + } + } + } + return $treeList; + } + + /** + * 从数据库中获取所有地区 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getAllList() + { + $list = self::useGlobalScope(false) + ->field('id, pid, name, level') + ->select() + ->toArray(); + return helper::arrayColumn2Key($list, 'id'); + } +} diff --git a/source/application/common/model/ReturnAddress.php b/source/application/common/model/ReturnAddress.php new file mode 100644 index 0000000..28ac670 --- /dev/null +++ b/source/application/common/model/ReturnAddress.php @@ -0,0 +1,25 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set('setting_' . $wxapp_id, $data); + } + return $static->getMergeData($data); + } + + /** + * 合并用户设置与默认数据 + * @param $userData + * @return array + */ + private function getMergeData($userData) + { + $defaultData = $this->defaultData(); + // 商城设置:配送方式 + if (isset($userData['store']['values']['delivery_type'])) { + unset($defaultData['store']['values']['delivery_type']); + } + return array_merge_multiple($defaultData, $userData); + } + + /** + * 默认配置 + * @param null|string $storeName + * @return array + */ + public function defaultData($storeName = null) + { + return [ + 'store' => [ + 'key' => 'store', + 'describe' => '商城设置', + 'values' => [ + // 商城名称 + 'name' => $storeName ?: '萤火小程序商城', + // 配送方式 + 'delivery_type' => array_keys(DeliveryTypeEnum::data()), + // 快递100 + 'kuaidi100' => [ + 'customer' => '', + 'key' => '', + ] + ], + ], + 'trade' => [ + 'key' => 'trade', + 'describe' => '交易设置', + 'values' => [ + 'order' => [ + 'close_days' => '3', + 'receive_days' => '10', + 'refund_days' => '7' + ], + 'freight_rule' => '10', + ] + ], + 'storage' => [ + 'key' => 'storage', + 'describe' => '上传设置', + 'values' => [ + 'default' => 'local', + 'engine' => [ + 'local' => [], + 'qiniu' => [ + 'bucket' => '', + 'access_key' => '', + 'secret_key' => '', + 'domain' => 'http://' + ], + 'aliyun' => [ + 'bucket' => '', + 'access_key_id' => '', + 'access_key_secret' => '', + 'domain' => 'http://' + ], + 'qcloud' => [ + 'bucket' => '', + 'region' => '', + 'secret_id' => '', + 'secret_key' => '', + 'domain' => 'http://' + ], + ] + ], + ], + 'sms' => [ + 'key' => 'sms', + 'describe' => '短信通知', + 'values' => [ + 'default' => 'aliyun', + 'engine' => [ + 'aliyun' => [ + 'AccessKeyId' => '', + 'AccessKeySecret' => '', + 'sign' => '萤火科技', + 'order_pay' => [ + 'is_enable' => '0', + 'template_code' => '', + 'accept_phone' => '', + ], + ], + ], + ], + ], + 'tplMsg' => [ + 'key' => 'tplMsg', + 'describe' => '模板消息', + 'values' => [ + 'payment' => [ + 'is_enable' => '0', + 'template_id' => '', + ], + 'delivery' => [ + 'is_enable' => '0', + 'template_id' => '', + ], + 'refund' => [ + 'is_enable' => '0', + 'template_id' => '', + ], + ], + ], + 'printer' => [ + 'key' => 'printer', + 'describe' => '小票打印机设置', + 'values' => [ + 'is_open' => '0', // 是否开启打印 + 'printer_id' => '', // 打印机id + 'order_status' => [], // 订单类型 10下单打印 20付款打印 30确认收货打印 + ], + ], + 'full_free' => [ + 'key' => 'full_free', + 'describe' => '满额包邮设置', + 'values' => [ + 'is_open' => '0', // 是否开启满额包邮 + 'money' => '', // 单笔订单额度 + 'notin_region' => [ // 不参与包邮的地区 + 'province' => [], + 'citys' => [], + 'treeData' => [], + ], + 'notin_goods' => [], // 不参与包邮的商品 (商品id集) + ], + ], + 'recharge' => [ + 'key' => 'recharge', + 'describe' => '用户充值设置', + 'values' => [ + 'is_entrance' => '1', // 是否允许用户充值 + 'is_custom' => '1', // 是否允许自定义金额 + 'is_match_plan' => '1', // 自定义金额是否自动匹配合适的套餐 + 'describe' => "1. 账户充值仅限微信在线方式支付,充值金额实时到账;\n" . + "2. 账户充值套餐赠送的金额即时到账;\n" . + "3. 账户余额有效期:自充值日起至用完即止;\n" . + "4. 若有其它疑问,可拨打客服电话400-000-1234", // 充值说明 + ], + ], + 'points' => [ + 'key' => 'points', + 'describe' => '积分设置', + 'values' => [ + 'points_name' => '积分', // 积分名称自定义 + 'is_shopping_gift' => '0', // 是否开启购物送积分 + 'gift_ratio' => '100', // 是否开启购物送积分 + 'is_shopping_discount' => '0', // 是否允许下单使用积分抵扣 + 'discount' => [ // 积分抵扣 + 'discount_ratio' => '0.01', // 积分抵扣比例 + 'full_order_price' => '100.00', // 订单满[?]元 + 'max_money_ratio' => '10', // 最高可抵扣订单额百分比 + ], + // 充值说明 + 'describe' => "a) 积分不可兑现、不可转让,仅可在本平台使用;\n" . + "b) 您在本平台参加特定活动也可使用积分,详细使用规则以具体活动时的规则为准;\n" . + "c) 积分的数值精确到个位(小数点后全部舍弃,不进行四舍五入)\n" . + "d) 买家在完成该笔交易(订单状态为“已签收”)后才能得到此笔交易的相应积分,如购买商品参加店铺其他优惠,则优惠的金额部分不享受积分获取;", + ], + ], + ]; + } + +} diff --git a/source/application/common/model/Spec.php b/source/application/common/model/Spec.php new file mode 100644 index 0000000..ba30cc7 --- /dev/null +++ b/source/application/common/model/Spec.php @@ -0,0 +1,15 @@ +belongsTo('Spec'); + } + +} diff --git a/source/application/common/model/Store.php b/source/application/common/model/Store.php new file mode 100644 index 0000000..1e0b898 --- /dev/null +++ b/source/application/common/model/Store.php @@ -0,0 +1,13 @@ +belongsTo('UploadGroup', 'group_id'); + } + + /** + * 获取图片完整路径 + * @param $value + * @param $data + * @return string + */ + public function getFilePathAttr($value, $data) + { + if ($data['storage'] === 'local') { + return self::$base_url . 'uploads/' . $data['file_name']; + } + return $data['file_url'] . '/' . $data['file_name']; + } + + /** + * 文件详情 + * @param $file_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($file_id) + { + return self::get($file_id); + } + + /** + * 根据文件名查询文件id + * @param $fileName + * @return mixed + */ + public static function getFildIdByName($fileName) + { + return (new static)->where(['file_name' => $fileName])->value('file_id'); + } + + /** + * 查询文件id + * @param $fileId + * @return mixed + */ + public static function getFileName($fileId) + { + return (new static)->where(['file_id' => $fileId])->value('file_name'); + } + + /** + * 添加新记录 + * @param $data + * @return false|int + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + return $this->save($data); + } + +} diff --git a/source/application/common/model/UploadFileUsed.php b/source/application/common/model/UploadFileUsed.php new file mode 100644 index 0000000..30aefbc --- /dev/null +++ b/source/application/common/model/UploadFileUsed.php @@ -0,0 +1,14 @@ +belongsTo("app\\{$module}\\model\\user\\Grade"); + } + + /** + * 关联收货地址表 + * @return \think\model\relation\HasMany + */ + public function address() + { + return $this->hasMany('UserAddress'); + } + + /** + * 关联收货地址表 (默认地址) + * @return \think\model\relation\BelongsTo + */ + public function addressDefault() + { + return $this->belongsTo('UserAddress', 'address_id'); + } + + /** + * 显示性别 + * @param $value + * @return mixed + */ + public function getGenderAttr($value) + { + return $this->gender[$value]; + } + + /** + * 获取用户信息 + * @param $where + * @param $with + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($where, $with = ['address', 'addressDefault']) + { + $filter = ['is_delete' => 0]; + if (is_array($where)) { + $filter = array_merge($filter, $where); + } else { + $filter['user_id'] = (int)$where; + } + return static::get($filter, $with); + } + + /** + * 累积用户的实际消费金额 + * @param $userId + * @param $expendMoney + * @return int|true + * @throws \think\Exception + */ + public function setIncUserExpend($userId, $expendMoney) + { + return $this->where(['user_id' => $userId])->setInc('expend_money', $expendMoney); + } + + /** + * 指定会员等级下是否存在用户 + * @param $gradeId + * @return bool + */ + public static function checkExistByGradeId($gradeId) + { + $model = new static; + return !!$model->where('grade_id', '=', (int)$gradeId) + ->where('is_delete', '=', 0) + ->value('user_id'); + } + + /** + * 累积用户总消费金额 + * @param $money + * @return int|true + * @throws \think\Exception + */ + public function setIncPayMoney($money) + { + return $this->setInc('pay_money', $money); + } + + /** + * 累积用户实际消费的金额 (批量) + * @param $data + * @return array|false + * @throws \Exception + */ + public function onBatchIncExpendMoney($data) + { + foreach ($data as $userId => $expendMoney) { + $this->where(['user_id' => $userId])->setInc('expend_money', $expendMoney); + } + return true; + } + + /** + * 累积用户的可用积分数量 (批量) + * @param $data + * @return array|false + * @throws \Exception + */ + public function onBatchIncPoints($data) + { + foreach ($data as $userId => $expendMoney) { + $this->where(['user_id' => $userId])->setInc('points', $expendMoney); + } + return true; + } + + /** + * 累积用户的可用积分 + * @param $points + * @param $describe + * @return int|true + * @throws \think\Exception + */ + public function setIncPoints($points, $describe) + { + // 新增积分变动明细 + PointsLogModel::add([ + 'user_id' => $this['user_id'], + 'value' => $points, + 'describe' => $describe, + ]); + // 更新用户可用积分 + return $this->setInc('points', $points); + } + +} diff --git a/source/application/common/model/UserAddress.php b/source/application/common/model/UserAddress.php new file mode 100644 index 0000000..dae311f --- /dev/null +++ b/source/application/common/model/UserAddress.php @@ -0,0 +1,36 @@ + Region::getNameById($data['province_id']), + 'city' => Region::getNameById($data['city_id']), + 'region' => $data['region_id'] == 0 ? $data['district'] + : Region::getNameById($data['region_id']), + ]; + } + +} diff --git a/source/application/common/model/UserCoupon.php b/source/application/common/model/UserCoupon.php new file mode 100644 index 0000000..be22b35 --- /dev/null +++ b/source/application/common/model/UserCoupon.php @@ -0,0 +1,133 @@ +belongsTo('User'); + } + + /** + * 优惠券状态 + * @param $value + * @param $data + * @return array + */ + public function getStateAttr($value, $data) + { + if ($data['is_use']) { + return ['text' => '已使用', 'value' => 0]; + } + if ($data['is_expire']) { + return ['text' => '已过期', 'value' => 0]; + } + return ['text' => '', 'value' => 1]; + } + + /** + * 优惠券颜色 + * @param $value + * @return mixed + */ + public function getColorAttr($value) + { + $status = [10 => 'blue', 20 => 'red', 30 => 'violet', 40 => 'yellow']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 优惠券类型 + * @param $value + * @return mixed + */ + public function getCouponTypeAttr($value) + { + $status = [10 => '满减券', 20 => '折扣券']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 折扣率 + * @param $value + * @return mixed + */ + public function getDiscountAttr($value) + { + return $value / 10; + } + + /** + * 有效期-开始时间 + * @param $value + * @return mixed + */ + public function getStartTimeAttr($value) + { + return ['text' => date('Y/m/d', $value), 'value' => $value]; + } + + /** + * 有效期-结束时间 + * @param $value + * @return mixed + */ + public function getEndTimeAttr($value) + { + return ['text' => date('Y/m/d', $value), 'value' => $value]; + } + + /** + * 优惠券详情 + * @param $coupon_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($coupon_id) + { + return static::get($coupon_id); + } + + /** + * 设置优惠券使用状态 + * @param int $couponId 用户的优惠券id + * @param bool $isUse 是否已使用 + * @return false|int + */ + public static function setIsUse($couponId, $isUse = true) + { + return (new static)->save(['is_use' => (int)$isUse], ['user_coupon_id' => $couponId]); + } + +} \ No newline at end of file diff --git a/source/application/common/model/Wxapp.php b/source/application/common/model/Wxapp.php new file mode 100644 index 0000000..650f9bc --- /dev/null +++ b/source/application/common/model/Wxapp.php @@ -0,0 +1,68 @@ +hasOne('WxappNavbar'); + } + + /** + * 小程序页面 + * @return \think\model\relation\HasOne + */ + public function diyPage() + { + return $this->hasOne('WxappPage'); + } + + /** + * 获取小程序信息 + * @param null $wxapp_id + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($wxapp_id = null) + { + return self::get($wxapp_id ?: []); + } + + /** + * 从缓存中获取小程序信息 + * @param null $wxapp_id + * @return mixed|null|static + * @throws BaseException + * @throws \think\exception\DbException + */ + public static function getWxappCache($wxapp_id = null) + { + if (is_null($wxapp_id)) { + $self = new static(); + $wxapp_id = $self::$wxapp_id; + } + if (!$data = Cache::get('wxapp_' . $wxapp_id)) { + $data = self::detail($wxapp_id); + if (empty($data)) throw new BaseException(['msg' => '未找到当前小程序信息']); + Cache::tag('cache')->set('wxapp_' . $wxapp_id, $data); + } + return $data; + } + +} diff --git a/source/application/common/model/WxappCategory.php b/source/application/common/model/WxappCategory.php new file mode 100644 index 0000000..3333bec --- /dev/null +++ b/source/application/common/model/WxappCategory.php @@ -0,0 +1,24 @@ +order(['sort' => 'asc'])->select(); + } + + /** + * 帮助详情 + * @param $help_id + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($help_id) + { + return self::get($help_id); + } + +} diff --git a/source/application/common/model/WxappNavbar.php b/source/application/common/model/WxappNavbar.php new file mode 100644 index 0000000..721547d --- /dev/null +++ b/source/application/common/model/WxappNavbar.php @@ -0,0 +1,51 @@ + '#000000', 20 => '#ffffff']; + return ['text' => $color[$value], 'value' => $value]; + } + + /** + * 小程序导航栏详情 + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail() + { + return self::get([]); + } + + /** + * 新增小程序导航栏默认设置 + * @param $wxapp_id + * @param $wxapp_title + * @return false|int + */ + public function insertDefault($wxapp_id, $wxapp_title) + { + return $this->save([ + 'wxapp_title' => $wxapp_title, + 'top_text_color' => 20, + 'top_background_color' => '#fd4a5f', + 'wxapp_id' => $wxapp_id + ]); + } + +} diff --git a/source/application/common/model/WxappPage.php b/source/application/common/model/WxappPage.php new file mode 100644 index 0000000..f715177 --- /dev/null +++ b/source/application/common/model/WxappPage.php @@ -0,0 +1,697 @@ + 'page', + 'name' => '页面设置', + 'params' => [ + 'name' => '页面名称', + 'title' => '页面标题', + 'share_title' => '分享标题' + ], + 'style' => [ + 'titleTextColor' => 'black', + 'titleBackgroundColor' => '#ffffff', + ] + ]; + } + + /** + * 页面diy元素默认数据 + * @return array + */ + public function getDefaultItems() + { + return [ + 'search' => [ + 'name' => '搜索框', + 'type' => 'search', + 'params' => ['placeholder' => '请输入关键字进行搜索'], + 'style' => [ + 'textAlign' => 'left', + 'searchStyle' => 'square' + ] + ], + 'banner' => [ + 'name' => '图片轮播', + 'type' => 'banner', + 'style' => [ + 'btnColor' => '#ffffff', + 'btnShape' => 'round' + ], + 'params' => [ + 'interval' => '2800' + ], + 'data' => [ + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'linkUrl' => '' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'linkUrl' => '' + ] + ] + ], + 'imageSingle' => [ + 'name' => '单图组', + 'type' => 'imageSingle', + 'style' => [ + 'paddingTop' => 0, + 'paddingLeft' => 0, + 'background' => '#ffffff' + ], + 'data' => [ + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'imgName' => 'image-1.jpg', + 'linkUrl' => '' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/banner/01.png', + 'imgName' => 'banner-2.jpg', + 'linkUrl' => '' + ] + ] + ], + 'navBar' => [ + 'name' => '导航组', + 'type' => 'navBar', + 'style' => ['background' => '#ffffff', 'rowsNum' => '4'], + 'data' => [ + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/navbar/01.png', + 'imgName' => 'icon-1.png', + 'linkUrl' => '', + 'text' => '按钮文字1', + 'color' => '#666666' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/navbar/01.png', + 'imgName' => 'icon-2.jpg', + 'linkUrl' => '', + 'text' => '按钮文字2', + 'color' => '#666666' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/navbar/01.png', + 'imgName' => 'icon-3.jpg', + 'linkUrl' => '', + 'text' => '按钮文字3', + 'color' => '#666666' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/navbar/01.png', + 'imgName' => 'icon-4.jpg', + 'linkUrl' => '', + 'text' => '按钮文字4', + 'color' => '#666666' + ] + ] + ], + 'blank' => [ + 'name' => '辅助空白', + 'type' => 'blank', + 'style' => [ + 'height' => '20', + 'background' => '#ffffff' + ] + ], + 'guide' => [ + 'name' => '辅助线', + 'type' => 'guide', + 'style' => [ + 'background' => '#ffffff', + 'lineStyle' => 'solid', + 'lineHeight' => '1', + 'lineColor' => "#000000", + 'paddingTop' => 10 + ] + ], + 'video' => [ + 'name' => '视频组', + 'type' => 'video', + 'params' => [ + 'videoUrl' => 'http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400', + 'poster' => self::$base_url . 'assets/store/img/diy/video_poster.png', + 'autoplay' => '0' + ], + 'style' => [ + 'paddingTop' => '0', + 'height' => '190' + ] + ], + 'article' => [ + 'name' => '文章组', + 'type' => 'article', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'category' => 0, + 'showNum' => 6 + ] + ], + 'style' => [], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'article_title' => '此处显示文章标题', + 'show_type' => 10, + 'image' => self::$base_url . 'assets/store/img/diy/article/01.png', + 'views_num' => '309' + ], + [ + 'article_title' => '此处显示文章标题', + 'show_type' => 10, + 'image' => self::$base_url . 'assets/store/img/diy/article/01.png', + 'views_num' => '309' + ] + ], + // '手动选择' => 默认数据 + 'data' => [] + ], + 'special' => [ + 'name' => '头条快报', + 'type' => 'special', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'category' => 0, + 'showNum' => 6 + ] + ], + 'style' => [ + 'display' => '1', + 'image' => self::$base_url . 'assets/store/img/diy/special.png' + ], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'article_title' => '张小龙4小时演讲:你和高手之间,隔着“简单”二字' + ], + [ + 'article_title' => '张小龙4小时演讲:你和高手之间,隔着“简单”二字' + ] + ], + // '手动选择' => 默认数据 + 'data' => [] + ], + 'notice' => [ + 'name' => '公告组', + 'type' => 'notice', + 'params' => [ + 'text' => '这里是第一条自定义公告的标题', + 'icon' => self::$base_url . 'assets/store/img/diy/notice.png' + ], + 'style' => [ + 'paddingTop' => '4', + 'background' => '#ffffff', + 'textColor' => '#000000' + ] + ], + 'richText' => [ + 'name' => '富文本', + 'type' => 'richText', + 'params' => [ + 'content' => '

这里是文本的内容

' + ], + 'style' => [ + 'paddingTop' => '0', + 'paddingLeft' => '0', + 'background' => '#ffffff' + ] + ], + 'window' => [ + 'name' => '图片橱窗', + 'type' => 'window', + 'style' => [ + 'paddingTop' => '0', + 'paddingLeft' => '0', + 'background' => '#ffffff', + 'layout' => '2' + ], + 'data' => [ + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/window/01.jpg', + 'linkUrl' => '' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/window/02.jpg', + 'linkUrl' => '' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/window/03.jpg', + 'linkUrl' => '' + ], + [ + 'imgUrl' => self::$base_url . 'assets/store/img/diy/window/04.jpg', + 'linkUrl' => '' + ] + ], + 'dataNum' => 4 + ], + 'goods' => [ + 'name' => '商品组', + 'type' => 'goods', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'category' => 0, + 'goodsSort' => 'all', // all; sales; price + 'showNum' => 6 + ] + ], + 'style' => [ + 'background' => '#F6F6F6', + 'display' => 'list', // list; slide + 'column' => '2', + 'show' => [ + 'goodsName' => '1', + 'goodsPrice' => '1', + 'linePrice' => '1', + 'sellingPoint' => '0', + 'goodsSales' => '0', + ] + ], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + ], + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + ], + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + ], + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + ] + ], + // '手动选择' => 默认数据 + 'data' => [ + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + 'is_default' => true + ], + [ + 'goods_name' => '此处显示商品名称', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'goods_price' => '99.00', + 'line_price' => '139.00', + 'selling_point' => '此款商品美观大方 不容错过', + 'goods_sales' => '100', + 'is_default' => true + ] + ] + ], + 'coupon' => [ + 'name' => '优惠券组', + 'type' => 'coupon', + 'style' => [ + 'paddingTop' => '10', + 'background' => '#ffffff' + ], + 'params' => [ + 'limit' => '5' + ], + 'data' => [ + [ + 'color' => 'red', + 'reduce_price' => '10', + 'min_price' => '100.00' + ], + [ + 'color' => 'violet', + 'reduce_price' => '10', + 'min_price' => '100.00' + ] + ] + ], + 'sharingGoods' => [ + 'name' => '拼团商品组', + 'type' => 'sharingGoods', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'category' => 0, + 'goodsSort' => 'all', // all; sales; price + 'showNum' => 6 + ] + ], + 'style' => [ + 'background' => '#F6F6F6', + 'show' => [ + 'goodsName' => '1', + 'sellingPoint' => '1', + 'sharingPrice' => '1', + 'linePrice' => '1' + ] + ], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'sharing_price' => '99.00', + 'line_price' => '139.00', + ], + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'goods_price' => '99.00', + 'line_price' => '139.00', + ], + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'sharing_price' => '99.00', + 'line_price' => '139.00', + ], + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'sharing_price' => '99.00', + 'line_price' => '139.00', + ] + ], + // '手动选择' => 默认数据 + 'data' => [ + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'sharing_price' => '99.00', + 'line_price' => '139.00', + 'is_default' => true + ], + [ + 'goods_name' => '此处是拼团商品', + 'image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'selling_point' => '此款商品美观大方 性价比较高 不容错过', + 'sharing_price' => '99.00', + 'line_price' => '139.00', + 'is_default' => true + ] + ] + ], + 'bargainGoods' => [ + 'name' => '砍价商品组', + 'type' => 'bargainGoods', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'category' => 0, + 'goodsSort' => 'all', // all; sales; price + 'showNum' => 6 + ] + ], + 'style' => [ + 'background' => '#F6F6F6', + 'show' => [ + 'goodsName' => '1', + 'peoples' => '1', + 'floorPrice' => '1', + 'originalPrice' => '1' + ] + ], + 'demo' => [ + 'helps_count' => 2, + 'helps' => [ + ['avatarUrl' => 'http://tva1.sinaimg.cn/large/0060lm7Tly1g4c7zrytvvj30dw0dwwes.jpg'], + ['avatarUrl' => 'http://tva1.sinaimg.cn/large/0060lm7Tly1g4c7zs2u5ej30b40b4dfx.jpg'], + ] + ], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'goods_name' => '此处是砍价商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'floor_price' => '0.01', + 'original_price' => '139.00', + ], + [ + 'goods_name' => '此处是砍价商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'floor_price' => '0.01', + 'original_price' => '139.00', + ], + ], + // '手动选择' => 默认数据 + 'data' => [ + [ + 'goods_name' => '此处是砍价商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'floor_price' => '0.01', + 'original_price' => '139.00', + ], + [ + 'goods_name' => '此处是砍价商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'floor_price' => '0.01', + 'original_price' => '139.00', + ], + ] + ], + 'sharpGoods' => [ + 'name' => '秒杀商品组', + 'type' => 'sharpGoods', + 'params' => [ + 'showNum' => 6 + ], + 'style' => [ + 'background' => '#ffffff', + 'column' => '3', + 'show' => [ + 'goodsName' => '1', + 'seckillPrice' => '1', + 'originalPrice' => '1' + ] + ], + // '手动选择' => 默认数据 + 'data' => [ + [ + 'goods_name' => '此处是秒杀商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'seckill_price' => '69.00', + 'original_price' => '139.00', + ], + [ + 'goods_name' => '此处是秒杀商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'seckill_price' => '69.00', + 'original_price' => '139.00', + ], + [ + 'goods_name' => '此处是秒杀商品', + 'goods_image' => self::$base_url . 'assets/store/img/diy/goods/01.png', + 'seckill_price' => '69.00', + 'original_price' => '139.00', + ], + ] + ], + 'shop' => [ + 'name' => '线下门店', + 'type' => 'shop', + 'params' => [ + 'source' => 'auto', // choice; auto + 'auto' => [ + 'showNum' => 6 + ] + ], + 'style' => [ + ], + // '自动获取' => 默认数据 + 'defaultData' => [ + [ + 'shop_name' => '此处显示门店名称', + 'logo_image' => self::$base_url . 'assets/store/img/diy/circular.png', + 'phone' => '010-6666666', + 'region' => [ + 'province' => 'xx省', + 'city' => 'xx市', + 'region' => 'xx区' + ], + 'address' => 'xx街道', + ], + [ + 'shop_name' => '此处显示门店名称', + 'logo_image' => self::$base_url . 'assets/store/img/diy/circular.png', + 'phone' => '010-6666666', + 'region' => [ + 'province' => 'xx省', + 'city' => 'xx市', + 'region' => 'xx区' + ], + 'address' => 'xx街道', + ], + ], + // '手动选择' => 默认数据 + 'data' => [ + [ + 'shop_name' => '此处显示门店名称', + 'logo_image' => self::$base_url . 'assets/store/img/diy/circular.png', + 'phone' => '010-6666666', + 'region' => [ + 'province' => 'xx省', + 'city' => 'xx市', + 'region' => 'xx区' + ], + 'address' => 'xx街道', + ], + ] + ], + 'officialAccount' => [ + 'name' => '关注公众号', + 'type' => 'officialAccount', + 'params' => [], + 'style' => [] + ], + 'service' => [ + 'name' => '在线客服', + 'type' => 'service', + 'params' => [ + 'type' => 'chat', // '客服类型' => chat在线聊天,phone拨打电话 + 'image' => self::$base_url . 'assets/store/img/diy/service.png', + 'phone_num' => '' + ], + 'style' => [ + 'right' => '1', + 'bottom' => '10', + 'opacity' => '100' + ] + ], + ]; + } + + /** + * 格式化页面数据 + * @param $json + * @return array + */ + public function getPageDataAttr($json) + { + // 旧版数据转义 + $array = $this->_transferToNewData($json); + // 合并默认数据 + return $this->_mergeDefaultData($array); + } + + /** + * 自动转换data为json格式 + * @param $value + * @return string + */ + public function setPageDataAttr($value) + { + return json_encode($value ?: ['items' => []]); + } + + /** + * diy页面详情 + * @param int $page_id + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($page_id) + { + return static::get(['page_id' => $page_id]); + } + + /** + * diy页面详情 + * @return static|null + * @throws \think\exception\DbException + */ + public static function getHomePage() + { + return self::get(['page_type' => 10]); + } + + /** + * 旧版数据转义为新版格式 + * @param $json + * @return array + */ + private function _transferToNewData($json) + { + $array = json_decode($json, true); + $items = $array['items']; + if (isset($items['page'])) { + unset($items['page']); + } + foreach ($items as &$item) { + isset($item['data']) && $item['data'] = array_values($item['data']); + } + return [ + 'page' => isset($array['page']) ? $array['page'] : $array['items']['page'], + 'items' => array_values(array_filter($items)) + ]; + } + + /** + * 合并默认数据 + * @param $array + * @return mixed + */ + private function _mergeDefaultData($array) + { + $array['page'] = array_merge_multiple($this->getDefaultPage(), $array['page']); + $defaultItems = $this->getDefaultItems(); + foreach ($array['items'] as &$item) { + if (isset($defaultItems[$item['type']])) { + array_key_exists('data', $item) && $defaultItems[$item['type']]['data'] = []; + $item = array_merge_multiple($defaultItems[$item['type']], $item); + } + } + return $array; + } + +} diff --git a/source/application/common/model/WxappPrepayId.php b/source/application/common/model/WxappPrepayId.php new file mode 100644 index 0000000..0ea3ad3 --- /dev/null +++ b/source/application/common/model/WxappPrepayId.php @@ -0,0 +1,57 @@ +where('order_id', '=', $orderId) + ->where('order_type', '=', $orderType) + ->order(['create_time' => 'desc']) + ->find(); + } + + /** + * 记录prepay_id使用次数 + * @return int|true + * @throws \think\Exception + */ + public function updateUsedTimes() + { + return $this->setInc('used_times', 1); + } + + /** + * 更新prepay_id已付款状态 + * @param $orderId + * @param $orderType + * @return false|int + */ + public static function updatePayStatus($orderId, $orderType = OrderTypeEnum::MASTER) + { + // 获取prepay_id记录 + $model = static::detail($orderId, $orderType); + if (empty($model)) { + return false; + } + // 更新记录 + return $model->save(['can_use_times' => 3, 'pay_status' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/common/model/admin/User.php b/source/application/common/model/admin/User.php new file mode 100644 index 0000000..d625d31 --- /dev/null +++ b/source/application/common/model/admin/User.php @@ -0,0 +1,16 @@ +order(['sort' => 'asc', 'create_time' => 'asc'])->select(); + $all = !empty($data) ? $data->toArray() : []; + Cache::tag('cache')->set('article_category_' . $model::$wxapp_id, $all); + } + return Cache::get('article_category_' . $model::$wxapp_id); + } + +} diff --git a/source/application/common/model/bargain/Active.php b/source/application/common/model/bargain/Active.php new file mode 100644 index 0000000..1a2aadf --- /dev/null +++ b/source/application/common/model/bargain/Active.php @@ -0,0 +1,98 @@ + 'integer', + 'is_floor_buy' => 'integer', + 'status' => 'integer', + ]; + + /** + * 追加的字段 + * @var array $append + */ + protected $append = [ + 'is_start', // 活动已开启 + 'is_end', // 活动已结束 + 'active_sales', // 活动销量 + ]; + + /** + * 获取器:活动开始时间 + * @param $value + * @return false|string + */ + public function getStartTimeAttr($value) + { + return \format_time($value); + } + + /** + * 获取器:活动结束时间 + * @param $value + * @return false|string + */ + public function getEndTimeAttr($value) + { + return \format_time($value); + } + + /** + * 获取器:活动是否已开启 + * @param $value + * @param $data + * @return false|string + */ + public function getIsStartAttr($value, $data) + { + return $value ?: $data['start_time'] <= time(); + } + + /** + * 获取器:活动是否已结束 + * @param $value + * @param $data + * @return false|string + */ + public function getIsEndAttr($value, $data) + { + return $value ?: $data['end_time'] <= time(); + } + + /** + * 获取器:显示销量 + * @param $value + * @param $data + * @return false|string + */ + public function getActiveSalesAttr($value, $data) + { + return $value ?: $data['actual_sales'] + $data['initial_sales']; + } + + /** + * 砍价活动详情 + * @param $activeId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($activeId, $with = []) + { + return static::get($activeId, $with); + } + +} \ No newline at end of file diff --git a/source/application/common/model/bargain/Setting.php b/source/application/common/model/bargain/Setting.php new file mode 100644 index 0000000..1b69c2e --- /dev/null +++ b/source/application/common/model/bargain/Setting.php @@ -0,0 +1,103 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set($cacheKey, $data); + } + return array_merge_multiple($self->defaultData(), $data); + } + + /** + * 获取设置项信息 + * @param $key + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($key) + { + return static::get(compact('key')); + } + + /** + * 默认配置 + * @return array + */ + public function defaultData() + { + return [ + 'basic' => [ + 'key' => 'basic', + 'describe' => '基础设置', + 'values' => [ + // 是否开启分销 + 'is_dealer' => '0', + // 砍价规则 + 'bargain_rules' => "活动期间,用户可以在砍价活动页选择活动商品发起砍价,可通过微信分享砍价商品活动页面给好友,并通过好友助力砍价,将商品砍至一定金额。\n\n" . + "每次砍价金额随机,可砍出最高商品日常价内的随机金额,参与好友越多越容易成功。\n\n" . + "以最终砍价后的优惠价格购买该商品,且用户须在活动时间结束之前进行支付购买,否则砍价商品价格将过期失效。\n\n" . + "商品库存有限,以前台展示的库存数量为准,先到先得。", + // 模板消息 + 'template_msg' => [] + ] + ] + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/model/bargain/Task.php b/source/application/common/model/bargain/Task.php new file mode 100644 index 0000000..eb87039 --- /dev/null +++ b/source/application/common/model/bargain/Task.php @@ -0,0 +1,140 @@ + 'integer', + 'is_buy' => 'integer', + 'status' => 'integer', + 'is_delete' => 'integer', + ]; + + /** + * 追加的字段 + * @var array $append + */ + protected $append = [ + 'is_end', // 是否已结束 + 'surplus_money', // 剩余砍价金额 + 'bargain_rate', // 砍价进度百分比(0-100) + ]; + + /** + * 订单模型初始化 + */ + public static function init() + { + parent::init(); + // 监听行为管理 + $static = new static; + Hook::listen('bargain_task', $static); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->BelongsTo("app\\{$module}\\model\\User"); + } + + /** + * 获取器:任务结束时间 + * @param $value + * @return false|string + */ + public function getEndTimeAttr($value) + { + return \format_time($value); + } + + /** + * 获取器:活动是否已结束 + * @param $value + * @param $data + * @return false|string + */ + public function getIsEndAttr($value, $data) + { + return $value ?: $data['end_time'] <= time(); + } + + /** + * 获取器:剩余砍价金额 + * @param $value + * @param $data + * @return false|string + */ + public function getSurplusMoneyAttr($value, $data) + { + $maxCutMoney = helper::bcsub($data['goods_price'], $data['floor_price']); + return $value ?: helper::bcsub($maxCutMoney, $data['cut_money']); + } + + /** + * 获取器:砍价进度百分比 + * @param $value + * @param $data + * @return false|string + */ + public function getBargainRateAttr($value, $data) + { + $maxCutMoney = helper::bcsub($data['goods_price'], $data['floor_price']); + $rate = helper::bcdiv($data['cut_money'], $maxCutMoney) * 100; + return $value ?: $rate; + } + + /** + * 获取器:砍价金额区间 + * @param $value + * @return mixed + */ + public function getSectionAttr($value) + { + return json_decode($value, true); + } + + /** + * 修改器:砍价金额区间 + * @param $value + * @return string + */ + public function setSectionAttr($value) + { + return json_encode($value); + } + + /** + * 砍价任务详情 + * @param $taskId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($taskId, $with = []) + { + $model = static::get($taskId, $with); + // 标识砍价任务过期 + if (!empty($model) && $model['status'] == 1 && $model->getData('end_time') <= time()) { + $model->save(['status' => 0]); + } + return $model; + } + +} \ No newline at end of file diff --git a/source/application/common/model/bargain/TaskHelp.php b/source/application/common/model/bargain/TaskHelp.php new file mode 100644 index 0000000..fc45d06 --- /dev/null +++ b/source/application/common/model/bargain/TaskHelp.php @@ -0,0 +1,28 @@ +BelongsTo("app\\{$module}\\model\\User") + ->field(['user_id', 'nickName', 'avatarUrl']); + } + +} \ No newline at end of file diff --git a/source/application/common/model/dealer/Apply.php b/source/application/common/model/dealer/Apply.php new file mode 100644 index 0000000..f8e90c0 --- /dev/null +++ b/source/application/common/model/dealer/Apply.php @@ -0,0 +1,95 @@ + '待审核', + 20 => '审核通过', + 30 => '驳回', + ]; + + /** + * 获取器:申请时间 + * @param $value + * @return false|string + */ + public function getApplyTimeAttr($value) + { + return date('Y-m-d H:i:s', $value); + } + + /** + * 获取器:审核时间 + * @param $value + * @return false|string + */ + public function getAuditTimeAttr($value) + { + return $value > 0 ? date('Y-m-d H:i:s', $value) : 0; + } + + /** + * 关联推荐人表 + * @return \think\model\relation\BelongsTo + */ + public function referee() + { + return $this->belongsTo('app\common\model\User', 'referee_id') + ->field(['user_id', 'nickName']); + } + + /** + * 销商申请记录详情 + * @param $where + * @return Apply|static + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return self::get($where); + } + + /** + * 购买指定商品成为分销商 + * @param $userId + * @param $goodsIds + * @param $wxappId + * @return bool + * @throws \think\exception\DbException + */ + public function becomeDealerUser($userId, $goodsIds, $wxappId) + { + // 验证是否设置 + $config = Setting::getItem('condition', $wxappId); + if ($config['become__buy_goods'] != '1' || empty($config['become__buy_goods_ids'])) { + return false; + } + // 判断商品是否在设置范围内 + $intersect = array_intersect($goodsIds, $config['become__buy_goods_ids']); + if (empty($intersect)) { + return false; + } + // 新增分销商用户 + User::add($userId, [ + 'referee_id' => Referee::getRefereeUserId($userId, 1), + 'wxapp_id' => $wxappId, + ]); + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/model/dealer/Capital.php b/source/application/common/model/dealer/Capital.php new file mode 100644 index 0000000..81c44c2 --- /dev/null +++ b/source/application/common/model/dealer/Capital.php @@ -0,0 +1,27 @@ +save(array_merge([ + 'wxapp_id' => $model::$wxapp_id + ], $data)); + } +} \ No newline at end of file diff --git a/source/application/common/model/dealer/Order.php b/source/application/common/model/dealer/Order.php new file mode 100644 index 0000000..96902ea --- /dev/null +++ b/source/application/common/model/dealer/Order.php @@ -0,0 +1,231 @@ +belongsTo('app\common\model\User'); + } + + /** + * 一级分销商用户 + * @return \think\model\relation\BelongsTo + */ + public function dealerFirst() + { + return $this->belongsTo('User', 'first_user_id'); + } + + /** + * 二级分销商用户 + * @return \think\model\relation\BelongsTo + */ + public function dealerSecond() + { + return $this->belongsTo('User', 'second_user_id'); + } + + /** + * 三级分销商用户 + * @return \think\model\relation\BelongsTo + */ + public function dealerThird() + { + return $this->belongsTo('User', 'third_user_id'); + } + + /** + * 订单类型 + * @param $value + * @return array + */ + public function getOrderTypeAttr($value) + { + $types = OrderTypeEnum::getTypeName(); + return ['text' => $types[$value], 'value' => $value]; + } + + /** + * 订单详情 + * @param $where + * @return Order|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where); + } + + /** + * 订单详情 + * @param $orderId + * @param $orderType + * @return Order|null + * @throws \think\exception\DbException + */ + public static function getDetailByOrderId($orderId, $orderType) + { + return static::detail(['order_id' => $orderId, 'order_type' => $orderType]); + } + + /** + * 发放分销订单佣金 + * @param array|\think\Model $order 订单详情 + * @param int $orderType 订单类型 + * @return bool|false|int + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public static function grantMoney($order, $orderType = OrderTypeEnum::MASTER) + { + // 订单是否已完成 + if ($order['order_status']['value'] != 30) { + return false; + } + // 佣金结算天数 + $settleDays = Setting::getItem('settlement', $order['wxapp_id'])['settle_days']; + // 判断该订单是否满足结算时间 (订单完成时间 + 佣金结算时间) ≤ 当前时间 + $deadlineTime = $order['receipt_time'] + ((int)$settleDays * 86400); + if ($settleDays > 0 && $deadlineTime > time()) { + return false; + } + // 分销订单详情 + $model = self::getDetailByOrderId($order['order_id'], $orderType); + if (!$model || $model['is_settled'] == 1) { + return false; + } + // 重新计算分销佣金 + $capital = $model->getCapitalByOrder($order); + // 发放一级分销商佣金 + $model['first_user_id'] > 0 && User::grantMoney($model['first_user_id'], $capital['first_money']); + // 发放二级分销商佣金 + $model['second_user_id'] > 0 && User::grantMoney($model['second_user_id'], $capital['second_money']); + // 发放三级分销商佣金 + $model['third_user_id'] > 0 && User::grantMoney($model['third_user_id'], $capital['third_money']); + // 更新分销订单记录 + return $model->save([ + 'order_price' => $capital['orderPrice'], + 'first_money' => $capital['first_money'], + 'second_money' => $capital['second_money'], + 'third_money' => $capital['third_money'], + 'is_settled' => 1, + 'settle_time' => time() + ]); + } + + /** + * 计算订单分销佣金 + * @param $order + * @return array + */ + protected function getCapitalByOrder($order) + { + // 分销佣金设置 + $setting = Setting::getItem('commission', $order['wxapp_id']); + // 分销层级 + $level = Setting::getItem('basic', $order['wxapp_id'])['level']; + // 分销订单佣金数据 + $capital = [ + // 订单总金额(不含运费) + 'orderPrice' => bcsub($order['pay_price'], $order['express_price'], 2), + // 一级分销佣金 + 'first_money' => 0.00, + // 二级分销佣金 + 'second_money' => 0.00, + // 三级分销佣金 + 'third_money' => 0.00 + ]; + // 计算分销佣金 + foreach ($order['goods'] as $goods) { + // 判断商品存在售后退款则不计算佣金 + if ($this->checkGoodsRefund($goods)) { + continue; + } + // 商品实付款金额 + $goodsPrice = min($capital['orderPrice'], $goods['total_pay_price']); + // 计算商品实际佣金 + $goodsCapital = $this->calculateGoodsCapital($setting, $goods, $goodsPrice); + // 累积分销佣金 + $level >= 1 && $capital['first_money'] += $goodsCapital['first_money']; + $level >= 2 && $capital['second_money'] += $goodsCapital['second_money']; + $level == 3 && $capital['third_money'] += $goodsCapital['third_money']; + } + return $capital; + } + + /** + * 计算商品实际佣金 + * @param $setting + * @param $goods + * @param $goodsPrice + * @return array + */ + private function calculateGoodsCapital($setting, $goods, $goodsPrice) + { + // 判断是否开启商品单独分销 + if ($goods['is_ind_dealer'] == false) { + // 全局分销比例 + return [ + 'first_money' => $goodsPrice * ($setting['first_money'] * 0.01), + 'second_money' => $goodsPrice * ($setting['second_money'] * 0.01), + 'third_money' => $goodsPrice * ($setting['third_money'] * 0.01) + ]; + } + // 商品单独分销 + if ($goods['dealer_money_type'] == 10) { + // 分销佣金类型:百分比 + return [ + 'first_money' => $goodsPrice * ($goods['first_money'] * 0.01), + 'second_money' => $goodsPrice * ($goods['second_money'] * 0.01), + 'third_money' => $goodsPrice * ($goods['third_money'] * 0.01) + ]; + } else { + return [ + 'first_money' => $goods['total_num'] * $goods['first_money'], + 'second_money' => $goods['total_num'] * $goods['second_money'], + 'third_money' => $goods['total_num'] * $goods['third_money'] + ]; + } + } + + /** + * 验证商品是否存在售后 + * @param $goods + * @return bool + */ + private function checkGoodsRefund($goods) + { + return !empty($goods['refund']) + && $goods['refund']['type']['value'] == 10 + && $goods['refund']['is_agree']['value'] != 20; + } + +} diff --git a/source/application/common/model/dealer/Referee.php b/source/application/common/model/dealer/Referee.php new file mode 100644 index 0000000..2d453b3 --- /dev/null +++ b/source/application/common/model/dealer/Referee.php @@ -0,0 +1,81 @@ +belongsTo('app\api\model\User'); + } + + /** + * 关联分销商用户表 + * @return \think\model\relation\BelongsTo + */ + public function dealer1() + { + return $this->belongsTo('User', 'dealer_id')->where('is_delete', '=', 0); + } + + /** + * 关联分销商用户表 + * @return \think\model\relation\BelongsTo + */ + public function dealer() + { + return $this->belongsTo('User')->where('is_delete', '=', 0); + } + + /** + * 获取上级用户id + * @param $user_id + * @param $level + * @param bool $is_dealer 必须是分销商 + * @return bool|mixed + * @throws \think\exception\DbException + */ + public static function getRefereeUserId($user_id, $level, $is_dealer = false) + { + $dealer_id = (new self)->where(compact('user_id', 'level')) + ->value('dealer_id'); + if (!$dealer_id) return 0; + return $is_dealer ? (User::isDealerUser($dealer_id) ? $dealer_id : 0) : $dealer_id; + } + + /** + * 获取我的团队列表 + * @param $user_id + * @param int $level + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id, $level = -1) + { + $level > -1 && $this->where('referee.level', '=', $level); + return $this->with(['dealer', 'user']) + ->alias('referee') + ->field('referee.*') + ->join('user', 'user.user_id = referee.user_id') + ->where('referee.dealer_id', '=', $user_id) + ->where('user.is_delete', '=', 0) + ->order(['referee.create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/common/model/dealer/Setting.php b/source/application/common/model/dealer/Setting.php new file mode 100644 index 0000000..40c0e40 --- /dev/null +++ b/source/application/common/model/dealer/Setting.php @@ -0,0 +1,389 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set('dealer_setting_' . $wxapp_id, $data); + } + return array_merge_multiple($self->defaultData(), $data); + } + + /** + * 获取设置项信息 + * @param $key + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($key) + { + return static::get(compact('key')); + } + + /** + * 是否开启分销功能 + * @param null $wxapp_id + * @return mixed + */ + public static function isOpen($wxapp_id = null) + { + return static::getItem('basic', $wxapp_id)['is_open']; + } + + /** + * 分销中心页面名称 + * @param null $wxapp_id + * @return mixed + */ + public static function getDealerTitle($wxapp_id = null) + { + return static::getItem('words', $wxapp_id)['index']['title']['value']; + } + + /** + * 默认配置 + * @return array + */ + public function defaultData() + { + return [ + 'basic' => [ + 'key' => 'basic', + 'describe' => '基础设置', + 'values' => [ + // 是否开启分销功能 + 'is_open' => '0', // 参数值:1开启 0关闭 + // 分销层级 + 'level' => '3', // 参数值:1一级 2二级 3三级 + // 分销商内购 + 'self_buy' => '0' // 参数值:1开启 0关闭 + ], + ], + 'condition' => [ + 'key' => 'condition', + 'describe' => '分销商条件', + 'values' => [ + // 成为分销商条件 + 'become' => '10', // 参数值:10填写申请信息(需后台审核) 20填写申请信息(无需审核) + // 购买指定商品成为分销商 0关闭 1开启 + 'become__buy_goods' => '0', + // 购买指定商品的id集 + 'become__buy_goods_ids' => [], + // 成为下线条件 + 'downline' => '10', // 参数值:10首次点击分享链接 20首次下单 30首次付款 + ] + ], + 'commission' => [ + 'key' => 'commission', + 'describe' => '佣金设置', + 'values' => [ + // 一级佣金 + 'first_money' => '0', + // 一级佣金 + 'second_money' => '0', + // 一级佣金 + 'third_money' => '0', + ] + ], + 'settlement' => [ + 'key' => 'settlement', + 'describe' => '结算', + 'values' => [ + // 提现方式 + 'pay_type' => [], // 参数值:10微信支付 20支付宝支付 30银行卡支付 + // 微信支付自动打款 + 'wechat_pay_auto' => '0', // 微信支付自动打款:1开启 0关闭 + // 最低提现额度 + 'min_money' => '10.00', + // 佣金结算天数 + 'settle_days' => '10', + ] + ], + 'words' => [ + 'key' => 'words', + 'describe' => '自定义文字', + 'values' => [ + 'index' => [ + 'title' => [ + 'default' => '分销中心', + 'value' => '分销中心' + ], + 'words' => [ + 'not_dealer' => [ + 'default' => '您还不是分销商,请先提交申请', + 'value' => '您还不是分销商,请先提交申请' + ], + 'apply_now' => [ + 'default' => '立即加入', + 'value' => '立即加入' + ], + 'referee' => [ + 'default' => '推荐人', + 'value' => '推荐人' + ], + 'money' => [ + 'default' => '可提现佣金', + 'value' => '可提现' + ], + 'freeze_money' => [ + 'default' => '待提现佣金', + 'value' => '待提现' + ], + 'total_money' => [ + 'default' => '已提现金额', + 'value' => '已提现金额' + ], + 'withdraw' => [ + 'default' => '去提现', + 'value' => '去提现' + ], + ] + ], + 'apply' => [ + 'title' => [ + 'default' => '申请成为分销商', + 'value' => '申请成为分销商' + ], + 'words' => [ + 'title' => [ + 'default' => '请填写申请信息', + 'value' => '请填写申请信息' + ], + 'license' => [ + 'default' => '分销商申请协议', + 'value' => '分销商申请协议' + ], + 'submit' => [ + 'default' => '申请成为经销商', + 'value' => '申请成为经销商' + ], + 'wait_audit' => [ + 'default' => '您的申请已受理,正在进行信息核验,请耐心等待。', + 'value' => '您的申请已受理,正在进行信息核验,请耐心等待。' + ], + 'goto_mall' => [ + 'default' => '去商城逛逛', + 'value' => '去商城逛逛' + ], + ] + ], + 'order' => [ + 'title' => [ + 'default' => '分销订单', + 'value' => '分销订单' + ], + 'words' => [ + 'all' => [ + 'default' => '全部', + 'value' => '全部' + ], + 'unsettled' => [ + 'default' => '未结算', + 'value' => '未结算' + ], + 'settled' => [ + 'default' => '已结算', + 'value' => '已结算' + ], + ] + ], + 'team' => [ + 'title' => [ + 'default' => '我的团队', + 'value' => '我的团队' + ], + 'words' => [ + 'total_team' => [ + 'default' => '团队总人数', + 'value' => '团队总人数' + ], + 'first' => [ + 'default' => '一级团队', + 'value' => '一级团队' + ], + 'second' => [ + 'default' => '二级团队', + 'value' => '二级团队' + ], + 'third' => [ + 'default' => '三级团队', + 'value' => '三级团队' + ], + ] + ], + 'withdraw_list' => [ + 'title' => [ + 'default' => '提现明细', + 'value' => '提现明细' + ], + 'words' => [ + 'all' => [ + 'default' => '全部', + 'value' => '全部' + ], + 'apply_10' => [ + 'default' => '审核中', + 'value' => '审核中' + ], + 'apply_20' => [ + 'default' => '审核通过', + 'value' => '审核通过' + ], + 'apply_40' => [ + 'default' => '已打款', + 'value' => '已打款' + ], + 'apply_30' => [ + 'default' => '驳回', + 'value' => '驳回' + ], + ] + ], + 'withdraw_apply' => [ + 'title' => [ + 'default' => '申请提现', + 'value' => '申请提现' + ], + 'words' => [ + 'capital' => [ + 'default' => '可提现佣金', + 'value' => '可提现佣金' + ], + 'money' => [ + 'default' => '提现金额', + 'value' => '提现金额' + ], + 'money_placeholder' => [ + 'default' => '请输入要提取的金额', + 'value' => '请输入要提取的金额' + ], + 'min_money' => [ + 'default' => '最低提现佣金', + 'value' => '最低提现佣金' + ], + 'submit' => [ + 'default' => '提交申请', + 'value' => '提交申请' + ], + ] + ], + 'qrcode' => [ + 'title' => [ + 'default' => '推广二维码', + 'value' => '推广二维码' + ] + ], + ] + ], + 'license' => [ + 'key' => 'license', + 'describe' => '申请协议', + 'values' => [ + 'license' => '' + ] + ], + 'background' => [ + 'key' => 'background', + 'describe' => '页面背景图', + 'values' => [ + // 分销中心首页 + 'index' => self::$base_url . 'assets/api/dealer-bg.png', + // 申请成为分销商页 + 'apply' => self::$base_url . 'assets/api/dealer-bg.png', + // 申请提现页 + 'withdraw_apply' => self::$base_url . 'assets/api/dealer-bg.png', + ], + ], + 'template_msg' => [ + 'key' => 'template_msg', + 'describe' => '模板消息', + 'values' => [ + 'apply_tpl' => '', // 分销商审核通知 + 'withdraw_tpl' => '', // 提现状态通知 + ] + ], + 'qrcode' => [ + 'key' => 'template_msg', + 'describe' => '分销海报', + 'values' => [ + 'backdrop' => [ + 'src' => self::$base_url . 'assets/store/img/dealer/backdrop.png', + ], + 'nickName' => [ + 'fontSize' => 14, + 'color' => '#000000', + 'left' => 150, + 'top' => 99 + ], + 'avatar' => [ + 'width' => 70, + 'style' => 'circle', + 'left' => 150, + 'top' => 18 + ], + 'qrcode' => [ + 'width' => 100, + 'style' => 'circle', + 'left' => 136, + 'top' => 128 + ] + ], + ] + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/model/dealer/User.php b/source/application/common/model/dealer/User.php new file mode 100644 index 0000000..e8a84d2 --- /dev/null +++ b/source/application/common/model/dealer/User.php @@ -0,0 +1,114 @@ + 'integer', + 'second_num' => 'integer', + 'third_num' => 'integer', + ]; + + /** + * 关联会员记录表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + return $this->belongsTo('app\common\model\User'); + } + + /** + * 关联推荐人表 + * @return \think\model\relation\BelongsTo + */ + public function referee() + { + return $this->belongsTo('app\common\model\User', 'referee_id') + ->field(['user_id', 'nickName']); + } + + /** + * 获取分销商用户信息 + * @param $userId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($userId, $with = ['user', 'referee']) + { + return self::get($userId, $with); + } + + /** + * 是否为分销商 + * @param $userId + * @return bool + */ + public static function isDealerUser($userId) + { + return !!(new static)->where('user_id', '=', $userId) + ->where('is_delete', '=', 0) + ->value('user_id'); + } + + /** + * 新增分销商用户记录 + * @param $user_id + * @param $data + * @return false|int + * @throws \think\exception\DbException + */ + public static function add($user_id, $data) + { + $model = static::detail($user_id) ?: new static; + return $model->save(array_merge([ + 'user_id' => $user_id, + 'is_delete' => 0, + 'wxapp_id' => $model::$wxapp_id + ], $data)); + } + + /** + * 发放分销商佣金 + * @param $user_id + * @param $money + * @return bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public static function grantMoney($user_id, $money) + { + // 分销商详情 + $model = static::detail($user_id); + if (!$model || $model['is_delete']) { + return false; + } + // 累积分销商可提现佣金 + $model->setInc('money', $money); + // 记录分销商资金明细 + Capital::add([ + 'user_id' => $user_id, + 'flow_type' => 10, + 'money' => $money, + 'describe' => '订单佣金结算', + 'wxapp_id' => $model['wxapp_id'], + ]); + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/model/dealer/Withdraw.php b/source/application/common/model/dealer/Withdraw.php new file mode 100644 index 0000000..9a80793 --- /dev/null +++ b/source/application/common/model/dealer/Withdraw.php @@ -0,0 +1,57 @@ + '微信', + 20 => '支付宝', + 30 => '银行卡', + ]; + + /** + * 申请状态 + * @var array + */ + public $applyStatus = [ + 10 => '待审核', + 20 => '审核通过', + 30 => '驳回', + 40 => '已打款', + ]; + + /** + * 关联分销商用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + + /** + * 提现详情 + * @param $id + * @return Apply|static + * @throws \think\exception\DbException + */ + public static function detail($id) + { + return self::get($id); + } + +} \ No newline at end of file diff --git a/source/application/common/model/recharge/Order.php b/source/application/common/model/recharge/Order.php new file mode 100644 index 0000000..f712342 --- /dev/null +++ b/source/application/common/model/recharge/Order.php @@ -0,0 +1,94 @@ + RechargeTypeEnum::data(), + // 支付状态 + 'pay_status' => PayStatusTypeEnum::data(), + ]; + } + + /** + * 关联会员记录表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + return $this->belongsTo('app\common\model\User'); + } + + /** + * 关联订单套餐快照表 + * @return \think\model\relation\HasOne + */ + public function orderPlan() + { + return $this->hasOne('OrderPlan', 'order_id'); + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getRechargeTypeAttr($value) + { + return ['text' => RechargeTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getPayStatusAttr($value) + { + return ['text' => PayStatusTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 付款时间 + * @param $value + * @return array + */ + public function getPayTimeAttr($value) + { + return [ + 'text' => $value > 0 ? date('Y-m-d H:i:s', $value) : '', + 'value' => $value + ]; + } + + /** + * 获取订单详情 + * @param $where + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where); + } + +} \ No newline at end of file diff --git a/source/application/common/model/recharge/OrderPlan.php b/source/application/common/model/recharge/OrderPlan.php new file mode 100644 index 0000000..114f464 --- /dev/null +++ b/source/application/common/model/recharge/OrderPlan.php @@ -0,0 +1,16 @@ + '未拼单', + 10 => '拼单中', + 20 => '拼单成功', + 30 => '拼单失败', + ]; + return ['text' => $state[$value], 'value' => $value]; + } + + /** + * 获取器:结束时间 + * @param $value + * @return array + */ + public function getEndTimeAttr($value) + { + return ['text' => date('Y-m-d H:i:s', $value), 'value' => $value]; + } + + /** + * 获取器:剩余拼团人数 + * @param $value + * @return array + */ + public function getSurplusPeopleAttr($value, $data) + { + return $data['people'] - $data['actual_people']; + } + + /** + * 关联拼团商品表 + * @return \think\model\relation\BelongsTo + */ + public function goods() + { + return $this->belongsTo('Goods'); + } + + /** + * 关联用户表(团长) + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User", 'creator_id'); + } + + /** + * 关联拼单成员表 + * @return \think\model\relation\HasMany + */ + public function users() + { + return $this->hasMany('ActiveUsers', 'active_id') + ->order(['is_creator' => 'desc', 'create_time' => 'asc']); + } + + /** + * 拼单详情 + * @param $active_id + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($active_id, $with = []) + { + return static::get($active_id, array_merge(['goods', 'users' => ['user', 'sharingOrder']], $with)); + } + + /** + * 验证当前拼单是否允许加入新成员 + * @return bool + */ + public function checkAllowJoin() + { + if (!in_array($this['status']['value'], [0, 10])) { + $this->error = '当前拼单已结束'; + return false; + } + if (time() > $this['end_time']) { + $this->error = '当前拼单已结束'; + return false; + } + if ($this['actual_people'] >= $this['people']) { + $this->error = '当前拼单人数已满'; + return false; + } + return true; + } + + /** + * 新增拼单记录 + * @param $creator_id + * @param $order_id + * @param OrderGoods $goods + * @return false|int + */ + public function onCreate($creator_id, $order_id, $goods) + { + // 新增拼单记录 + $this->save([ + 'goods_id' => $goods['goods_id'], + 'people' => $goods['people'], + 'actual_people' => 1, + 'creator_id' => $creator_id, + 'end_time' => time() + ($goods['group_time'] * 60 * 60), + 'status' => 10, + 'wxapp_id' => $goods['wxapp_id'] + ]); + // 新增拼单成员记录 + ActiveUsers::add([ + 'active_id' => $this['active_id'], + 'order_id' => $order_id, + 'user_id' => $creator_id, + 'is_creator' => 1, + 'wxapp_id' => $goods['wxapp_id'] + ]); + return true; + } + + /** + * 更新拼单记录 + * @param $user_id + * @param $order_id + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function onUpdate($user_id, $order_id) + { + // 验证当前拼单是否允许加入新成员 + if (!$this->checkAllowJoin()) { + return false; + } + // 新增拼单成员记录 + ActiveUsers::add([ + 'active_id' => $this['active_id'], + 'order_id' => $order_id, + 'user_id' => $user_id, + 'is_creator' => 0, + 'wxapp_id' => $this['wxapp_id'] + ]); + // 累计已拼人数 + $actual_people = $this['actual_people'] + 1; + // 更新拼单记录:当前已拼人数、拼单状态 + $status = $actual_people >= $this['people'] ? 20 : 10; + $this->save([ + 'actual_people' => $actual_people, + 'status' => $status + ]); + // 拼单成功, 发送模板消息 + if ($status == 20) { + $model = static::detail($this['active_id']); + (new MessageService)->sharingActive($model, '拼团成功'); + } + return true; + } + +} diff --git a/source/application/common/model/sharing/ActiveUsers.php b/source/application/common/model/sharing/ActiveUsers.php new file mode 100644 index 0000000..a463788 --- /dev/null +++ b/source/application/common/model/sharing/ActiveUsers.php @@ -0,0 +1,46 @@ +belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联拼团订单表 + * @return \think\model\relation\BelongsTo + */ + public function sharingOrder() + { + return $this->belongsTo('Order', 'order_id'); + } + + /** + * 新增拼团拼单成员记录 + * @param $data + * @return false|int + */ + public static function add($data) + { + return (new static)->save($data); + } + +} diff --git a/source/application/common/model/sharing/Category.php b/source/application/common/model/sharing/Category.php new file mode 100644 index 0000000..cd400f9 --- /dev/null +++ b/source/application/common/model/sharing/Category.php @@ -0,0 +1,98 @@ +order(['sort' => 'asc', 'create_time' => 'asc'])->select(); + $all = !empty($data) ? $data->toArray() : []; + $tree = []; + foreach ($all as $first) { + if ($first['parent_id'] != 0) continue; + $twoTree = []; + foreach ($all as $two) { + if ($two['parent_id'] != $first['category_id']) continue; + $threeTree = []; + foreach ($all as $three) + $three['parent_id'] == $two['category_id'] + && $threeTree[$three['category_id']] = $three; + !empty($threeTree) && $two['child'] = $threeTree; + $twoTree[$two['category_id']] = $two; + } + if (!empty($twoTree)) { + array_multisort(array_column($twoTree, 'sort'), SORT_ASC, $twoTree); + $first['child'] = $twoTree; + } + $tree[$first['category_id']] = $first; + } + Cache::tag('cache')->set('sharing_category_' . $model::$wxapp_id, compact('all', 'tree')); + } + return Cache::get('sharing_category_' . $model::$wxapp_id); + } + + /** + * 获取所有分类 + * @return mixed + */ + public static function getCacheAll() + { + return self::getALL()['all']; + } + + /** + * 获取所有分类(树状结构) + * @return mixed + */ + public static function getCacheTree() + { + return self::getALL()['tree']; + } + + /** + * 获取所有分类(树状结构) + * @return string + */ + public static function getCacheTreeJson() + { + return json_encode(static::getCacheTree()); + } + + /** + * 获取指定分类下的所有子分类id + * @param $parent_id + * @param array $all + * @return array + */ + public static function getSubCategoryId($parent_id, $all = []) + { + $arrIds = [$parent_id]; + empty($all) && $all = self::getCacheAll(); + foreach ($all as $key => $item) { + if ($item['parent_id'] == $parent_id) { + unset($all[$key]); + $subIds = self::getSubCategoryId($item['category_id'], $all); + !empty($subIds) && $arrIds = array_merge($arrIds, $subIds); + } + } + return $arrIds; + } + +} diff --git a/source/application/common/model/sharing/Comment.php b/source/application/common/model/sharing/Comment.php new file mode 100644 index 0000000..285a973 --- /dev/null +++ b/source/application/common/model/sharing/Comment.php @@ -0,0 +1,64 @@ +belongsTo('Order'); + } + + /** + * 订单商品 + * @return \think\model\relation\BelongsTo + */ + public function OrderGoods() + { + return $this->belongsTo('OrderGoods'); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联评价图片表 + * @return \think\model\relation\HasMany + */ + public function image() + { + return $this->hasMany('CommentImage', 'comment_id')->order(['id' => 'asc']); + } + + /** + * 评价详情 + * @param $comment_id + * @return Comment|null + * @throws \think\exception\DbException + */ + public static function detail($comment_id) + { + return self::get($comment_id, ['user', 'orderM', 'OrderGoods', 'image.file']); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharing/CommentImage.php b/source/application/common/model/sharing/CommentImage.php new file mode 100644 index 0000000..a728d83 --- /dev/null +++ b/source/application/common/model/sharing/CommentImage.php @@ -0,0 +1,28 @@ +belongsTo("app\\{$module}\\model\\UploadFile", 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/sharing/Goods.php b/source/application/common/model/sharing/Goods.php new file mode 100644 index 0000000..87b7cd9 --- /dev/null +++ b/source/application/common/model/sharing/Goods.php @@ -0,0 +1,391 @@ +belongsTo('Category'); + } + + /** + * 关联商品规格表 + * @return \think\model\relation\HasMany + */ + public function sku() + { + return $this->hasMany('GoodsSku', 'goods_id')->order(['goods_sku_id' => 'asc']); + } + + /** + * 关联商品规格关系表 + * @return \think\model\relation\BelongsToMany + */ + public function specRel() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsToMany( + "app\\{$module}\\model\\SpecValue", + 'SharingGoodsSpecRel', + 'spec_value_id', + 'goods_id' + )->order(['id' => 'asc']); + } + + /** + * 关联商品图片表 + * @return \think\model\relation\HasMany + */ + public function image() + { + $module = self::getCalledModule() ?: 'common'; + return $this->hasMany("app\\{$module}\\model\\sharing\\GoodsImage", 'goods_id') + ->order(['id' => 'asc']); + } + + /** + * 关联运费模板表 + * @return \think\model\relation\BelongsTo + */ + public function delivery() + { + $module = self::getCalledModule() ?: 'common'; + return $this->BelongsTo("app\\{$module}\\model\\Delivery"); + } + + /** + * 关联订单评价表 + * @return \think\model\relation\HasMany + */ + public function commentData() + { + return $this->hasMany('Comment', 'goods_id'); + } + + /** + * 计费方式 + * @param $value + * @return mixed + */ + public function getGoodsStatusAttr($value) + { + $status = [10 => '上架', 20 => '下架']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 获取商品列表 + * @param $param + * @return mixed + * @throws \think\exception\DbException + */ + public function getList($param) + { + // 商品列表获取条件 + $params = array_merge([ + 'status' => 10, // 商品状态 + 'category_id' => 0, // 分类id + 'search' => '', // 搜索关键词 + 'sortType' => 'all', // 排序类型 + 'sortPrice' => false, // 价格排序 高低 + 'listRows' => 15, // 每页数量 + ], $param); + // 筛选条件 + $filter = []; + $params['category_id'] > 0 && $filter['category_id'] = ['IN', Category::getSubCategoryId($params['category_id'])]; + $params['status'] > 0 && $filter['goods_status'] = $params['status']; + !empty($params['search']) && $filter['goods_name'] = ['like', '%' . trim($params['search']) . '%']; + // 排序规则 + $sort = []; + if ($params['sortType'] === 'all') { + $sort = ['goods_sort', 'goods_id' => 'desc']; + } elseif ($params['sortType'] === 'sales') { + $sort = ['goods_sales' => 'desc']; + } elseif ($params['sortType'] === 'price') { + $sort = $params['sortPrice'] ? ['goods_max_price' => 'desc'] : ['goods_min_price']; + } + // 商品表名称 + $tableName = $this->getTable(); + // 多规格商品 最高价与最低价 + $GoodsSku = new GoodsSku; + $minPriceSql = $GoodsSku->field(['MIN(sharing_price)']) + ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql(); + $maxPriceSql = $GoodsSku->field(['MAX(sharing_price)']) + ->where('goods_id', 'EXP', "= `$tableName`.`goods_id`")->buildSql(); + // 执行查询 + $list = $this + ->field(['*', '(sales_initial + sales_actual) as goods_sales', + "$minPriceSql AS goods_min_price", + "$maxPriceSql AS goods_max_price" + ]) + ->with(['category', 'image.file', 'sku']) + ->where('is_delete', '=', 0) + ->where($filter) + ->order($sort) + ->paginate($params['listRows'], false, [ + 'query' => \request()->request() + ]); + // 整理列表数据并返回 + return $this->setGoodsListData($list, true); + } + + /** + * 设置商品展示的数据 + * @param $data + * @param bool $isMultiple + * @param callable $callback + * @return mixed + */ + protected function setGoodsListData(&$data, $isMultiple = true, callable $callback = null) + { + if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data; + // 整理商品列表数据 + foreach ($dataSource as &$goods) { + // 商品默认规格 + $goodsSku = $goods['sku'][0]; + // 商品默认数据 + $goods['goods_image'] = $goods['image'][0]['file_path']; + $goods['goods_sku'] = $goodsSku; + // 回调函数 + is_callable($callback) && call_user_func($callback, $goods); + } + return $data; + } + + /** + * 根据商品id集获取商品列表 + * @param array $goodsIds + * @param null $status + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($goodsIds, $status = null) + { + // 筛选条件 + $filter = ['goods_id' => ['in', $goodsIds]]; + $status > 0 && $filter['goods_status'] = $status; + if (!empty($goodsIds)) { + $this->orderRaw('field(goods_id, ' . implode(',', $goodsIds) . ')'); + } + // 获取商品列表数据 + $data = $this->with(['category', 'image.file', 'sku', 'spec_rel.spec', 'delivery.rule']) + ->where($filter) + ->select(); + if ($data->isEmpty()) return $data; + // 格式化数据 + foreach ($data as &$item) { + $item['goods_image'] = $item['image'][0]['file_path']; + } + return $data; + } + + /** + * 商品多规格信息 + * @param \think\Collection $spec_rel + * @param \think\Collection $skuData + * @return array + */ + public function getManySpecData($spec_rel, $skuData) + { + // spec_attr + $specAttrData = []; + foreach ($spec_rel->toArray() as $item) { + if (!isset($specAttrData[$item['spec_id']])) { + $specAttrData[$item['spec_id']] = [ + 'group_id' => $item['spec']['spec_id'], + 'group_name' => $item['spec']['spec_name'], + 'spec_items' => [], + ]; + } + $specAttrData[$item['spec_id']]['spec_items'][] = [ + 'item_id' => $item['spec_value_id'], + 'spec_value' => $item['spec_value'], + ]; + } + // spec_list + $specListData = []; + foreach ($skuData->toArray() as $item) { + $image = (isset($item['image']) && !empty($item['image'])) ? $item['image'] : ['file_id' => 0, 'file_path' => '']; + $specListData[] = [ + 'goods_sku_id' => $item['goods_sku_id'], + 'spec_sku_id' => $item['spec_sku_id'], + 'rows' => [], + 'form' => [ + 'image_id' => $image['file_id'], + 'image_path' => $image['file_path'], + 'goods_no' => $item['goods_no'], + 'goods_price' => $item['goods_price'], + 'sharing_price' => $item['sharing_price'], + 'goods_weight' => $item['goods_weight'], + 'line_price' => $item['line_price'], + 'stock_num' => $item['stock_num'], + ], + ]; + } + return ['spec_attr' => array_values($specAttrData), 'spec_list' => $specListData]; + } + + /** + * 多规格表格数据 + * @param $goods + * @return array + */ + public function getManySpecTable(&$goods) + { + $specData = $this->getManySpecData($goods['spec_rel'], $goods['sku']); + $totalRow = count($specData['spec_list']); + foreach ($specData['spec_list'] as $i => &$sku) { + $rowData = []; + $rowCount = 1; + foreach ($specData['spec_attr'] as $attr) { + $skuValues = $attr['spec_items']; + $rowCount *= count($skuValues); + $anInterBankNum = ($totalRow / $rowCount); + $point = (($i / $anInterBankNum) % count($skuValues)); + if (0 === ($i % $anInterBankNum)) { + $rowData[] = [ + 'rowspan' => $anInterBankNum, + 'item_id' => $skuValues[$point]['item_id'], + 'spec_value' => $skuValues[$point]['spec_value'] + ]; + } + } + $sku['rows'] = $rowData; + } + return $specData; + } + + /** + * 获取商品详情 + * @param $goodsId + * @return static + */ + public static function detail($goodsId) + { + /* @var $model self */ + $model = (new static)->with([ + 'category', + 'image.file', + 'sku.image', + 'spec_rel.spec', + ])->where('goods_id', '=', $goodsId) + ->find(); + // 整理列表数据并返回 + return $model->setGoodsListData($model, false); + } + + /** + * 指定的商品规格信息 + * @param static $goods 商品详情 + * @param int $specSkuId + * @return array|bool + */ + public static function getGoodsSku($goods, $specSkuId) + { + // 获取指定的sku + $goodsSku = []; + foreach ($goods['sku'] as $item) { + if ($item['spec_sku_id'] == $specSkuId) { + $goodsSku = $item; + break; + } + } + if (empty($goodsSku)) { + return false; + } + // 多规格文字内容 + $goodsSku['goods_attr'] = ''; + if ($goods['spec_type'] == 20) { + $specRelData = helper::arrayColumn2Key($goods['spec_rel'], 'spec_value_id'); + $attrs = explode('_', $goodsSku['spec_sku_id']); + foreach ($attrs as $specValueId) { + $goodsSku['goods_attr'] .= $specRelData[$specValueId]['spec']['spec_name'] . ':' + . $specRelData[$specValueId]['spec_value'] . '; '; + } + } + return $goodsSku; + } + + /** + * 更新商品库存销量 + * @param $goodsList + * @throws \Exception + */ + public function updateStockSales($goodsList) + { + // 整理批量更新商品销量 + $goodsSave = []; + // 批量更新商品规格:sku销量、库存 + $goodsSpecSave = []; + foreach ($goodsList as $goods) { + $goodsSave[] = [ + 'goods_id' => $goods['goods_id'], + 'sales_actual' => ['inc', $goods['total_num']] + ]; + // 付款减库存 + if ($goods['deduct_stock_type'] == 20) { + $goodsSpecSave[] = [ + 'data' => ['stock_num' => ['dec', $goods['total_num']]], + 'where' => [ + 'goods_id' => $goods['goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + ]; + } + } + // 更新商品总销量 + $this->allowField(true)->isUpdate()->saveAll($goodsSave); + // 更新商品规格库存 + !empty($goodsSpecSave) && (new GoodsSku)->updateAll($goodsSpecSave); + } + +} diff --git a/source/application/common/model/sharing/GoodsImage.php b/source/application/common/model/sharing/GoodsImage.php new file mode 100644 index 0000000..0a66546 --- /dev/null +++ b/source/application/common/model/sharing/GoodsImage.php @@ -0,0 +1,28 @@ +belongsTo("app\\{$module}\\model\\UploadFile", 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/sharing/GoodsSku.php b/source/application/common/model/sharing/GoodsSku.php new file mode 100644 index 0000000..7166b03 --- /dev/null +++ b/source/application/common/model/sharing/GoodsSku.php @@ -0,0 +1,51 @@ +hasOne("app\\{$module}\\model\\UploadFile", 'file_id', 'image_id'); + } + + /** + * 获取器:拼团价与划线价差额 + * @param $value + * @param $data + * @return mixed + */ + public function getDiffPriceAttr($value, $data) + { + return max(0, helper::bcsub($data['line_price'], $data['sharing_price'])); + } + + /** + * 获取sku信息详情 + * @param $goodsId + * @param $specSkuId + * @return GoodsSku|null + * @throws \think\exception\DbException + */ + public static function detail($goodsId, $specSkuId) + { + return static::get(['goods_id' => $goodsId, 'spec_sku_id' => $specSkuId]); + } + +} diff --git a/source/application/common/model/sharing/GoodsSpecRel.php b/source/application/common/model/sharing/GoodsSpecRel.php new file mode 100644 index 0000000..fca6bb4 --- /dev/null +++ b/source/application/common/model/sharing/GoodsSpecRel.php @@ -0,0 +1,26 @@ +belongsTo('Spec'); + } + +} diff --git a/source/application/common/model/sharing/Order.php b/source/application/common/model/sharing/Order.php new file mode 100644 index 0000000..092967c --- /dev/null +++ b/source/application/common/model/sharing/Order.php @@ -0,0 +1,433 @@ +belongsTo('Active'); + } + + /** + * 订单商品列表 + * @return \think\model\relation\HasMany + */ + public function goods() + { + return $this->hasMany('OrderGoods', 'order_id'); + } + + /** + * 关联订单收货地址表 + * @return \think\model\relation\HasOne + */ + public function address() + { + return $this->hasOne('OrderAddress', 'order_id'); + } + + /** + * 关联订单收货地址表 + * @return \think\model\relation\HasOne + */ + public function extract() + { + return $this->hasOne('OrderExtract', 'order_id'); + } + + /** + * 关联自提门店表 + * @return \think\model\relation\BelongsTo + */ + public function extractShop() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\store\\Shop", 'extract_shop_id'); + } + + /** + * 关联门店店员表 + * @return \think\model\relation\BelongsTo + */ + public function extractClerk() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\store\\shop\\Clerk", 'extract_clerk_id'); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联物流公司表 + * @return \think\model\relation\BelongsTo + */ + public function express() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\Express"); + } + + /** + * 拼团订单状态文字描述 + * @param $value + * @param $data + * @return string + */ + public function getStateTextAttr($value, $data) + { + if (!isset($data['active_status'])) { + $data['active_status'] = ''; + } + // 订单状态:已完成 + if ($data['order_status'] == 30) { + return '已完成'; + } + // 订单状态:已取消 + if ($data['order_status'] == 20) { + // 拼单未成功 + if ($data['order_type'] == 20 && $data['active_status'] == 30) { + return $data['is_refund'] ? '拼团未成功,已退款' : '拼团未成功,待退款'; + } + return '已取消'; + } + // 付款状态 + if ($data['pay_status'] == 10) { + return '待付款'; + } + // 订单类型:单独购买 + if ($data['order_type'] == 10) { + if ($data['delivery_status'] == 10) { + return '已付款,待发货'; + } + if ($data['receipt_status'] == 10) { + return '已发货,待收货'; + } + } + // 订单类型:拼团 + if ($data['order_type'] == 20) { + // 拼单未成功 + if ($data['active_status'] == 30) { + return $data['is_refund'] ? '拼团未成功,已退款' : '拼团未成功,待退款'; + } + // 拼单中 + if ($data['active_status'] == 10) { + return '已付款,待成团'; + } + // 拼单成功 + if ($data['active_status'] == 20) { + if ($data['delivery_status'] == 10) { + return '拼团成功,待发货'; + } + if ($data['receipt_status'] == 10) { + return '已发货,待收货'; + } + } + } + return $value; + } + + /** + * 获取器:拼单状态 + * @param $value + * @return array|bool + */ + public function getActiveStatusAttr($value) + { + if (is_null($value)) { + return false; + } + $state = [ + 0 => '未拼单', + 10 => '拼单中', + 20 => '拼单成功', + 30 => '拼单失败', + ]; + return ['text' => $state[$value], 'value' => $value]; + } + + /** + * 获取器:订单金额(含优惠折扣) + * @param $value + * @param $data + * @return string + */ + public function getOrderPriceAttr($value, $data) + { + // 兼容旧数据:订单金额 + if ($value == 0) { + return helper::bcadd(helper::bcsub($data['total_price'], $data['coupon_money']), $data['update_price']); + } + return $value; + } + + /** + * 改价金额(差价) + * @param $value + * @return array + */ + public function getUpdatePriceAttr($value) + { + return [ + 'symbol' => $value < 0 ? '-' : '+', + 'value' => sprintf('%.2f', abs($value)) + ]; + } + + /** + * 订单类型 + * @param $value + * @return array + */ + public function getOrderTypeAttr($value) + { + $status = [10 => '单独购买', 20 => '拼团']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getPayTypeAttr($value) + { + return ['text' => PayTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 付款状态 + * @param $value + * @return array + */ + public function getPayStatusAttr($value) + { + return ['text' => PayStatusEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 发货状态 + * @param $value + * @return array + */ + public function getDeliveryStatusAttr($value) + { + $status = [10 => '待发货', 20 => '已发货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 收货状态 + * @param $value + * @return array + */ + public function getReceiptStatusAttr($value) + { + $status = [10 => '待收货', 20 => '已收货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 收货状态 + * @param $value + * @return array + */ + public function getOrderStatusAttr($value) + { + $status = [10 => '进行中', 20 => '已取消', 21 => '待取消', 30 => '已完成', 40 => '拼团失败']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 配送方式 + * @param $value + * @return array + */ + public function getDeliveryTypeAttr($value) + { + return ['text' => DeliveryTypeEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 生成订单号 + * @return string + */ + public function orderNo() + { + return OrderService::createOrderNo(); + } + + /** + * 订单详情 + * @param int|array $where + * @param array $with + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($where, $with = [ + 'active', + 'goods' => ['image'], + 'address', + 'extract', + 'express', + 'extract_shop.logo', + 'extract_clerk' + ]) + { + is_array($where) ? $filter = $where : $filter['order_id'] = (int)$where; + return self::get($filter, $with); + } + + /** + * 批量获取订单列表 + * @param $orderIds + * @param array $with 关联查询 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($orderIds, $with = []) + { + $data = $this->getListByInArray('order_id', $orderIds, $with); + return helper::arrayColumn2Key($data, 'order_id'); + } + + /** + * 批量获取订单列表 + * @param string $field + * @param array $data + * @param array $with + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getListByInArray($field, $data, $with = []) + { + return $this->with($with) + ->alias('order') + ->field('order.*, active.status as active_status') + ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT') + ->where($field, 'in', $data) + ->where('order.is_delete', '=', 0) + ->select(); + } + + /** + * 根据订单号批量查询 + * @param $orderNos + * @param array $with + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByOrderNos($orderNos, $with = []) + { + return $this->getListByInArray('order_no', $orderNos, $with); + } + + /** + * 批量更新订单 + * @param $orderIds + * @param $data + * @return false|int + */ + public function onBatchUpdate($orderIds, $data) + { + return $this->isUpdate(true)->save($data, ['order_id' => ['in', $orderIds]]); + } + + /** + * 确认核销(自提订单) + * @param int $clerkId 核销员id + * @return bool|false|int + */ + public function verificationOrder($clerkId) + { + if ( + $this['pay_status']['value'] != 20 + || $this['delivery_type']['value'] != DeliveryTypeEnum::EXTRACT + || $this['delivery_status']['value'] == 20 + || in_array($this['order_status']['value'], [20, 21]) + // 拼团订单验证拼单状态 + || ($this['order_type']['value'] == 20 ? $this['active']['status']['value'] != 20 : false) + ) { + $this->error = '该订单不满足核销条件'; + return false; + } + $this->transaction(function () use ($clerkId) { + // 更新订单状态:已发货、已收货 + $this->save([ + 'extract_clerk_id' => $clerkId, // 核销员 + 'delivery_status' => 20, + 'delivery_time' => time(), + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); + // 新增订单核销记录 + ShopOrder::add( + $this['order_id'], + $this['extract_shop_id'], + $this['extract_clerk_id'], + OrderTypeEnum::SHARING + ); + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::SHARING); + $OrderCompleteService->complete([$this], static::$wxapp_id); + }); + return true; + } + +} diff --git a/source/application/common/model/sharing/OrderAddress.php b/source/application/common/model/sharing/OrderAddress.php new file mode 100644 index 0000000..7dda862 --- /dev/null +++ b/source/application/common/model/sharing/OrderAddress.php @@ -0,0 +1,48 @@ + RegionModel::getNameById($data['province_id']), + 'city' => RegionModel::getNameById($data['city_id']), + 'region' => $data['region_id'] == 0 ? '' : RegionModel::getNameById($data['region_id']), + ]; + } + + /** + * 获取完整地址 + * @return string + */ + public function getFullAddress() + { + return $this['region']['province'] . $this['region']['city'] . $this['region']['region'] . $this['detail']; + } + +} diff --git a/source/application/common/model/sharing/OrderExtract.php b/source/application/common/model/sharing/OrderExtract.php new file mode 100644 index 0000000..d591f7f --- /dev/null +++ b/source/application/common/model/sharing/OrderExtract.php @@ -0,0 +1,17 @@ +belongsTo('Goods'); + } + + /** + * 订单拼团商品图 + * @return \think\model\relation\BelongsTo + */ + public function image() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\UploadFile", 'image_id', 'file_id'); + } + + /** + * 关联拼团商品sku表 + * @return \think\model\relation\BelongsTo + */ + public function sku() + { + return $this->belongsTo('GoodsSku', 'spec_sku_id', 'spec_sku_id'); + } + + /** + * 关联拼团订单主表 + * @return \think\model\relation\BelongsTo + */ + public function orderM() + { + return $this->belongsTo('Order'); + } + + /** + * 关联拼团售后单记录表 + * @return \think\model\relation\HasOne + */ + public function refund() + { + return $this->hasOne('OrderRefund', 'order_goods_id'); + } + + /** + * 拼团订单商品详情 + * @param $where + * @return OrderGoods|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where, ['image', 'refund']); + } + + /** + * 回退商品库存 + * @param $goodsList + * @param $isPayOrder + * @return array|false + * @throws \Exception + */ + public function backGoodsStock($goodsList, $isPayOrder = false) + { + $goodsSkuData = []; + foreach ($goodsList as $goods) { + $item = [ + 'where' => [ + 'goods_id' => $goods['goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + 'data' => ['stock_num' => ['inc', $goods['total_num']]], + ]; + if ($isPayOrder == true) { + // 付款订单全部库存 + $goodsSkuData[] = $item; + } else { + // 未付款订单,判断必须为下单减库存时才回退 + $goods['deduct_stock_type'] == DeductStockTypeEnum::CREATE && $goodsSkuData[] = $item; + } + } + // 更新商品sku库存 + return !empty($goodsSkuData) && (new GoodsSkuModel)->updateAll($goodsSkuData); + } + +} diff --git a/source/application/common/model/sharing/OrderRefund.php b/source/application/common/model/sharing/OrderRefund.php new file mode 100644 index 0000000..97eb77a --- /dev/null +++ b/source/application/common/model/sharing/OrderRefund.php @@ -0,0 +1,117 @@ +belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联订单主表 + * @return \think\model\relation\BelongsTo + */ + public function orderMaster() + { + return $this->belongsTo('Order'); + } + + /** + * 关联订单商品表 + * @return \think\model\relation\BelongsTo + */ + public function orderGoods() + { + return $this->belongsTo('OrderGoods'); + } + + /** + * 关联图片记录表 + * @return \think\model\relation\HasMany + */ + public function image() + { + return $this->hasMany('OrderRefundImage', 'order_refund_id'); + } + + /** + * 关联物流公司表 + * @return \think\model\relation\BelongsTo + */ + public function express() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\Express"); + } + + /** + * 关联用户表 + * @return \think\model\relation\HasOne + */ + public function address() + { + return $this->hasOne('OrderRefundAddress', 'order_refund_id'); + } + + /** + * 售后类型 + * @param $value + * @return array + */ + public function getTypeAttr($value) + { + $status = [10 => '退货退款', 20 => '换货']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 商家是否同意售后 + * @param $value + * @return array + */ + public function getIsAgreeAttr($value) + { + $status = [0 => '待审核', 10 => '已同意', 20 => '已拒绝']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 售后单状态 + * @param $value + * @return array + */ + public function getStatusAttr($value) + { + $status = [0 => '进行中', 10 => '已拒绝', 20 => '已完成', 30 => '已取消']; + return ['text' => $status[$value], 'value' => $value]; + } + + /** + * 售后单详情 + * @param $where + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + return static::get($where, ['image.file', 'order_goods.image', 'express', 'address']); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharing/OrderRefundAddress.php b/source/application/common/model/sharing/OrderRefundAddress.php new file mode 100644 index 0000000..aa4f191 --- /dev/null +++ b/source/application/common/model/sharing/OrderRefundAddress.php @@ -0,0 +1,17 @@ +belongsTo("app\\{$module}\\model\\UploadFile", 'image_id', 'file_id') + ->bind(['file_path', 'file_name', 'file_url']); + } + +} diff --git a/source/application/common/model/sharing/Setting.php b/source/application/common/model/sharing/Setting.php new file mode 100644 index 0000000..be1ddc4 --- /dev/null +++ b/source/application/common/model/sharing/Setting.php @@ -0,0 +1,105 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set('sharing_setting_' . $wxapp_id, $data); + } + return array_merge_multiple($self->defaultData(), $data); + } + + /** + * 获取设置项信息 + * @param $key + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($key) + { + return static::get(compact('key')); + } + + /** + * 默认配置 + * @return array + */ + public function defaultData() + { + return [ + 'basic' => [ + 'key' => 'basic', + 'describe' => '基础设置', + 'values' => [ + // 拼团失败自动退款 + 'auto_refund' => '1', + // 是否允许使用优惠券 + 'is_coupon' => '1', + // 是否开启分销 + 'is_dealer' => '0', + // 拼团规则 简述 + 'rule_brief' => '好友拼单 · 人满发货 · 人不满退款', + // 拼团规则 详述 + 'rule_detail' => "开团:选择商品,点击“发起拼单”按钮,付款完成后即开团成功,就可以邀请小伙伴一起拼团啦;\n\n参团:进入朋友分享的页面,点击“立即参团”按钮,付款完成后参团成功,在有效时间内凑齐人数即成团,就可以等待收货喽;\n\n成团:在开团或参团成功后,点击“立即分享”将页面分享给好友,在有效时间内凑齐人数即成团,成团后商家开始发货;\n\n组团失败:在有效时间内未凑齐人数,即组团失败,组团失败后订单所付款将原路退回到支付账户。", + // 拼单状态模板消息id + 'tpl_msg_id' => '', + ] + ] + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/Active.php b/source/application/common/model/sharp/Active.php new file mode 100644 index 0000000..18be773 --- /dev/null +++ b/source/application/common/model/sharp/Active.php @@ -0,0 +1,38 @@ +hasMany('ActiveTime', 'active_id') + ->order(['active_time' => 'asc']); + } + + /** + * 活动会场详情 + * @param $activeId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($activeId, $with = []) + { + return static::get($activeId, $with); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/ActiveGoods.php b/source/application/common/model/sharp/ActiveGoods.php new file mode 100644 index 0000000..b699af1 --- /dev/null +++ b/source/application/common/model/sharp/ActiveGoods.php @@ -0,0 +1,111 @@ +belongsTo('Active', 'active_id'); + } + + /** + * 关联活动会场场次表 + * @return \think\model\relation\BelongsTo + */ + public function activeTime() + { + return $this->belongsTo('ActiveTime', 'active_time_id'); + } + + /** + * 根据活动场次ID获取商品列表 + * @param int $activeTimeId + * @param array $goodsParam + * @return false|\PDOStatement|string|\think\Collection + */ + public static function getGoodsListByActiveTimeId($activeTimeId, $goodsParam = []) + { + // 商品关联列表 + $model = new static; + $activeGoodsList = $model->getSharpGoodsByActiveTimeId($activeTimeId); + // 将列表的索引值设置为商品ID + $activeGoodsList = helper::arrayColumn2Key($activeGoodsList, 'sharp_goods_id'); + // 获取商品列表 + $sharpGoodsList = $model->getGoodsListByIds(array_keys($activeGoodsList), $goodsParam); + // 整理活动商品信息 + foreach ($sharpGoodsList as &$item) { + // 活动商品的销量 + $item['sales_actual'] = $activeGoodsList[$item['sharp_goods_id']]['sales_actual']; + // 商品销售进度 + $item['progress'] = $model->getProgress($item['sales_actual'], $item['seckill_stock']); + } + /* @var $sharpGoodsList \think\model\Collection */ + return $sharpGoodsList->hidden(['sku', 'goods']); + } + + /** + * 计算商品销售进度 + * @param $value1 + * @param $value2 + * @return mixed + */ + protected function getProgress($value1, $value2) + { + if ($value2 <= 0) return 100; + $progress = helper::bcdiv($value1, $value2); + return min(100, (int)helper::bcmul($progress, 100, 0)); + } + + /** + * 获取秒杀商品模型 + * @return Goods + */ + protected function getGoodsModel() + { + return new GoodsModel; + } + + /** + * 根据活动场次ID获取商品集 + * @param $activeTimeId + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getSharpGoodsByActiveTimeId($activeTimeId) + { + return $this->where('active_time_id', '=', $activeTimeId)->select(); + } + + /** + * 根据商品ID集获取商品列表 + * @param array $sharpGoodsIds + * @param array $param + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getGoodsListByIds($sharpGoodsIds, $param = []) + { + return $this->getGoodsModel()->getListByIds($sharpGoodsIds, $param); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/ActiveTime.php b/source/application/common/model/sharp/ActiveTime.php new file mode 100644 index 0000000..f954359 --- /dev/null +++ b/source/application/common/model/sharp/ActiveTime.php @@ -0,0 +1,56 @@ +belongsTo('Active', 'active_id'); + } + + /** + * 当前场次下秒杀商品的数量 + * @return \think\model\relation\HasMany + */ + public function goods() + { + return $this->hasMany('ActiveGoods', 'active_time_id'); + } + + /** + * 获取器:活动场次时间 + * @param $value + * @return string + */ + public function getActiveTimeAttr($value) + { + return \pad_left($value) . ':00'; + } + + /** + * 活动会场场次详情 + * @param $activeTimeId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($activeTimeId, $with = []) + { + return static::get($activeTimeId, $with); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/Goods.php b/source/application/common/model/sharp/Goods.php new file mode 100644 index 0000000..49f710b --- /dev/null +++ b/source/application/common/model/sharp/Goods.php @@ -0,0 +1,153 @@ +belongsTo("app\\{$module}\\model\\Goods"); + } + + /** + * 关联商品规格表 + * @return \think\model\relation\HasMany + */ + public function sku() + { + return $this->hasMany('GoodsSku', 'sharp_goods_id') + ->order(['seckill_price' => 'asc', 'goods_sku_id' => 'asc']); + } + + /** + * 根据商品id集获取商品列表 + * @param array $goodsIds + * @param array $param + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($goodsIds, $param = []) + { + // 默认条件 + $param = array_merge([ + 'status' => null, + 'limit' => 15, + ], $param); + // 筛选条件 + !is_null($param['status']) && $this->where('status', '=', (int)$param['status']); + // 获取商品列表数据 + $list = $this->with(['sku']) + ->where('sharp_goods_id', 'in', $goodsIds) + ->where('is_delete', '=', 0) + ->order(['sort' => 'asc']) + ->paginate($param['limit'], false, [ + 'query' => \request()->request() + ]); + // 设置商品数据 + return $this->setGoodsListData($list, true); + } + + /** + * 秒杀商品详情 + * @param $sharpGoodsId + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($sharpGoodsId, $with = []) + { + // 整理商品数据并返回 + $model = static::get($sharpGoodsId, $with); + return $model->setGoodsListData($model, false); + } + + /** + * 商品多规格信息 + * @param $goods + * @param $sharpGoodsSku + * @return array|null + */ + public function getSpecData($goods, $sharpGoodsSku) + { + $specData = GoodsService::getSpecData($goods); + if ($goods['spec_type'] == 10) { + return $specData; + } + $skuData = helper::arrayColumn2Key($sharpGoodsSku, 'spec_sku_id'); + foreach ($specData['spec_list'] as &$item) { + if (isset($skuData[$item['spec_sku_id']])) { + $item['form']['seckill_price'] = $skuData[$item['spec_sku_id']]['seckill_price']; + $item['form']['original_price'] = $item['form']['goods_price']; + $item['form']['seckill_stock'] = $skuData[$item['spec_sku_id']]['seckill_stock']; + } + } + return $specData; + } + + /** + * 设置商品展示的数据 + * @param $data + * @param bool $isMultiple + * @param callable|null $callback + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + protected function setGoodsListData($data, $isMultiple = true, callable $callback = null) + { + // 设置原商品数据 + $data = GoodsService::setGoodsData($data, $isMultiple); + if (!$isMultiple) $dataSource = [&$data]; else $dataSource = &$data; + // 整理商品数据 + foreach ($dataSource as &$item) { + // 商品名称 + $item['goods_name'] = $item['goods']['goods_name']; + // 商品图片 + $item['goods_image'] = $item['goods']['goods_image']; + // 秒杀商品sku信息 + $item['goods_sku'] = $this->getDefaultSharpSku($item['sku'], $item['goods']['sku']); + // 回调函数 + is_callable($callback) && call_user_func($callback, $item); + } + return $data; + } + + /** + * 整理秒杀商品的默认sku信息 (用于商品列表) + * @param $sharpSku + * @param $goodsSku + * @return mixed + */ + private function getDefaultSharpSku($sharpSku, $goodsSku) + { + $sharpGoodsSku = $sharpSku[0]; + $goodsSkuItem = helper::getArrayItemByColumn($goodsSku, 'spec_sku_id', $sharpGoodsSku['spec_sku_id']); + $sharpGoodsSku['original_price'] = $goodsSkuItem['goods_price']; + $sharpGoodsSku['image_id'] = $goodsSkuItem['image_id']; + $sharpGoodsSku['goods_no'] = $goodsSkuItem['goods_no']; + $sharpGoodsSku['line_price'] = $goodsSkuItem['line_price']; + $sharpGoodsSku['goods_weight'] = $goodsSkuItem['goods_weight']; + return $sharpGoodsSku; + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/GoodsSku.php b/source/application/common/model/sharp/GoodsSku.php new file mode 100644 index 0000000..5923f7a --- /dev/null +++ b/source/application/common/model/sharp/GoodsSku.php @@ -0,0 +1,26 @@ +belongsTo("app\\{$module}\\model\\sharp\\Goods"); + } + +} \ No newline at end of file diff --git a/source/application/common/model/sharp/Setting.php b/source/application/common/model/sharp/Setting.php new file mode 100644 index 0000000..fb5f8eb --- /dev/null +++ b/source/application/common/model/sharp/Setting.php @@ -0,0 +1,100 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set($cacheKey, $data); + } + return array_merge_multiple($self->defaultData(), $data); + } + + /** + * 获取设置项信息 + * @param $key + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($key) + { + return static::get(compact('key')); + } + + /** + * 默认配置 + * @return array + */ + public function defaultData() + { + return [ + 'basic' => [ + 'key' => 'basic', + 'describe' => '基础设置', + 'values' => [ + // 是否开启分销 + 'is_dealer' => '0', + 'order' => [ + // 秒杀订单未支付n分钟后自动关闭 + 'order_close' => '10', + ] + ] + ] + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/model/store/Access.php b/source/application/common/model/store/Access.php new file mode 100644 index 0000000..a548fa3 --- /dev/null +++ b/source/application/common/model/store/Access.php @@ -0,0 +1,61 @@ +order(['sort' => 'asc', 'create_time' => 'asc'])->select(); + return $data ? $data->toArray() : []; + } + + /** + * 权限信息 + * @param int|array $where + * @return array|false|\PDOStatement|string|\think\Model|static + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function detail($where) + { + $model = static::useGlobalScope(false); + is_array($where) ? $model->where($where) : $model->where('access_id', '=', $where); + return $model->find(); + } + + /** + * 获取权限url集 + * @param $accessIds + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function getAccessUrls($accessIds) + { + $urls = []; + foreach (static::getAll() as $item) { + in_array($item['access_id'], $accessIds) && $urls[] = $item['url']; + } + return $urls; + } + +} \ No newline at end of file diff --git a/source/application/common/model/store/Role.php b/source/application/common/model/store/Role.php new file mode 100644 index 0000000..df5b81a --- /dev/null +++ b/source/application/common/model/store/Role.php @@ -0,0 +1,27 @@ +hasOne("app\\{$module}\\model\\UploadFile", 'file_id', 'logo_image_id'); + } + + /** + * 地区名称 + * @param $value + * @param $data + * @return array + */ + public function getRegionAttr($value, $data) + { + return [ + 'province' => RegionModel::getNameById($data['province_id']), + 'city' => RegionModel::getNameById($data['city_id']), + 'region' => $data['region_id'] == 0 ? '' : RegionModel::getNameById($data['region_id']), + ]; + } + + /** + * 门店详情 + * @param $shop_id + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($shop_id) + { + return static::get($shop_id, ['logo']); + } + +} \ No newline at end of file diff --git a/source/application/common/model/store/User.php b/source/application/common/model/store/User.php new file mode 100644 index 0000000..01ca08a --- /dev/null +++ b/source/application/common/model/store/User.php @@ -0,0 +1,82 @@ +belongsTo("app\\{$module}\\model\\Wxapp"); + } + + /** + * 关联用户角色表表 + * @return \think\model\relation\BelongsToMany + */ + public function role() + { + return $this->belongsToMany('Role', 'StoreUserRole'); + } + + /** + * 验证用户名是否重复 + * @param $user_name + * @return bool + */ + public static function checkExist($user_name) + { + return !!static::useGlobalScope(false) + ->where('user_name', '=', $user_name) + ->where('is_delete', '=', 0) + ->value('store_user_id'); + } + + /** + * 商家用户详情 + * @param $where + * @param array $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($where, $with = []) + { + !is_array($where) && $where = ['store_user_id' => (int)$where]; + return static::get(array_merge(['is_delete' => 0], $where), $with); + } + + /** + * 保存登录状态 + * @param $user + * @throws \think\Exception + */ + public function loginState($user) + { + /** @var \app\common\model\Wxapp $wxapp */ + $wxapp = $user['wxapp']; + // 保存登录状态 + Session::set('yoshop_store', [ + 'user' => [ + 'store_user_id' => $user['store_user_id'], + 'user_name' => $user['user_name'], + ], + 'wxapp' => $wxapp->toArray(), + 'is_login' => true, + ]); + } + +} diff --git a/source/application/common/model/store/UserRole.php b/source/application/common/model/store/UserRole.php new file mode 100644 index 0000000..f0d7727 --- /dev/null +++ b/source/application/common/model/store/UserRole.php @@ -0,0 +1,17 @@ +BelongsTo("app\\{$module}\\model\\User"); + } + + /** + * 关联门店表 + * @return \think\model\relation\BelongsTo + */ + public function shop() + { + $module = static::getCalledModule() ?: 'common'; + return $this->BelongsTo("app\\{$module}\\model\\store\\Shop"); + } + + /** + * 店员详情 + * @param $where + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($where) + { + $filter = is_array($where) ? $where : ['clerk_id' => $where]; + return static::get(array_merge(['is_delete' => 0], $filter)); + } + +} \ No newline at end of file diff --git a/source/application/common/model/store/shop/Order.php b/source/application/common/model/store/shop/Order.php new file mode 100644 index 0000000..619a170 --- /dev/null +++ b/source/application/common/model/store/shop/Order.php @@ -0,0 +1,73 @@ +BelongsTo("app\\{$module}\\model\\store\\Shop"); + } + + /** + * 关联店员表 + * @return \think\model\relation\BelongsTo + */ + public function clerk() + { + $module = static::getCalledModule() ?: 'common'; + return $this->BelongsTo("app\\{$module}\\model\\store\\shop\\Clerk"); + } + + /** + * 订单类型 + * @param $value + * @return array + */ + public function getOrderTypeAttr($value) + { + $types = OrderTypeEnum::getTypeName(); + return ['text' => $types[$value], 'value' => $value]; + } + + /** + * 新增核销记录 + * @param int $order_id 订单id + * @param int $shop_id 门店id + * @param int $clerk_id 核销员id + * @param int $order_type + * @return mixed + */ + public static function add( + $order_id, + $shop_id, + $clerk_id, + $order_type = OrderTypeEnum::MASTER + ) + { + return (new static)->save([ + 'order_id' => $order_id, + 'order_type' => $order_type, + 'shop_id' => $shop_id, + 'clerk_id' => $clerk_id, + 'wxapp_id' => static::$wxapp_id + ]); + } + +} \ No newline at end of file diff --git a/source/application/common/model/user/BalanceLog.php b/source/application/common/model/user/BalanceLog.php new file mode 100644 index 0000000..2044264 --- /dev/null +++ b/source/application/common/model/user/BalanceLog.php @@ -0,0 +1,66 @@ + SceneEnum::data(), + ]; + } + + /** + * 关联会员记录表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 余额变动场景 + * @param $value + * @return array + */ + public function getSceneAttr($value) + { + return ['text' => SceneEnum::data()[$value]['name'], 'value' => $value]; + } + + /** + * 新增记录 + * @param $scene + * @param $data + * @param $describeParam + */ + public static function add($scene, $data, $describeParam) + { + $model = new static; + $model->save(array_merge([ + 'scene' => $scene, + 'describe' => vsprintf(SceneEnum::data()[$scene]['describe'], $describeParam), + 'wxapp_id' => $model::$wxapp_id + ], $data)); + } + +} \ No newline at end of file diff --git a/source/application/common/model/user/Grade.php b/source/application/common/model/user/Grade.php new file mode 100644 index 0000000..e6093fd --- /dev/null +++ b/source/application/common/model/user/Grade.php @@ -0,0 +1,111 @@ + 'asc']) + { + $model = new static; + $wxappId = $wxappId ? $wxappId : $model::$wxapp_id; + return $model->where('status', '=', '1') + ->where('is_delete', '=', '0') + ->where('wxapp_id', '=', $wxappId) + ->order($order) + ->select(); + } + + /** + * 验证等级权重是否存在 + * @param int $weight 验证的权重 + * @param int $gradeId 自身的等级ID + * @return bool + */ + public static function checkExistByWeight($weight, $gradeId = 0) + { + $model = new static; + $gradeId > 0 && $model->where('grade_id', '<>', (int)$gradeId); + return $model->where('weight', '=', (int)$weight) + ->value('grade_id'); + } + +} \ No newline at end of file diff --git a/source/application/common/model/user/GradeLog.php b/source/application/common/model/user/GradeLog.php new file mode 100644 index 0000000..ed4adbf --- /dev/null +++ b/source/application/common/model/user/GradeLog.php @@ -0,0 +1,36 @@ + ChangeTypeEnum::ADMIN_USER, + 'wxapp_id' => static::$wxapp_id + ], $item); + } + return $this->isUpdate(false)->saveAll($saveData); + } + +} \ No newline at end of file diff --git a/source/application/common/model/user/PointsLog.php b/source/application/common/model/user/PointsLog.php new file mode 100644 index 0000000..5323cb7 --- /dev/null +++ b/source/application/common/model/user/PointsLog.php @@ -0,0 +1,48 @@ +belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 新增记录 + * @param $data + */ + public static function add($data) + { + $static = new static; + $static->save(array_merge(['wxapp_id' => $static::$wxapp_id], $data)); + } + + /** + * 新增记录 (批量) + * @param $saveData + * @return array|false + * @throws \Exception + */ + public function onBatchAdd($saveData) + { + return $this->isUpdate(false)->saveAll($saveData); + } + +} \ No newline at end of file diff --git a/source/application/common/model/wow/Order.php b/source/application/common/model/wow/Order.php new file mode 100644 index 0000000..459d6d2 --- /dev/null +++ b/source/application/common/model/wow/Order.php @@ -0,0 +1,123 @@ +belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 获取器:最后更新时间 + * @param $value + * @return array + */ + public function getLastTimeAttr($value) + { + return ['value' => $value, 'text' => date('Y-m-d H:i:s', $value)]; + } + + /** + * 获取单条记录 + * @param $id + * @param $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($id, $with = ['goods.image.file', 'user']) + { + return static::get($id, $with); + } + + /** + * 添加好物圈订单同步记录(批量) + * @param $wxappId + * @param $orderList + * @param $orderType + * @return array|false + * @throws \Exception + */ + public function add($wxappId, $orderList, $orderType = OrderTypeEnum::MASTER) + { + // 批量添加 + $saveData = []; + foreach ($orderList as $item) { + $saveData[] = [ + 'order_id' => $item['order_id'], + 'user_id' => $item['user_id'], + 'order_type' => $orderType, + 'status' => WowOrderService::getStatusByOrder($item), + 'last_time' => time(), + 'wxapp_id' => $wxappId, + ]; + } + return $this->isUpdate(false)->saveAll($saveData); + } + + /** + * 更新好物圈订单同步记录(批量) + * @param $legalList + * @return array|false + * @throws \Exception + */ + public function edit($legalList) + { + // 批量更新 + $saveData = []; + foreach ($legalList as $id => $item) { + $saveData[] = [ + 'id' => $id, + 'status' => WowOrderService::getStatusByOrder($item), + 'last_time' => time(), + ]; + } + return $this->isUpdate()->saveAll($saveData); + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + + /** + * 根据订单id集和订单类型获取记录 + * @param array $orderIds + * @param int $orderType + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByOrderIds($orderIds, $orderType = OrderTypeEnum::MASTER) + { + return $this->where('order_id', 'in', $orderIds) + ->where('order_type', '=', $orderType) + ->where('is_delete', '=', 0) + ->select(); + } + +} \ No newline at end of file diff --git a/source/application/common/model/wow/Setting.php b/source/application/common/model/wow/Setting.php new file mode 100644 index 0000000..07b505d --- /dev/null +++ b/source/application/common/model/wow/Setting.php @@ -0,0 +1,98 @@ +toArray(), null, 'key'); + Cache::tag('cache')->set($cacheKey, $data); + } + return array_merge_multiple($self->defaultData(), $data); + } + + /** + * 获取设置项信息 + * @param $key + * @return null|static + * @throws \think\exception\DbException + */ + public static function detail($key) + { + return static::get(compact('key')); + } + + /** + * 默认配置 + * @return array + */ + public function defaultData() + { + return [ + 'basic' => [ + 'key' => 'basic', + 'describe' => '基础设置', + 'values' => [ + // 是否同步购物车 (商品收藏) + 'is_shopping' => '0', + // 是否同步订单 + 'is_order' => '0', + ] + ] + ]; + } + +} \ No newline at end of file diff --git a/source/application/common/model/wow/Shoping.php b/source/application/common/model/wow/Shoping.php new file mode 100644 index 0000000..3724a4b --- /dev/null +++ b/source/application/common/model/wow/Shoping.php @@ -0,0 +1,98 @@ +belongsTo("app\\{$module}\\model\\Goods"); + } + + /** + * 关联用户表 + * @return \think\model\relation\BelongsTo + */ + public function user() + { + $module = self::getCalledModule() ?: 'common'; + return $this->belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 获取单条记录 + * @param $id + * @param $with + * @return static|null + * @throws \think\exception\DbException + */ + public static function detail($id, $with = ['goods.image.file', 'user']) + { + return static::get($id, $with); + } + + /** + * 新增好物圈商品收藏记录 + * @param int $userId 用户id + * @param array $goodsIds 商品id + * @return array|false + * @throws \Exception + */ + public function add($userId, $goodsIds) + { + // 过滤该用户已收藏的商品id + $newGoodsIds = $this->getFilterGoodsIds($userId, $goodsIds); + if (empty($newGoodsIds)) { + return false; + } + $saveData = []; + foreach ($newGoodsIds as $goodsId) { + $saveData[] = [ + 'goods_id' => $goodsId, + 'user_id' => $userId, + 'wxapp_id' => self::$wxapp_id, + ]; + } + return $this->isUpdate(false)->saveAll($saveData); + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + + /** + * 过滤指定用户已收藏的商品id + * @param $userId + * @param $newGoodsIds + * @return array + */ + private function getFilterGoodsIds($userId, $newGoodsIds) + { + $alreadyGoodsId = $this->where('user_id', '=', $userId) + ->where('is_delete', '=', 0) + ->column('goods_id'); + return array_diff($newGoodsIds, $alreadyGoodsId); + } + +} \ No newline at end of file diff --git a/source/application/common/model/wxapp/Formid.php b/source/application/common/model/wxapp/Formid.php new file mode 100644 index 0000000..bc57e61 --- /dev/null +++ b/source/application/common/model/wxapp/Formid.php @@ -0,0 +1,50 @@ +belongsTo("app\\{$module}\\model\\User"); + } + + /** + * 获取一个可用的formid + * @param $userId + * @return array|false|\PDOStatement|string|\think\Model|static + */ + public static function getAvailable($userId) + { + return (new static)->where([ + 'user_id' => $userId, + 'is_used' => 0, + 'expiry_time' => ['>=', time()] + ])->order(['create_time' => 'asc'])->find(); + } + + /** + * 标记为已使用 + * @param $id + * @return Formid + */ + public static function setIsUsed($id) + { + return static::update(['is_used' => 1], compact('id')); + } + +} \ No newline at end of file diff --git a/source/application/common/service/Basics.php b/source/application/common/service/Basics.php new file mode 100644 index 0000000..71ae337 --- /dev/null +++ b/source/application/common/service/Basics.php @@ -0,0 +1,31 @@ +error; + } + + /** + * 是否存在错误 + * @return bool + */ + public function hasError() + { + return !empty($this->error); + } + +} \ No newline at end of file diff --git a/source/application/common/service/Goods.php b/source/application/common/service/Goods.php new file mode 100644 index 0000000..1db8d28 --- /dev/null +++ b/source/application/common/service/Goods.php @@ -0,0 +1,53 @@ +getListByIds(helper::getArrayColumn($dataSource, $goodsIndex)); + $goodsList = helper::arrayColumn2Key($goodsData, 'goods_id'); + // 整理列表数据 + foreach ($dataSource as &$item) { + $item['goods'] = isset($goodsList[$item[$goodsIndex]]) ? $goodsList[$item[$goodsIndex]] : null; + } + return $data; + } + + /** + * 商品多规格信息 + * @param GoodsModel|null $model + * @return null|array + */ + public static function getSpecData($model = null) + { + // 商品sku数据 + if (!is_null($model) && $model['spec_type'] == 20) { + return $model->getManySpecData($model['spec_rel'], $model['sku']); + } + return null; + } + +} \ No newline at end of file diff --git a/source/application/common/service/Message.php b/source/application/common/service/Message.php new file mode 100644 index 0000000..42a4154 --- /dev/null +++ b/source/application/common/service/Message.php @@ -0,0 +1,346 @@ + 'pages/order/detail', + OrderTypeEnum::SHARING => 'pages/sharing/order/detail/detail', + ]; + // 发送模板消息 + $status = $this->sendTemplateMessage($order['wxapp_id'], [ + 'touser' => $order['user']['open_id'], + 'template_id' => $template['template_id'], + 'page' => $urls[$orderType] . '?order_id=' . $order['order_id'], + 'form_id' => $formId['form_id'], + 'data' => [ + // 订单编号 + 'keyword1' => $order['order_no'], + // 支付时间 + 'keyword2' => date('Y-m-d H:i:s', $order['pay_time']), + // 订单金额 + 'keyword3' => $order['pay_price'], + // 商品名称 + 'keyword4' => $this->formatGoodsName($order['goods']), + ] + ]); + // 标记formid已使用 + $status === true && FormIdService::setIsUsed($formId['id']); + // 2. 商家短信通知 + $smsConfig = SettingModel::getItem('sms', $order['wxapp_id']); + $SmsDriver = new SmsDriver($smsConfig); + return $SmsDriver->sendSms('order_pay', ['order_no' => $order['order_no']]); + } + + /** + * 后台发货通知 + * @param \think\Model $order + * @param int $orderType 订单类型 (10商城订单 20拼团订单) + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function delivery($order, $orderType = OrderTypeEnum::MASTER) + { + // 微信模板消息 + $template = SettingModel::getItem('tplMsg', $order['wxapp_id'])['delivery']; + if (!$template['is_enable'] || empty($template['template_id'])) { + return false; + } + // 获取可用的formid + if (!$formId = FormIdService::getAvailableFormId($order['user_id'])) { + return false; + } + // 页面链接 + $urls = [ + OrderTypeEnum::MASTER => 'pages/order/detail', + OrderTypeEnum::SHARING => 'pages/sharing/order/detail/detail', + ]; + // 发送模板消息 + $status = $this->sendTemplateMessage($order['wxapp_id'], [ + 'touser' => $order['user']['open_id'], + 'template_id' => $template['template_id'], + 'page' => $urls[$orderType] . '?order_id=' . $order['order_id'], + 'form_id' => $formId['form_id'], + 'data' => [ + // 订单编号 + 'keyword1' => $order['order_no'], + // 商品信息 + 'keyword2' => $this->formatGoodsName($order['goods']), + // 收货人 + 'keyword3' => $order['address']['name'], + // 收货地址 + 'keyword4' => implode('', $order['address']['region']) . $order['address']['detail'], + // 物流公司 + 'keyword5' => $order['express']['express_name'], + // 物流单号 + 'keyword6' => $order['express_no'], + ] + ]); + // 标记formid已使用 + $status === true && FormIdService::setIsUsed($formId['id']); + return $status; + } + + /** + * 后台售后单状态通知 + * @param \think\Model $refund + * @param $order_no + * @param int $orderType 订单类型 (10商城订单 20拼团订单) + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function refund($refund, $order_no, $orderType = OrderTypeEnum::MASTER) + { + // 微信模板消息 + $template = SettingModel::getItem('tplMsg', $refund['wxapp_id'])['refund']; + if (!$template['is_enable'] || empty($template['template_id'])) { + return false; + } + // 获取可用的formid + if (!$formId = FormIdService::getAvailableFormId($refund['user_id'])) { + return false; + } + // 页面链接 + $urls = [ + OrderTypeEnum::MASTER => 'pages/order/refund/index', + OrderTypeEnum::SHARING => 'pages/sharing/order/refund/index', + ]; + // 发送模板消息 + $status = $this->sendTemplateMessage($refund['wxapp_id'], [ + 'touser' => $refund['user']['open_id'], + 'template_id' => $template['template_id'], + 'page' => $urls[$orderType], + 'form_id' => $formId['form_id'], + 'data' => [ + // 售后类型 + 'keyword1' => $refund['type']['text'], + // 状态 + 'keyword2' => $refund['status']['text'], + // 订单号 + 'keyword3' => $order_no, + // 商品名称 + 'keyword4' => $refund['order_goods']['goods_name'], + // 申请时间 + 'keyword5' => $refund['create_time'], + // 申请原因 + 'keyword6' => $refund['apply_desc'], + ] + ]); + // 标记formid已使用 + FormIdService::setIsUsed($formId['id']); + return $status; + } + + /** + * 拼团拼单状态通知 + * @param \app\common\model\sharing\Active $active + * @param string $status_text + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function sharingActive($active, $status_text) + { + // 微信模板消息 + $config = SharingSettingModel::getItem('basic', $active['wxapp_id']); + if (empty($config['tpl_msg_id'])) { + return false; + } + foreach ($active['users'] as $item) { + // 获取可用的formid + if (!$formId = FormIdService::getAvailableFormId($item['user']['user_id'])) { + continue; + } + // 发送模板消息 + $this->sendTemplateMessage($active['wxapp_id'], [ + 'touser' => $item['user']['open_id'], + 'template_id' => $config['tpl_msg_id'], + 'page' => 'pages/sharing/active/index?active_id=' . $active['active_id'], + 'form_id' => $formId['form_id'], + 'data' => [ + // 订单编号 + 'keyword1' => $item['sharing_order']['order_no'], + // 商品名称 + 'keyword2' => $active['goods']['goods_name'], + // 拼团价格 + 'keyword3' => $item['sharing_order']['pay_price'], + // 拼团人数 + 'keyword4' => $active['people'], + // 拼团时间 + 'keyword5' => $item['create_time'], + // 拼团结果 + 'keyword6' => $status_text, + ] + ]); + // 标记formid已使用 + FormIdService::setIsUsed($formId['id']); + } + return true; + } + + /** + * 分销商提现审核通知 + * @param \app\common\model\dealer\Withdraw $withdraw + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function withdraw($withdraw) + { + // 模板消息id + $template = DealerSettingModel::getItem('template_msg', $withdraw['wxapp_id']); + if (empty($template['withdraw_tpl'])) { + return false; + } + // 获取可用的formid + if (!$formId = FormIdService::getAvailableFormId($withdraw['user_id'])) { + return false; + } + // 获取用户信息 + $user = UserModel::detail($withdraw['user_id']); + // 发送模板消息 + $remark = '无'; + if ($withdraw['apply_status'] == 30) { + $remark = $withdraw['reject_reason']; + } + $status = $this->sendTemplateMessage($withdraw['wxapp_id'], [ + 'touser' => $user['open_id'], + 'template_id' => $template['withdraw_tpl'], + 'page' => 'pages/dealer/withdraw/list/list', + 'form_id' => $formId['form_id'], + 'data' => [ + // 提现时间 + 'keyword1' => $withdraw['create_time'], + // 提现方式 + 'keyword2' => $withdraw['pay_type']['text'], + // 提现金额 + 'keyword3' => $withdraw['money'], + // 提现状态 + 'keyword4' => $withdraw->applyStatus[$withdraw['apply_status']], + // 备注 + 'keyword5' => $remark, + ] + ]); + // 标记formid已使用 + FormIdService::setIsUsed($formId['id']); + return $status; + } + + /** + * 分销商入驻审核通知 + * @param \app\common\model\dealer\Apply $dealer + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function dealer($dealer) + { + // 模板消息id + $template = DealerSettingModel::getItem('template_msg', $dealer['wxapp_id']); + if (empty($template['apply_tpl'])) { + return false; + } + // 获取可用的formid + if (!$formId = FormIdService::getAvailableFormId($dealer['user_id'])) { + return false; + } + // 获取用户信息 + $user = UserModel::detail($dealer['user_id']); + // 发送模板消息 + $remark = '分销商入驻审核通知'; + if ($dealer['apply_status'] == 30) { + $remark .= "\n\n驳回原因:" . $dealer['reject_reason']; + } + $status = $this->sendTemplateMessage($dealer['wxapp_id'], [ + 'touser' => $user['open_id'], + 'template_id' => $template['apply_tpl'], + 'page' => 'pages/dealer/index/index', + 'form_id' => $formId['form_id'], + 'data' => [ + // 申请时间 + 'keyword1' => $dealer['apply_time'], + // 审核状态 + 'keyword2' => $dealer->applyStatus[$dealer['apply_status']], + // 审核时间 + 'keyword3' => $dealer['audit_time'], + // 备注信息 + 'keyword4' => $remark, + ] + ]); + // 标记formid已使用 + FormIdService::setIsUsed($formId['id']); + return $status; + } + + /** + * 发送模板消息 + * @param $wxappId + * @param $params + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function sendTemplateMessage($wxappId, $params) + { + // 微信模板消息 + $wxConfig = WxappModel::getWxappCache($wxappId); + $WxTplMsg = new WxTplMsg($wxConfig['app_id'], $wxConfig['app_secret']); + return $WxTplMsg->sendTemplateMessage($params); + } + + /** + * 格式化商品名称 + * @param $goodsData + * @return string + */ + private function formatGoodsName($goodsData) + { + $str = ''; + foreach ($goodsData as $goods) { + $str .= $goods['goods_name'] . ' '; + } + return $str; + } + +} \ No newline at end of file diff --git a/source/application/common/service/Order.php b/source/application/common/service/Order.php new file mode 100644 index 0000000..596c93b --- /dev/null +++ b/source/application/common/service/Order.php @@ -0,0 +1,91 @@ + 'app\common\model\Order', + OrderTypeEnum::SHARING => 'app\common\model\sharing\Order' + ]; + + /** + * 生成订单号 + * @return string + */ + public static function createOrderNo() + { + return date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); + } + + /** + * 整理订单列表 (根据order_type获取不同类型的订单记录) + * @param \think\Collection|\think\Paginator $data 数据源 + * @param string $orderIndex 订单记录的索引 + * @param array $with 关联查询 + * @return mixed + */ + public static function getOrderList($data, $orderIndex = 'order', $with = []) + { + // 整理订单id + $orderIds = []; + foreach ($data as &$item) { + $orderIds[$item['order_type']['value']][] = $item['order_id']; + } + // 获取订单列表 + $orderList = []; + foreach ($orderIds as $orderType => $values) { + $model = self::model($orderType); + $orderList[$orderType] = $model->getListByIds($values, $with); + } + // 格式化到数据源 + foreach ($data as $key => &$item) { + if (!isset($orderList[$item['order_type']['value']][$item['order_id']])) { + // todo: 兼容错误数据 + $item->delete(); + unset($data[$key]); + continue; + } + $item[$orderIndex] = $orderList[$item['order_type']['value']][$item['order_id']]; + } + return $data; + } + + /** + * 获取订单详情 (根据order_type获取不同类型的订单详情) + * @param $orderId + * @param int $orderType + * @return mixed + */ + public static function getOrderDetail($orderId, $orderType = OrderTypeEnum::MASTER) + { + $model = self::model($orderType); + return $model::detail($orderId); + } + + /** + * 根据订单类型获取对应的订单模型类 + * @param int $orderType + * @return mixed + */ + public static function model($orderType = OrderTypeEnum::MASTER) + { + static $models = []; + if (!isset($models[$orderType])) { + $models[$orderType] = new self::$orderModelClass[$orderType]; + } + return $models[$orderType]; + } + +} \ No newline at end of file diff --git a/source/application/common/service/delivery/Express.php b/source/application/common/service/delivery/Express.php new file mode 100644 index 0000000..4142aa5 --- /dev/null +++ b/source/application/common/service/delivery/Express.php @@ -0,0 +1,249 @@ +cityId = $cityId; + $this->goodsList = $goodsList; + $this->orderType = $orderType; + // 整合运费模板 + $this->initDeliveryTemplate(); + } + + /** + * 验证用户收货地址是否在配送范围 + * @return bool + */ + public function isIntraRegion() + { + if (!$this->cityId) return false; + foreach ($this->data as $item) { + $cityIds = []; + foreach ($item['delivery']['rule'] as $ruleItem) { + $cityIds = array_merge($cityIds, $ruleItem['region_data']); + } + if (!in_array($this->cityId, $cityIds)) { + $this->notInRuleGoodsId = current($item['goodsList'])['goods_id']; + return false; + } + } + return true; + } + + /** + * 获取不在配送范围的商品名称 + * @return null + */ + public function getNotInRuleGoodsName() + { + $item = helper::getArrayItemByColumn($this->goodsList, 'goods_id', $this->notInRuleGoodsId); + return !empty($item) ? $item['goods_name'] : null; + } + + /** + * 获取订单的配送费用 + * @return float|string + */ + public function getDeliveryFee() + { + if (empty($this->cityId) || empty($this->goodsList) || $this->notInRuleGoodsId > 0) { + return helper::number2(0.00); + } + // 处理商品包邮 + $this->freeshipping(); + // 计算配送金额 + foreach ($this->data as &$item) { + // 计算当前配送模板的运费 + $item['delivery_fee'] = $this->calcDeliveryAmount($item); + } + // 根据运费组合策略获取最终运费金额 + return helper::number2($this->getFinalFreight()); + } + + /** + * 根据运费组合策略 计算最终运费 + * @return double + */ + private function getFinalFreight() + { + // 运费合集 + $expressPriceArr = helper::getArrayColumn($this->data, 'delivery_fee'); + // 最终运费金额 + $expressPrice = 0.00; + // 判断运费组合策略 + switch (SettingModel::getItem('trade')['freight_rule']) { + case '10': // 策略1: 叠加 + $expressPrice = array_sum($expressPriceArr); + break; + case '20': // 策略2: 以最低运费结算 + $expressPrice = min($expressPriceArr); + break; + case '30': // 策略3: 以最高运费结算 + $expressPrice = max($expressPriceArr); + break; + } + return $expressPrice; + } + + /** + * 处理商品包邮 + * @return bool + */ + private function freeshipping() + { + // 订单商品总金额 + $orderTotalPrice = helper::getArrayColumnSum($this->goodsList, 'total_price'); + // 获取满额包邮设置 + $options = SettingModel::getItem('full_free'); + foreach ($this->data as &$item) { + $item['free_goods_list'] = []; + foreach ($item['goodsList'] as $goodsItem) { + if ( + $this->orderType === OrderTypeEnum::MASTER + && $options['is_open'] == true + && $orderTotalPrice >= $options['money'] + && !in_array($goodsItem['goods_id'], $options['notin_goods']) + && !in_array($this->cityId, $options['notin_region']['citys']) + ) { + $item['free_goods_list'][] = $goodsItem['goods_id']; + } + } + } + return true; + } + + /** + * 计算当前配送模板的运费 + * @param $item + * @return float|mixed|string + */ + private function calcDeliveryAmount($item) + { + // 获取运费模板下商品总数量or总重量 + if (!$totality = $this->getItemGoodsTotal($item)) { + return 0.00; + } + // 当前收货城市配送规则 + $deliveryRule = $this->getCityDeliveryRule($item['delivery']); + if ($totality <= $deliveryRule['first']) { + return $deliveryRule['first_fee']; + } + // 续件or续重 数量 + $additional = $totality - $deliveryRule['first']; + if ($additional <= $deliveryRule['additional']) { + return helper::bcadd($deliveryRule['first_fee'], $deliveryRule['additional_fee']); + } + // 计算续重/件金额 + if ($deliveryRule['additional'] < 1) { + // 配送规则中续件为0 + $additionalFee = 0.00; + } else { + $additionalFee = helper::bcdiv($deliveryRule['additional_fee'], $deliveryRule['additional']) * $additional; + } + return helper::bcadd($deliveryRule['first_fee'], $additionalFee); + } + + /** + * 获取运费模板下商品总数量or总重量 + * @param $item + * @return int|string + */ + private function getItemGoodsTotal($item) + { + $totalWeight = 0; // 总重量 + $totalNum = 0; // 总数量 + foreach ($item['goodsList'] as $goodsItem) { + // 如果商品为包邮,则不计算总量中 + if (!in_array($goodsItem['goods_id'], $item['free_goods_list'])) { + $goodsWeight = helper::bcmul($goodsItem['goods_sku']['goods_weight'], $goodsItem['total_num']); + $totalWeight = helper::bcadd($totalWeight, $goodsWeight); + $totalNum = helper::bcadd($totalNum, $goodsItem['total_num']); + } + } + return $item['delivery']['method']['value'] == 10 ? $totalNum : $totalWeight; + } + + /** + * 根据城市id获取规则信息 + * @param + * @return array|false + */ + private function getCityDeliveryRule($delivery) + { + foreach ($delivery['rule'] as $item) { + if (in_array($this->cityId, $item['region_data'])) { + return $item; + } + } + return false; + } + + /** + * 整合运费模板 + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function initDeliveryTemplate() + { + // 运费模板ID集 + $deliveryIds = helper::getArrayColumn($this->goodsList, 'delivery_id'); + // 运费模板列表 + $deliveryList = (new DeliveryModel)->getListByIds(array_values(array_unique($deliveryIds))); + // 整理数据集 + foreach ($deliveryList as $item) { + $this->data[$item['delivery_id']]['delivery'] = $item; + $this->data[$item['delivery_id']]['goodsList'] = $this->getGoodsListByDeliveryId($item['delivery_id']); + } + return true; + } + + /** + * 根据运费模板id整理商品集 + * @param $deliveryId + * @return array + */ + private function getGoodsListByDeliveryId($deliveryId) + { + $data = []; + foreach ($this->goodsList as $item) { + $item['delivery_id'] == $deliveryId && $data[] = $item; + } + return $data; + } + + +} \ No newline at end of file diff --git a/source/application/common/service/goods/source/Bargain.php b/source/application/common/service/goods/source/Bargain.php new file mode 100644 index 0000000..198000c --- /dev/null +++ b/source/application/common/service/goods/source/Bargain.php @@ -0,0 +1,13 @@ + 'Master', + OrderSourceEnum::BARGAIN => 'Bargain', + OrderSourceEnum::SHARP => 'Sharp', + ]; + + /** + * 根据订单来源获取商品库存类 + * @param int $orderSource + * @return mixed + */ + public static function getFactory($orderSource = OrderSourceEnum::MASTER) + { + static $classObj = []; + if (!isset($classObj[$orderSource])) { + $className = __NAMESPACE__ . '\\' . static::$class[$orderSource]; + $classObj[$orderSource] = new $className(); + } + return $classObj[$orderSource]; + } +} \ No newline at end of file diff --git a/source/application/common/service/goods/source/Master.php b/source/application/common/service/goods/source/Master.php new file mode 100644 index 0000000..a7c25ef --- /dev/null +++ b/source/application/common/service/goods/source/Master.php @@ -0,0 +1,123 @@ + ['stock_num' => ['dec', $goods['total_num']]], + 'where' => [ + 'goods_id' => $goods['goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + ]; + } + } + return !empty($data) && $this->updateGoodsSku($data); + } + + /** + * 更新商品库存销量(订单付款后) + * @param $goodsList + * @return bool + * @throws \Exception + */ + public function updateStockSales($goodsList) + { + $goodsData = []; + $goodsSkuData = []; + foreach ($goodsList as $goods) { + // 记录商品的销量 + $goodsData[] = [ + 'goods_id' => $goods['goods_id'], + 'sales_actual' => ['inc', $goods['total_num']] + ]; + // 付款减库存 + if ($goods['deduct_stock_type'] == 20) { + $goodsSkuData[] = [ + 'data' => ['stock_num' => ['dec', $goods['total_num']]], + 'where' => [ + 'goods_id' => $goods['goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + ]; + } + } + // 更新商品销量 + !empty($goodsData) && $this->updateGoods($goodsData); + // 更新商品sku库存 + !empty($goodsSkuData) && $this->updateGoodsSku($goodsSkuData); + return true; + } + + /** + * 回退商品库存 + * @param $goodsList + * @param $isPayOrder + * @return array|false + * @throws \Exception + */ + public function backGoodsStock($goodsList, $isPayOrder = false) + { + $goodsSkuData = []; + foreach ($goodsList as $goods) { + $item = [ + 'where' => [ + 'goods_id' => $goods['goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + 'data' => ['stock_num' => ['inc', $goods['total_num']]], + ]; + if ($isPayOrder == true) { + // 付款订单全部库存 + $goodsSkuData[] = $item; + } else { + // 未付款订单,判断必须为下单减库存时才回退 + $goods['deduct_stock_type'] == DeductStockTypeEnum::CREATE && $goodsSkuData[] = $item; + } + } + // 更新商品sku库存 + return !empty($goodsSkuData) && $this->updateGoodsSku($goodsSkuData); + } + + /** + * 更新商品信息 + * @param $data + * @return array|false + * @throws \Exception + */ + private function updateGoods($data) + { + return (new GoodsModel)->allowField(true)->isUpdate()->saveAll($data); + } + + /** + * 更新商品sku信息 + * @param $data + * @return \think\Collection + */ + private function updateGoodsSku($data) + { + return (new GoodsSkuModel)->updateAll($data); + } +} \ No newline at end of file diff --git a/source/application/common/service/goods/source/Sharp.php b/source/application/common/service/goods/source/Sharp.php new file mode 100644 index 0000000..9e47e36 --- /dev/null +++ b/source/application/common/service/goods/source/Sharp.php @@ -0,0 +1,151 @@ + $goods['sharp_goods_id'], + 'seckill_stock' => ['dec', $goods['total_num']] + ]; + // 记录商品sku库存 + $goodsSkuData[] = [ + 'data' => ['seckill_stock' => ['dec', $goods['total_num']]], + 'where' => [ + 'sharp_goods_id' => $goods['sharp_goods_id'], + 'spec_sku_id' => $goods['spec_sku_id'], + ], + ]; + } + } + // 更新商品总销量 + !empty($goodsData) && $this->updateGoods($goodsData); + return !empty($goodsSkuData) && $this->updateGoodsSku($goodsSkuData); + } + + /** + * 更新商品库存销量(订单付款后) + * @param $goodsList + * @return bool + * @throws \Exception + */ + public function updateStockSales($goodsList) + { + $goodsData = []; + $goodsSkuData = []; + foreach ($goodsList as $goods) { + // 商品id + $sharpGoodsId = $goods['goods_source_id']; + // 记录商品总销量 + $goodsItem = [ + 'sharp_goods_id' => $sharpGoodsId, + 'total_sales' => ['inc', $goods['total_num']] + ]; + // 付款减库存 + if ($goods['deduct_stock_type'] == 20) { + // 记录商品总库存 + $goodsItem['seckill_stock'] = ['dec', $goods['total_num']]; + // 记录商品sku库存 + $goodsSkuData[] = [ + 'data' => ['seckill_stock' => ['dec', $goods['total_num']]], + 'where' => [ + 'sharp_goods_id' => $sharpGoodsId, + 'spec_sku_id' => $goods['spec_sku_id'], + ], + ]; + } + $goodsData[] = $goodsItem; + } + // 更新商品库存销量 + !empty($goodsData) && $this->updateGoods(array_values($goodsData)); + return !empty($goodsSkuData) && $this->updateGoodsSku($goodsSkuData); + } + + /** + * 回退商品库存 + * @param $goodsList + * @param $isPayOrder + * @return array|false + * @throws \Exception + */ + public function backGoodsStock($goodsList, $isPayOrder = false) + { + $goodsData = []; + $goodsSkuData = []; + foreach ($goodsList as $goods) { + // 商品id + $sharpGoodsId = $goods['goods_source_id']; + $goodsItem = [ + 'sharp_goods_id' => $sharpGoodsId, + 'seckill_stock' => ['inc', $goods['total_num']] + ]; + $goodsSkuItem = [ + 'where' => [ + 'sharp_goods_id' => $sharpGoodsId, + 'spec_sku_id' => $goods['spec_sku_id'], + ], + 'data' => ['seckill_stock' => ['inc', $goods['total_num']]], + ]; + // 付款订单全部库存 + if ($isPayOrder == true) { + $goodsData[] = $goodsItem; + $goodsSkuData[] = $goodsSkuItem; + } + // 未付款订单,判断必须为下单减库存时才回退 + if ($isPayOrder == false + && $goods['deduct_stock_type'] == DeductStockTypeEnum::CREATE) { + $goodsData[] = $goodsItem; + $goodsSkuData[] = $goodsSkuItem; + } + } + // 更新商品总库存 + !empty($goodsData) && $this->updateGoods($goodsData); + // 更新商品sku库存 + return !empty($goodsSkuData) && $this->updateGoodsSku($goodsSkuData); + } + + /** + * 更新商品信息 + * @param $data + * @return array|false + * @throws \Exception + */ + private function updateGoods($data) + { + return (new SharpGoodsModel)->allowField(true)->isUpdate()->saveAll($data); + } + + /** + * 更新商品sku信息 + * @param $data + * @return \think\Collection + */ + private function updateGoodsSku($data) + { + return (new GoodsSkuModel)->updateAll($data); + } + +} \ No newline at end of file diff --git a/source/application/common/service/order/Complete.php b/source/application/common/service/order/Complete.php new file mode 100644 index 0000000..ea391e2 --- /dev/null +++ b/source/application/common/service/order/Complete.php @@ -0,0 +1,183 @@ + 'app\common\model\Order', + OrderTypeEnum::SHARING => 'app\common\model\sharing\Order', + ]; + + /* @var \app\common\model\Order $model */ + private $model; + + /* @var UserModel $model */ + private $UserModel; + + /** + * 构造方法 + * Complete constructor. + * @param int $orderType + */ + public function __construct($orderType = OrderTypeEnum::MASTER) + { + $this->orderType = $orderType; + $this->model = $this->getOrderModel(); + $this->UserModel = new UserModel; + } + + /** + * 初始化订单模型类 + * @return \app\common\model\Order|mixed + */ + private function getOrderModel() + { + $class = $this->orderModelClass[$this->orderType]; + return new $class; + } + + /** + * 执行订单完成后的操作 + * @param \think\Collection|array $orderList + * @param int $wxappId + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \Exception + */ + public function complete($orderList, $wxappId) + { + // 已完成订单结算 + // 条件:后台订单流程设置 - 已完成订单设置0天不允许申请售后 + if (SettingModel::getItem('trade', $wxappId)['order']['refund_days'] == 0) { + $this->settled($orderList); + } + // 发放分销商佣金 + foreach ($orderList as $order) { + DealerOrderModel::grantMoney($order, $this->orderType); + } + // 更新好物圈订单状态 + if ($this->orderType == OrderTypeEnum::MASTER) { + (new WowService($wxappId))->update($orderList); + } + return true; + } + + /** + * 执行订单结算 + * @param $orderList + * @return bool + * @throws \Exception + */ + public function settled($orderList) + { + // 订单id集 + $orderIds = helper::getArrayColumn($orderList, 'order_id'); + // 累积用户实际消费金额 + $this->setIncUserExpend($orderList); + // 处理订单赠送的积分 + $this->setGiftPointsBonus($orderList); + // 将订单设置为已结算 + $this->model->onBatchUpdate($orderIds, ['is_settled' => 1]); + return true; + } + + /** + * 处理订单赠送的积分 + * @param $orderList + * @return bool + * @throws \Exception + */ + private function setGiftPointsBonus($orderList) + { + // 计算用户所得积分 + $userData = []; + $logData = []; + foreach ($orderList as $order) { + // 计算用户所得积分 + $pointsBonus = $order['points_bonus']; + if ($pointsBonus <= 0) continue; + // 减去订单退款的积分 + foreach ($order['goods'] as $goods) { + if ( + !empty($goods['refund']) + && $goods['refund']['type']['value'] == 10 // 售后类型:退货退款 + && $goods['refund']['is_agree']['value'] == 10 // 商家审核:已同意 + ) { + $pointsBonus -= $goods['points_bonus']; + } + } + // 计算用户所得积分 + !isset($userData[$order['user_id']]) && $userData[$order['user_id']] = 0; + $userData[$order['user_id']] += $pointsBonus; + // 整理用户积分变动明细 + $logData[] = [ + 'user_id' => $order['user_id'], + 'value' => $pointsBonus, + 'describe' => "订单赠送:{$order['order_no']}", + 'wxapp_id' => $order['wxapp_id'], + ]; + } + if (!empty($userData)) { + // 累积到会员表记录 + $this->UserModel->onBatchIncPoints($userData); + // 批量新增积分明细记录 + (new PointsLogModel)->onBatchAdd($logData); + } + return true; + } + + /** + * 累积用户实际消费金额 + * @param $orderList + * @return bool + * @throws \Exception + */ + private function setIncUserExpend($orderList) + { + // 计算并累积实际消费金额(需减去售后退款的金额) + $userData = []; + foreach ($orderList as $order) { + // 订单实际支付金额 + $expendMoney = $order['pay_price']; + // 减去订单退款的金额 + foreach ($order['goods'] as $goods) { + if ( + !empty($goods['refund']) + && $goods['refund']['type']['value'] == 10 // 售后类型:退货退款 + && $goods['refund']['is_agree']['value'] == 10 // 商家审核:已同意 + ) { + $expendMoney -= $goods['refund']['refund_money']; + } + } + !isset($userData[$order['user_id']]) && $userData[$order['user_id']] = 0.00; + $expendMoney > 0 && $userData[$order['user_id']] += $expendMoney; + } + // 累积到会员表记录 + $this->UserModel->onBatchIncExpendMoney($userData); + return true; + } + +} \ No newline at end of file diff --git a/source/application/common/service/order/Printer.php b/source/application/common/service/order/Printer.php new file mode 100644 index 0000000..2dad26b --- /dev/null +++ b/source/application/common/service/order/Printer.php @@ -0,0 +1,115 @@ +getPrintContent($order); + // 执行打印请求 + return $PrinterDriver->printTicket($content); + } + + /** + * 构建订单打印的内容 + * @param \app\common\model\BaseModel $order + * @return string + */ + private function getPrintContent($order) + { + // 商城名称 + $storeName = SettingModel::getItem('store', $order['wxapp_id'])['name']; + // 收货地址 + /* @var \app\common\model\OrderAddress $address */ + $address = $order['address']; + // 拼接模板内容 + $content = "{$storeName}
"; + $content .= '--------------------------------
'; + $content .= "昵称:{$order['user']['nickName']} [{$order['user_id']}]
"; + $content .= "订单号:{$order['order_no']}
"; + $content .= '付款时间:' . date('Y-m-d H:i:s', $order['pay_time']) . '
'; + // 收货人信息 + if ($order['delivery_type']['value'] == DeliveryTypeEnum::EXPRESS) { + $content .= "--------------------------------
"; + $content .= "收货人:{$address['name']}
"; + $content .= "联系电话:{$address['phone']}
"; + $content .= '收货地址:' . $address->getFullAddress() . '
'; + } + // 自提信息 + if ($order['delivery_type']['value'] == DeliveryTypeEnum::EXTRACT && !empty($order['extract'])) { + $content .= "--------------------------------
"; + $content .= "联系人:{$order['extract']['linkman']}
"; + $content .= "联系电话:{$order['extract']['phone']}
"; + $content .= "自提门店:{$order['extract_shop']['shop_name']}
"; + } + // 商品信息 + $content .= '=========== 商品信息 ===========
'; + foreach ($order['goods'] as $key => $goods) { + $content .= ($key + 1) . ".商品名称:{$goods['goods_name']}
"; + !empty($goods['goods_attr']) && $content .= " 商品规格:{$goods['goods_attr']}
"; + $content .= " 购买数量:{$goods['total_num']}
"; + $content .= " 商品总价:{$goods['total_price']}元
"; + $content .= '--------------------------------
'; + } + // 买家备注 + if (!empty($order['buyer_remark'])) { + $content .= '============ 买家备注 ============
'; + $content .= "{$order['buyer_remark']}
"; + $content .= '--------------------------------
'; + } + // 订单金额 + if ($order['coupon_money'] > 0) { + $content .= "优惠券:-{$order['coupon_money']}元
"; + } + if ($order['points_num'] > 0) { + $content .= "积分抵扣:-{$order['points_money']}元
"; + } + if ($order['update_price']['value'] != '0.00') { + $content .= "后台改价:{$order['update_price']['symbol']}{$order['update_price']['value']}元
"; + } + // 运费 + if ($order['delivery_type']['value'] == DeliveryTypeEnum::EXPRESS) { + $content .= "运费:{$order['express_price']}元
"; + $content .= '------------------------------
'; + } + // 实付款 + $content .= "实付款:{$order['pay_price']}
"; + return $content; + } + +} \ No newline at end of file diff --git a/source/application/common/service/order/Refund.php b/source/application/common/service/order/Refund.php new file mode 100644 index 0000000..3e80be6 --- /dev/null +++ b/source/application/common/service/order/Refund.php @@ -0,0 +1,79 @@ +wxpay($order, $money); + } + // 2.余额支付退款 + if ($order['pay_type']['value'] == PayTypeEnum::BALANCE) { + return $this->balance($order, $money); + } + return false; + } + + /** + * 余额支付退款 + * @param $order + * @param $money + * @return bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function balance(&$order, $money) + { + // 回退用户余额 + $user = UserModel::detail($order['user_id']); + $user->setInc('balance', $money); + // 记录余额明细 + BalanceLogModel::add(SceneEnum::REFUND, [ + 'user_id' => $user['user_id'], + 'money' => $money, + ], ['order_no' => $order['order_no']]); + return true; + } + + /** + * 微信支付退款 + * @param $order + * @param double $money + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function wxpay(&$order, $money) + { + $wxConfig = WxappModel::getWxappCache($order['wxapp_id']); + $WxPay = new WxPay($wxConfig); + return $WxPay->refund($order['transaction_id'], $order['pay_price'], $money); + } + +} \ No newline at end of file diff --git a/source/application/common/service/order/Source.php b/source/application/common/service/order/Source.php new file mode 100644 index 0000000..e945047 --- /dev/null +++ b/source/application/common/service/order/Source.php @@ -0,0 +1,9 @@ +getQrcode($scene, $page); + // 保存到文件 + file_put_contents($savePath, $content); + return $savePath; + } + + /** + * 获取网络图片到临时目录 + * @param $wxapp_id + * @param $url + * @param string $mark + * @return string + */ + protected function saveTempImage($wxapp_id, $url, $mark = 'temp') + { + $dirPath = RUNTIME_PATH . 'image' . '/' . $wxapp_id; + !is_dir($dirPath) && mkdir($dirPath, 0755, true); + $savePath = $dirPath . '/' . $mark . '_' . md5($url) . '.png'; + if (file_exists($savePath)) return $savePath; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); + $img = curl_exec($ch); + curl_close($ch); + $fp = fopen($savePath, 'w'); + fwrite($fp, $img); + fclose($fp); + return $savePath; + } + +} \ No newline at end of file diff --git a/source/application/common/service/qrcode/Extract.php b/source/application/common/service/qrcode/Extract.php new file mode 100644 index 0000000..6fbbea1 --- /dev/null +++ b/source/application/common/service/qrcode/Extract.php @@ -0,0 +1,98 @@ +wxappId = $wxappId; + $this->user = $user; + $this->orderId = $orderId; + $this->orderType = $orderType; + } + + /** + * 获取小程序码 + * @return mixed + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function getImage() + { + // 判断二维码文件存在则直接返回url + if (file_exists($this->getPosterPath())) { + return $this->getPosterUrl(); + } + // 下载小程序码 + $qrcode = $this->saveQrcode( + $this->wxappId, + "oid:{$this->orderId},oty:{$this->orderType}", + 'pages/store/check/order' + ); + return $this->savePoster($qrcode); + } + + private function savePoster($qrcode) + { + copy($qrcode, $this->getPosterPath()); + return $this->getPosterUrl(); + } + + /** + * 二维码文件路径 + * @return string + */ + private function getPosterPath() + { + // 保存路径 + $tempPath = WEB_PATH . "temp/{$this->wxappId}/"; + !is_dir($tempPath) && mkdir($tempPath, 0755, true); + return $tempPath . $this->getPosterName(); + } + + /** + * 二维码文件名称 + * @return string + */ + private function getPosterName() + { + return 'extract_' . md5("{$this->orderId}_{$this->user['open_id']}}") . '.png'; + } + + /** + * 二维码url + * @return string + */ + private function getPosterUrl() + { + return \base_url() . 'temp/' . $this->wxappId . '/' . $this->getPosterName() . '?t=' . time(); + } + +} \ No newline at end of file diff --git a/source/application/common/service/qrcode/Goods.php b/source/application/common/service/qrcode/Goods.php new file mode 100644 index 0000000..3ec22d7 --- /dev/null +++ b/source/application/common/service/qrcode/Goods.php @@ -0,0 +1,177 @@ + 'pages/goods/index', + 20 => 'pages/sharing/goods/index' + ]; + + /** + * 构造方法 + * Goods constructor. + * @param $goods + * @param $user + * @param int $goodsType + */ + public function __construct($goods, $user, $goodsType = 10) + { + parent::__construct(); + // 商品信息 + $this->goods = $goods; + // 当前用户id + $this->user_id = $user ? $user['user_id'] : 0; + // 商品类型:10商城商品 20拼团商品 + $this->goodsType = $goodsType; + } + + /** + * @return mixed + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function getImage() + { + // 判断海报图文件存在则直接返回url + if (file_exists($this->getPosterPath())) { + return $this->getPosterUrl(); + } + // 小程序id + $wxappId = $this->goods['wxapp_id']; + // 商品海报背景图 + $backdrop = __DIR__ . '/resource/goods_bg.png'; + // 下载商品首图 + $goodsUrl = $this->saveTempImage($wxappId, $this->goods['image'][0]['file_path'], 'goods'); + // 小程序码参数 + $scene = "gid:{$this->goods['goods_id']},uid:" . ($this->user_id ?: ''); + // 下载小程序码 + $qrcode = $this->saveQrcode($wxappId, $scene, $this->pages[$this->goodsType]); + // 拼接海报图 + return $this->savePoster($backdrop, $goodsUrl, $qrcode); + } + + /** + * 拼接海报图 + * @param $backdrop + * @param $goodsUrl + * @param $qrcode + * @return string + * @throws \Exception + */ + private function savePoster($backdrop, $goodsUrl, $qrcode) + { + // 实例化图像编辑器 + $editor = Grafika::createEditor(['Gd']); + // 字体文件路径 + $fontPath = Grafika::fontsDir() . '/' . 'st-heiti-light.ttc'; + // 打开海报背景图 + $editor->open($backdropImage, $backdrop); + // 打开商品图片 + $editor->open($goodsImage, $goodsUrl); + // 重设商品图片宽高 + $editor->resizeExact($goodsImage, 690, 690); + // 商品图片添加到背景图 + $editor->blend($backdropImage, $goodsImage, 'normal', 1.0, 'top-left', 30, 30); + // 商品名称处理换行 + $fontSize = 30; + $goodsName = $this->wrapText($fontSize, 0, $fontPath, $this->goods['goods_name'], 680, 2); + // 写入商品名称 + $editor->text($backdropImage, $goodsName, $fontSize, 30, 750, new Color('#333333'), $fontPath); + // 写入商品价格 + $priceType = [10 => 'goods_price', 20 => 'sharing_price']; + $editor->text($backdropImage, $this->goods['sku'][0][$priceType[$this->goodsType]], 38, 62, 964, new Color('#ff4444')); + // 打开小程序码 + $editor->open($qrcodeImage, $qrcode); + // 重设小程序码宽高 + $editor->resizeExact($qrcodeImage, 140, 140); + // 小程序码添加到背景图 + $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', 570, 914); + // 保存图片 + $editor->save($backdropImage, $this->getPosterPath()); + return $this->getPosterUrl(); + } + + /** + * 处理文字超出长度自动换行 + * @param integer $fontsize 字体大小 + * @param integer $angle 角度 + * @param string $fontface 字体名称 + * @param string $string 字符串 + * @param integer $width 预设宽度 + * @param null $max_line 最多行数 + * @return string + */ + private function wrapText($fontsize, $angle, $fontface, $string, $width, $max_line = null) + { + // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 + $content = ""; + // 将字符串拆分成一个个单字 保存到数组 letter 中 + $letter = []; + for ($i = 0; $i < mb_strlen($string, 'UTF-8'); $i++) { + $letter[] = mb_substr($string, $i, 1, 'UTF-8'); + } + $line_count = 0; + foreach ($letter as $l) { + $testbox = imagettfbbox($fontsize, $angle, $fontface, $content . ' ' . $l); + // 判断拼接后的字符串是否超过预设的宽度 + if (($testbox[2] > $width) && ($content !== "")) { + $line_count++; + if ($max_line && $line_count >= $max_line) { + $content = mb_substr($content, 0, -1, 'UTF-8') . "..."; + break; + } + $content .= "\n"; + } + $content .= $l; + } + return $content; + } + + /** + * 海报图文件路径 + * @return string + */ + private function getPosterPath() + { + // 保存路径 + $tempPath = WEB_PATH . 'temp' . '/' . $this->goods['wxapp_id'] . '/'; + !is_dir($tempPath) && mkdir($tempPath, 0755, true); + return $tempPath . $this->getPosterName(); + } + + /** + * 海报图文件名称 + * @return string + */ + private function getPosterName() + { + return 'goods_' . md5("{$this->user_id}_{$this->goodsType}_{$this->goods['goods_id']}") . '.png'; + } + + /** + * 海报图url + * @return string + */ + private function getPosterUrl() + { + return \base_url() . 'temp/' . $this->goods['wxapp_id'] . '/' . $this->getPosterName() . '?t=' . time(); + } + + +} \ No newline at end of file diff --git a/source/application/common/service/qrcode/Poster.php b/source/application/common/service/qrcode/Poster.php new file mode 100644 index 0000000..1a4ad8f --- /dev/null +++ b/source/application/common/service/qrcode/Poster.php @@ -0,0 +1,175 @@ +dealer = $dealer; + // 分销商海报设置 + $this->config = Setting::getItem('qrcode', $dealer['wxapp_id']); + } + + /** + * 获取分销二维码 + * @return string + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function getImage() + { + if (file_exists($this->getPosterPath())) { + return $this->getPosterUrl(); + } + // 小程序id + $wxappId = $this->dealer['wxapp_id']; + // 1. 下载背景图 + $backdrop = $this->saveTempImage($wxappId, $this->config['backdrop']['src'], 'backdrop'); + // 2. 下载用户头像 + $avatarUrl = $this->saveTempImage($wxappId, $this->dealer['user']['avatarUrl'], 'avatar'); + // 3. 下载小程序码 + $qrcode = $this->saveQrcode($wxappId, 'uid:' . $this->dealer['user_id']); + // 4. 拼接海报图 + return $this->savePoster($backdrop, $avatarUrl, $qrcode); + } + + /** + * 海报图文件路径 + * @return string + */ + private function getPosterPath() + { + // 保存路径 + $tempPath = WEB_PATH . 'temp' . DS . $this->dealer['wxapp_id'] . DS; + !is_dir($tempPath) && mkdir($tempPath, 0755, true); + return $tempPath . $this->getPosterName(); + } + + /** + * 海报图文件名称 + * @return string + */ + private function getPosterName() + { + return md5('poster_' . $this->dealer['user_id']) . '.png'; + } + + /** + * 海报图url + * @return string + */ + private function getPosterUrl() + { + return \base_url() . 'temp/' . $this->dealer['wxapp_id'] . '/' . $this->getPosterName() . '?t=' . time(); + } + + /** + * 拼接海报图 + * @param $backdrop + * @param $avatarUrl + * @param $qrcode + * @return string + * @throws \Exception + */ + private function savePoster($backdrop, $avatarUrl, $qrcode) + { + // 实例化图像编辑器 + $editor = Grafika::createEditor(['Gd']); + // 打开海报背景图 + $editor->open($backdropImage, $backdrop); + // 生成圆形用户头像 + $this->config['avatar']['style'] === 'circle' && $this->circular($avatarUrl, $avatarUrl); + // 打开用户头像 + $editor->open($avatarImage, $avatarUrl); + // 重设用户头像宽高 + $avatarWidth = $this->config['avatar']['width'] * 2; + $editor->resizeExact($avatarImage, $avatarWidth, $avatarWidth); + // 用户头像添加到背景图 + $avatarX = $this->config['avatar']['left'] * 2; + $avatarY = $this->config['avatar']['top'] * 2; + $editor->blend($backdropImage, $avatarImage, 'normal', 1.0, 'top-left', $avatarX, $avatarY); + + // 生成圆形小程序码 + $this->config['qrcode']['style'] === 'circle' && $this->circular($qrcode, $qrcode); + // 打开小程序码 + $editor->open($qrcodeImage, $qrcode); + // 重设小程序码宽高 + $qrcodeWidth = $this->config['qrcode']['width'] * 2; + $editor->resizeExact($qrcodeImage, $qrcodeWidth, $qrcodeWidth); + // 小程序码添加到背景图 + $qrcodeX = $this->config['qrcode']['left'] * 2; + $qrcodeY = $this->config['qrcode']['top'] * 2; + $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', $qrcodeX, $qrcodeY); + + // 写入用户昵称 + $fontSize = $this->config['nickName']['fontSize'] * 2 * 0.76; + $fontX = $this->config['nickName']['left'] * 2; + $fontY = $this->config['nickName']['top'] * 2; + $Color = new Color($this->config['nickName']['color']); + $fontPath = Grafika::fontsDir() . DS . 'st-heiti-light.ttc'; + $editor->text($backdropImage, $this->dealer['user']['nickName'], $fontSize, $fontX, $fontY, $Color, $fontPath); + + // 保存图片 + $editor->save($backdropImage, $this->getPosterPath()); + return $this->getPosterUrl(); + } + + /** + * 生成圆形图片 + * @param static $imgpath 图片地址 + * @param string $saveName 保存文件名,默认空。 + */ + private function circular($imgpath, $saveName = '') + { + $srcImg = imagecreatefromstring(file_get_contents($imgpath)); + $w = imagesx($srcImg); + $h = imagesy($srcImg); + $w = $h = min($w, $h); + $newImg = imagecreatetruecolor($w, $h); + // 这一句一定要有 + imagesavealpha($newImg, true); + // 拾取一个完全透明的颜色,最后一个参数127为全透明 + $bg = imagecolorallocatealpha($newImg, 255, 255, 255, 127); + imagefill($newImg, 0, 0, $bg); + $r = $w / 2; //圆半径 + for ($x = 0; $x < $w; $x++) { + for ($y = 0; $y < $h; $y++) { + $rgbColor = imagecolorat($srcImg, $x, $y); + if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r))) { + imagesetpixel($newImg, $x, $y, $rgbColor); + } + } + } + // 输出图片到文件 + imagepng($newImg, $saveName); + // 释放空间 + imagedestroy($srcImg); + imagedestroy($newImg); + } + +} \ No newline at end of file diff --git a/source/application/common/service/qrcode/bargain/Goods.php b/source/application/common/service/qrcode/bargain/Goods.php new file mode 100644 index 0000000..221b13d --- /dev/null +++ b/source/application/common/service/qrcode/bargain/Goods.php @@ -0,0 +1,171 @@ +active = $active; + // 商品信息 + $this->goods = $goods; + // 当前用户id + $this->user_id = $user ? $user['user_id'] : 0; + } + + /** + * @return mixed + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function getImage() + { + // 判断海报图文件存在则直接返回url + if (file_exists($this->getPosterPath())) { + return $this->getPosterUrl(); + } + // 小程序id + $wxappId = $this->active['wxapp_id']; + // 商品海报背景图 + $backdrop = __DIR__ . '/../resource/goods_bg.png'; + // 下载商品首图 + $goodsUrl = $this->saveTempImage($wxappId, $this->goods['goods_image'], 'goods'); + // 小程序码参数 + $scene = "aid:{$this->active['active_id']},uid:" . ($this->user_id ?: ''); + // 下载小程序码 + $qrcode = $this->saveQrcode($wxappId, $scene, 'pages/bargain/goods/index'); + // 拼接海报图 + return $this->savePoster($backdrop, $goodsUrl, $qrcode); + } + + /** + * 拼接海报图 + * @param $backdrop + * @param $goodsUrl + * @param $qrcode + * @return string + * @throws \Exception + */ + private function savePoster($backdrop, $goodsUrl, $qrcode) + { + // 实例化图像编辑器 + $editor = Grafika::createEditor(['Gd']); + // 字体文件路径 + $fontPath = Grafika::fontsDir() . '/' . 'st-heiti-light.ttc'; + // 打开海报背景图 + $editor->open($backdropImage, $backdrop); + // 打开商品图片 + $editor->open($goodsImage, $goodsUrl); + // 重设商品图片宽高 + $editor->resizeExact($goodsImage, 690, 690); + // 商品图片添加到背景图 + $editor->blend($backdropImage, $goodsImage, 'normal', 1.0, 'top-left', 30, 30); + // 商品名称处理换行 + $fontSize = 30; + $goodsName = $this->wrapText($fontSize, 0, $fontPath, $this->goods['goods_name'], 680, 2); + // 写入商品名称 + $editor->text($backdropImage, $goodsName, $fontSize, 30, 750, new Color('#333333'), $fontPath); + // 写入商品价格 + $editor->text($backdropImage, $this->active['floor_price'], 38, 62, 964, new Color('#ff4444')); + // 打开小程序码 + $editor->open($qrcodeImage, $qrcode); + // 重设小程序码宽高 + $editor->resizeExact($qrcodeImage, 140, 140); + // 小程序码添加到背景图 + $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', 570, 914); + // 保存图片 + $editor->save($backdropImage, $this->getPosterPath()); + return $this->getPosterUrl(); + } + + /** + * 处理文字超出长度自动换行 + * @param integer $fontsize 字体大小 + * @param integer $angle 角度 + * @param string $fontface 字体名称 + * @param string $string 字符串 + * @param integer $width 预设宽度 + * @param null $max_line 最多行数 + * @return string + */ + private function wrapText($fontsize, $angle, $fontface, $string, $width, $max_line = null) + { + // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 + $content = ""; + // 将字符串拆分成一个个单字 保存到数组 letter 中 + $letter = []; + for ($i = 0; $i < mb_strlen($string, 'UTF-8'); $i++) { + $letter[] = mb_substr($string, $i, 1, 'UTF-8'); + } + $line_count = 0; + foreach ($letter as $l) { + $testbox = imagettfbbox($fontsize, $angle, $fontface, $content . ' ' . $l); + // 判断拼接后的字符串是否超过预设的宽度 + if (($testbox[2] > $width) && ($content !== "")) { + $line_count++; + if ($max_line && $line_count >= $max_line) { + $content = mb_substr($content, 0, -1, 'UTF-8') . "..."; + break; + } + $content .= "\n"; + } + $content .= $l; + } + return $content; + } + + /** + * 海报图文件路径 + * @return string + */ + private function getPosterPath() + { + // 保存路径 + $tempPath = WEB_PATH . 'temp' . '/' . $this->active['wxapp_id'] . '/'; + !is_dir($tempPath) && mkdir($tempPath, 0755, true); + return $tempPath . $this->getPosterName(); + } + + /** + * 海报图文件名称 + * @return string + */ + private function getPosterName() + { + return 'bargain_active_' . md5("{$this->user_id}_{$this->active['active_id']}") . '.png'; + } + + /** + * 海报图url + * @return string + */ + private function getPosterUrl() + { + return \base_url() . 'temp/' . $this->active['wxapp_id'] . '/' . $this->getPosterName() . '?t=' . time(); + } + +} \ No newline at end of file diff --git a/source/application/common/service/qrcode/resource/goods_bg.png b/source/application/common/service/qrcode/resource/goods_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..26eeb04284aae6d55ac51ec400d52703bfd6578f GIT binary patch literal 8980 zcmeHNXIN9&);?kv6%_;-iU^8=LI^#fM2CPPO+lpzNJ1!sDI@_x4ag`>U=(SFGJ_NW z1p`<>ib(GlPyr!=AP7kBJ>O=o&ggSLzw3Rj_j~S-$&Z|z!(Mx>z1RD`Yn^ilJfnYd z$F?7~K@hY<`xM#`f;dJYXai{r2e^VP)w%@^{8UX7su9ta>S|5KK__g87@UYU-r5dl zh_kkFbE?LvLeTmH4(Cj$CVIL^EDl5rvm(h5>o8MwTN5<(iT0GC&W9}|(4fy=>U z!UAlrY34nrIL^^n5(O+w5yynk!%NpBM=Cf zj4Vu6Rtk)eqPP*L)-)*sMeNfIXdDGgb|6t5hy;EuuDO5w;pYi^$6I0H)k#I0W9EIpi#)8DzimkaK zAvMT2Ybud^j!3-xX&2Af6RAXsJ&`1$p&@d_*nwa}bfrlAK3q=^sZF3ztqEA1Hd;*- zOd##xV1v}q(9}2~C$E8)Q8)pIYbyVUM$2j{D8uFDWlo%cBV<3>qKQ~%JdQy9WNY)M zE&8jrYe>M8fM+z0?BIg4(IOM^BEPQ~>G1VfPJDH~kG3{nkLAa&+QL9Eu(iB?mgi5G z0C?7Z{()L>@(1p51ORq2Ky{b#Ehz}vAE=E!agO$SnDv3OcV^|;1oxg(qQ0c9S465_ zl24Y{PlT3pMj~H5YG{i*EW{2{AMjfhD{R|*-vH?ndjVhTV<@?0M@h8L368DWG8+!^ zkPkfW^7mXBiS4Q`H(4t6$!euOx6Scr7>#f6>>3?Kq}tPH%9|mQ@Eh#c4F4bO*CGbX_!RiZWqfq}r~kFU z!7~0a{_kuLJg#lyKlA_P#(x(7_qG2rai6XK8~uNy{r3gVXZ!c1|24#ZKj3|1;y>d5 zljA@Af1~|31F!clT7KH(k9_!y7XL1-zb~ymvil>S{$sTIZ2e!x|0ONH>i;iV{!tb_ zX6&np`#P=vqUGnJ`<+GaOIUw3JKtEX{$2ZTq}5-v{5q}vs+M1h?l(5U-1t`D zqitoCa0h}q6JsO(^IiV3QiSpbNY^y0aQ3wgZ%2+@7Wg2@;DtL_ZGT8aKOt@JEBO?YGE?A+(> zPCj%fFFl>fH!xhcWBa?BT1rHOOyk8227u*4N!EVgj|75b@wPsv$V9lc-47zxuCrF2T?}Mhmo0^5EO3wV#)cXhO6sfEv&1G(Q4Jma`HFkfaba8k=jrU6sQW18 zzYf1C>8)DIJ1t>AhC1W_8ElQvff~>=p1G?DSBvc$g-<$27VNU(w8KF@<|b!}#H@cs zWe9vBAt4G13RW)nxzy*oU!)l=P(lR}&97cXrkI!%a_jM-6i1DL?0R(E2a)M4=juov%OU>mDIJQ8WhjH@d)p#Z;QWn?V7f<=gQ(t z+eBOZZ@>NK%hWAc%%i;DDX2`!SnjG&!mgJ&f3e6Dq2n99ItNhJkD2PPXCNrHvyOIl zcG}f!{)R*W+s62~h|1IiZ)EijdpzFa(PdLz&≤US3{Z3k|MXu$GPv0s8&SOlfK9 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 = $active; + // 商品信息 + $this->goods = $goods; + // 当前用户id + $this->user_id = $user ? $user['user_id'] : 0; + } + + /** + * @return mixed + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function getImage() + { + // 判断海报图文件存在则直接返回url + if (file_exists($this->getPosterPath())) { + return $this->getPosterUrl(); + } + // 小程序id + $wxappId = $this->active['wxapp_id']; + // 商品海报背景图 + $backdrop = __DIR__ . '/../resource/goods_bg.png'; + // 下载商品首图 + $goodsUrl = $this->saveTempImage($wxappId, $this->goods['goods_image'], 'goods'); + // 小程序码参数 + $scene = "aid:{$this->active['active_time_id']},gid:{$this->goods['sharp_goods_id']},uid:" . ($this->user_id ?: ''); + // 下载小程序码 + $qrcode = $this->saveQrcode($wxappId, $scene, 'pages/sharp/goods/index'); + // 拼接海报图 + return $this->savePoster($backdrop, $goodsUrl, $qrcode); + } + + /** + * 拼接海报图 + * @param $backdrop + * @param $goodsUrl + * @param $qrcode + * @return string + * @throws \Exception + */ + private function savePoster($backdrop, $goodsUrl, $qrcode) + { + // 实例化图像编辑器 + $editor = Grafika::createEditor(['Gd']); + // 字体文件路径 + $fontPath = Grafika::fontsDir() . '/' . 'st-heiti-light.ttc'; + // 打开海报背景图 + $editor->open($backdropImage, $backdrop); + // 打开商品图片 + $editor->open($goodsImage, $goodsUrl); + // 重设商品图片宽高 + $editor->resizeExact($goodsImage, 690, 690); + // 商品图片添加到背景图 + $editor->blend($backdropImage, $goodsImage, 'normal', 1.0, 'top-left', 30, 30); + // 商品名称处理换行 + $fontSize = 30; + $goodsName = $this->wrapText($fontSize, 0, $fontPath, $this->goods['goods_name'], 680, 2); + // 写入商品名称 + $editor->text($backdropImage, $goodsName, $fontSize, 30, 750, new Color('#333333'), $fontPath); + // 写入商品价格 + $editor->text($backdropImage, $this->goods['goods_sku']['seckill_price'], 38, 62, 964, new Color('#ff4444')); + // 打开小程序码 + $editor->open($qrcodeImage, $qrcode); + // 重设小程序码宽高 + $editor->resizeExact($qrcodeImage, 140, 140); + // 小程序码添加到背景图 + $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', 570, 914); + // 保存图片 + $editor->save($backdropImage, $this->getPosterPath()); + return $this->getPosterUrl(); + } + + /** + * 处理文字超出长度自动换行 + * @param integer $fontsize 字体大小 + * @param integer $angle 角度 + * @param string $fontface 字体名称 + * @param string $string 字符串 + * @param integer $width 预设宽度 + * @param null $max_line 最多行数 + * @return string + */ + private function wrapText($fontsize, $angle, $fontface, $string, $width, $max_line = null) + { + // 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度 + $content = ""; + // 将字符串拆分成一个个单字 保存到数组 letter 中 + $letter = []; + for ($i = 0; $i < mb_strlen($string, 'UTF-8'); $i++) { + $letter[] = mb_substr($string, $i, 1, 'UTF-8'); + } + $line_count = 0; + foreach ($letter as $l) { + $testbox = imagettfbbox($fontsize, $angle, $fontface, $content . ' ' . $l); + // 判断拼接后的字符串是否超过预设的宽度 + if (($testbox[2] > $width) && ($content !== "")) { + $line_count++; + if ($max_line && $line_count >= $max_line) { + $content = mb_substr($content, 0, -1, 'UTF-8') . "..."; + break; + } + $content .= "\n"; + } + $content .= $l; + } + return $content; + } + + /** + * 海报图文件路径 + * @return string + */ + private function getPosterPath() + { + // 保存路径 + $tempPath = WEB_PATH . 'temp' . '/' . $this->active['wxapp_id'] . '/'; + !is_dir($tempPath) && mkdir($tempPath, 0755, true); + return $tempPath . $this->getPosterName(); + } + + /** + * 海报图文件名称 + * @return string + */ + private function getPosterName() + { + return 'sharp_goods_' . md5("{$this->user_id}_{$this->active['active_time_id']}_{$this->goods['sharp_goods_id']}") . '.png'; + } + + /** + * 海报图url + * @return string + */ + private function getPosterUrl() + { + return \base_url() . 'temp/' . $this->active['wxapp_id'] . '/' . $this->getPosterName() . '?t=' . time(); + } + +} \ No newline at end of file diff --git a/source/application/common/service/wechat/wow/Order.php b/source/application/common/service/wechat/wow/Order.php new file mode 100644 index 0000000..9bacd4c --- /dev/null +++ b/source/application/common/service/wechat/wow/Order.php @@ -0,0 +1,364 @@ +wxappId = $wxappId; + $this->initApiDriver(); + } + + /** + * 导入好物圈订单信息 + * @param array|\think\Collection $orderList 订单列表 + * @param bool $isCheck 验证后台是否开启同步设置 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \Exception + */ + public function import($orderList, $isCheck = true) + { + // 判断是否开启同步设置 + $setting = SettingModel::getItem('basic', $this->wxappId); + if ($isCheck && $setting['is_order'] == false) { + return false; + } + // 整理订单列表 + $orderListParams = $this->getOrderList($orderList, true); + // 执行api请求 + $status = $this->ApiDriver->import($orderListParams); + if ($status == false) { + $this->error = $this->ApiDriver->getError(); + return $status; + } + // 新增好物圈订单记录 + $this->model()->add($this->wxappId, $orderList); + return $status; + } + + /** + * 更新好物圈订单信息 + * @param array|\think\Collection $orderList 订单列表 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \Exception + */ + public function update($orderList) + { + // 过滤不存在的订单列表 + $legalList = $this->getLegalOrderList($orderList); + if (empty($legalList)) { + return false; + } + // 整理订单列表 + $orderListParams = $this->getOrderList($legalList); + // 执行api请求 + $status = $this->ApiDriver->update($orderListParams); + if ($status == false) { + $this->error = $this->ApiDriver->getError(); + return $status; + } + // 更新好物圈订单记录 + $this->model()->edit($legalList); + return $status; + } + + /** + * 获取存在好物圈记录的订单列表 + * 用于过滤不存在好物圈同步记录的订单 + * @param array|\think\Collection $orderList 订单列表 + * @param int $orderType + * @return array|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getLegalOrderList($orderList, $orderType = OrderTypeEnum::MASTER) + { + // 把order_id设置为key + $orderList = helper::arrayColumn2Key($orderList, 'order_id'); + // 查询出合法的id集 + $legalOrderList = $this->model()->getListByOrderIds(array_keys($orderList), $orderType); + // 遍历合法的订单信息 + $legalList = []; + foreach ($legalOrderList as $item) { + $legalList[$item['id']] = $orderList[$item['order_id']]; + } + return $legalList; + } + + /** + * 删除好物圈订单记录 + * @param array $id 订单同步记录id + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function delete($id) + { + // 实例化模型 + $model = $this->model($id, ['user']); + // 执行api请求 + $status = $this->ApiDriver->delete($model['user']['open_id'], $model['order_id']); + if ($status == false) { + $this->error = $this->ApiDriver->getError(); + } + // 删除订单记录 + $model->setDelete(); + return true; + } + + /** + * 返回错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + + /** + * 返回商城id + * @return mixed + */ + public function getWxappId() + { + return $this->wxappId; + } + + /** + * 实例化微信api驱动 + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function initApiDriver() + { + $config = WxappModel::getWxappCache($this->wxappId); + $this->ApiDriver = new WowOrder($config['app_id'], $config['app_secret']); + } + + /** + * 获取好物圈订单记录模型 + * @param int|null $id + * @param array $with + * @return OrderModel|null + * @throws \think\exception\DbException + */ + private function model($id = null, $with = ['user']) + { + static $model; + if (!$model instanceof OrderModel) { + $model = $id > 0 ? OrderModel::detail($id, $with) : (new OrderModel); + } + return $model; + } + + /** + * 整理订单列表 (用于添加好物圈接口) + * @param $orderList + * @param bool $isCreate 是否为创建新订单 + * @return array + */ + private function getOrderList($orderList, $isCreate = false) + { + // 整理api参数 + $data = []; + foreach ($orderList as $order) { + // 商品列表 + $goodsList = $this->getProductList($order['goods']); + // 订单记录 + $item = [ + 'order_id' => $order['order_id'], + 'trans_id' => $order['transaction_id'], // 微信支付交易单号 + 'status' => self::getStatusByOrder($order), // 订单状态,3:支付完成 4:已发货 5:已退款 100: 已完成 + 'ext_info' => [ + 'user_open_id' => $order['user']['open_id'], + 'order_detail_page' => [ + 'path' => "pages/order/detail?order_id={$order['order_id']}" + ], + ], + ]; + + // 用于更新订单的参数 + if ($isCreate == false) { + + // 快递及包裹信息 + // 条件1:配送方式为快递配送 + // 条件2: 订单已发货 + if ( + $order['delivery_type']['value'] == DeliveryTypeEnum::EXPRESS + && $order['delivery_status']['value'] == 20 + ) { + $item['ext_info']['express_info']['express_package_info_list'] = [[ + 'express_company_id' => $order['express']['express_id'], // 快递公司id + 'express_company_name' => $order['express']['express_name'], // 快递公司名 + 'express_code' => $order['express_no'], // 快递单号 + 'ship_time' => $order['delivery_time'], // 发货时间 + 'express_page' => [ + 'path' => "pages/order/detail?order_id={$order['order_id']}" + ], + 'express_goods_info_list' => helper::getArrayColumns($goodsList, ['item_code', 'sku_id']) + ]]; + } + + } + + // 用于新增订单的参数 + if ($isCreate == true) { + // 订单创建时间 + $item['create_time'] = $order['create_time']; + // 支付完成时间 + $item['pay_finish_time'] = $order['pay_time']; + // 订单金额,单位:分 + $item['fee'] = $order['pay_price'] * 100; + // 订单支付方式,0:未知方式 1:微信支付 2:其他支付方式 + $item['ext_info']['payment_method'] = $order['pay_type']['value'] == PayTypeEnum::WECHAT ? 1 : 2; + // 商品列表 + $item['ext_info']['product_info'] = ['item_list' => $goodsList]; + // 收件人信息 + $item['ext_info']['express_info'] = array_merge( + $this->getAddressInfo($order['delivery_type']['value'], $order['address']), + ['price' => $order['express_price'] * 100] // 运费 + ); + // todo: 商家信息 + $item['ext_info']['brand_info'] = [ + 'phone' => '020-666666', // 必填:联系电话 + 'contact_detail_page' => [ + 'kf_type' => 3, + 'path' => 'pages/index/index', + ], + ]; + } + $data[] = $item; + } + return $data; + } + + /** + * 整理订单状态码 + * 订单状态,3:支付完成 4:已发货 5:已退款 100: 已完成 + * @param array $order + * @return bool|int + */ + public static function getStatusByOrder($order) + { + // 未付款 + if ($order['pay_status']['value'] != 20) { + return (int)false; + } + // 已退款 + if ($order['order_status']['value'] == 20) { + return 5; + } + // 已完成 + if ($order['order_status']['value'] == 30) { + return 100; + } + // 支付完成(未发货) + if ($order['delivery_status']['value'] == 10) { + return 3; + } + // 已发货 + if ($order['delivery_status']['value'] == 20) { + return 4; + } + return (int)false; + } + + /** + * 订单状态,3:支付完成 4:已发货 5:已退款 100: 已完成 + * @return array + */ + public static function status() + { + return [ + 0 => '未知', + 3 => '支付完成', + 4 => '已发货', + 5 => '已退款', + 100 => '已完成', + ]; + } + + /** + * 整理商品列表 + * @param array $goodsList + * @return array + */ + private function getProductList($goodsList) + { + $data = []; + foreach ($goodsList as $goods) { + $data[] = [ + 'item_code' => $goods['goods_id'], // 物品id,要求appid下全局唯一 + 'sku_id' => $goods['goods_id'], + 'amount' => $goods['total_num'], // 物品数量 + 'total_fee' => $goods['total_price'] * 100, // 物品总价,单位:分 + 'thumb_url' => $goods['image']['file_path'], + 'title' => $goods['goods_name'], // 商品名称 + 'unit_price' => $goods['goods_price'] * 100, // 物品单价(实际售价) + 'original_price' => $goods['line_price'] * 100, // 物品原价 + 'category_list' => ['商品分类'], // todo: 商品分类 + 'item_detail_page' => ['path' => "pages/goods/index?goods_id={$goods['goods_id']}"], + ]; + } + return $data; + } + + /** + * 整理收件人信息 + * @param int $deliveryType + * @param array $express + * @return array + */ + private function getAddressInfo($deliveryType, $express) + { + // 快递信息 + $data = []; + if ($deliveryType == DeliveryTypeEnum::EXPRESS) { + $data = [ + 'name' => $express['name'], // 收件人姓名 + 'phone' => $express['phone'], // 收件人联系电话 + 'province' => $express['region']['province'], // 省 + 'city' => $express['region']['city'], // 市 + 'district' => $express['region']['region'], // 区 + ]; + // 详细地址 + $data['address'] = "{$data['province']} {$data['city']} {$data['district']} {$express['detail']}"; + } + return $data; + } + +} \ No newline at end of file diff --git a/source/application/common/service/wechat/wow/Shoping.php b/source/application/common/service/wechat/wow/Shoping.php new file mode 100644 index 0000000..872581a --- /dev/null +++ b/source/application/common/service/wechat/wow/Shoping.php @@ -0,0 +1,163 @@ +wxappId = $wxappId; + $this->initApiDriver(); + } + + /** + * 添加好物圈商品收藏 + * @param \think\Collection $user 用户信息 + * @param array $goodsList 商品列表 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function add($user, $goodsList) + { + // 判断是否开启同步设置 + $setting = SettingModel::getItem('basic', $this->wxappId); + if ($setting['is_shopping'] == false) { + return false; + } + // 整理商品列表 + $productList = $this->getProductListToAdd($goodsList); + // 执行api请求 + $status = $this->ApiDriver->addList($user['open_id'], $productList); + if ($status == false) { + $this->error = $this->ApiDriver->getError(); + return $status; + } + // 写入商品收藏记录 + $goodsIds = helper::getArrayColumn($goodsList, 'goods_id'); + $this->model()->add($user['user_id'], $goodsIds); + return $status; + } + + /** + * 删除好物圈商品收藏 + * @param $id + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function delete($id) + { + // 实例化模型 + $model = $this->model($id, ['user']); + // 执行api请求 + $status = $this->ApiDriver->delete($model['user']['open_id'], [[ + 'item_code' => $model['goods_id'], + 'sku_id' => $model['goods_id'], + ]]); + if ($status == false) { + $this->error = $this->ApiDriver->getError(); + } + // 删除商品收藏记录 + $model->setDelete(); + return true; + } + + /** + * 返回错误信息 + * @return mixed + */ + public function getError() + { + return $this->error; + } + + /** + * 实例化微信api驱动 + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function initApiDriver() + { + $config = WxappModel::getWxappCache($this->wxappId); + $this->ApiDriver = new WowShoping($config['app_id'], $config['app_secret']); + } + + /** + * 获取好物圈订单记录模型 + * @param int|null $id + * @param array $with + * @return ShopingModel|null + * @throws \think\exception\DbException + */ + private function model($id = null, $with = ['user']) + { + static $model; + if (!$model instanceof ShopingModel) { + $model = $id > 0 ? ShopingModel::detail($id, $with) : (new ShopingModel); + } + return $model; + } + + /** + * 整理商品列表 (用于添加收藏接口) + * @param $goodsList + * @return array + */ + private function getProductListToAdd(&$goodsList) + { + // 整理api参数 + $productList = []; + foreach ($goodsList as $goods) { + $imageList = []; // 商品图片 + foreach ($goods['image'] as $image) { + $imageList[] = $image['file_path']; + } + // sku信息 + $skuInfo = &$goods['sku'][0]; + $productList[] = [ + 'item_code' => $goods['goods_id'], + 'title' => $goods['goods_name'], + 'category_list' => [$goods['category']['name']], + 'image_list' => $imageList, + 'src_wxapp_path' => "/pages/goods/index?goods_id={$goods['goods_id']}", // 商品页面路径 + 'sku_info' => [ // 商品sku +// 'sku_id' => "{$goods['goods_id']}_{$skuInfo['spec_sku_id']}", + 'sku_id' => $goods['goods_id'], + 'price' => $skuInfo['goods_price'] * 100, + 'original_price' => $skuInfo['line_price'] * 100, // 划线价 + 'status' => 1, + ], + ]; + } + return $productList; + } + +} \ No newline at end of file diff --git a/source/application/common/service/wxapp/FormId.php b/source/application/common/service/wxapp/FormId.php new file mode 100644 index 0000000..957ffef --- /dev/null +++ b/source/application/common/service/wxapp/FormId.php @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- + +return [ + // +---------------------------------------------------------------------- + // | 应用设置 + // +---------------------------------------------------------------------- + + // 应用调试模式 + 'app_debug' => false, + // 应用Trace + 'app_trace' => false, + // 应用模式状态 + 'app_status' => '', + // 是否支持多模块 + 'app_multi_module' => true, + // 入口自动绑定模块 + 'auto_bind_module' => false, + // 注册的根命名空间 + 'root_namespace' => [], + // 扩展函数文件 + 'extra_file_list' => [THINK_PATH . 'helper' . EXT], + // 默认输出类型 + 'default_return_type' => 'json', + // 默认AJAX 数据返回格式,可选json xml ... + 'default_ajax_return' => 'json', + // 默认JSONP格式返回的处理方法 + 'default_jsonp_handler' => 'jsonpReturn', + // 默认JSONP处理方法 + 'var_jsonp_handler' => 'callback', + // 默认时区 + 'default_timezone' => 'PRC', + // 是否开启多语言 + 'lang_switch_on' => false, + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => 'htmlspecialchars', + // 默认语言 + 'default_lang' => 'zh-cn', + // 应用类库后缀 + 'class_suffix' => false, + // 控制器类后缀 + 'controller_suffix' => false, + + // +---------------------------------------------------------------------- + // | 模块设置 + // +---------------------------------------------------------------------- + + // 默认模块名 + 'default_module' => 'store', + // 禁止访问模块 + 'deny_module_list' => ['common'], + // 默认控制器名 + 'default_controller' => 'Index', + // 默认操作名 + 'default_action' => 'index', + // 默认验证器 + 'default_validate' => '', + // 默认的空控制器名 + 'empty_controller' => 'Error', + // 操作方法后缀 + 'action_suffix' => '', + // 自动搜索控制器 + 'controller_auto_search' => false, + + // +---------------------------------------------------------------------- + // | URL设置 + // +---------------------------------------------------------------------- + + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // pathinfo分隔符 + 'pathinfo_depr' => '/', + // URL伪静态后缀 + 'url_html_suffix' => '', + // URL普通方式参数 用于自动生成 + 'url_common_param' => false, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 0, + // 是否开启路由 + 'url_route_on' => true, + // 路由使用完整匹配 + 'route_complete_match' => false, + // 路由配置文件(支持配置多个) + 'route_config_file' => ['route'], + // 是否强制使用路由 + 'url_route_must' => false, + // 域名部署 + 'url_domain_deploy' => false, + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // 是否自动转换URL中的控制器和操作名 + 'url_convert' => true, + // 默认的访问控制器层 + 'url_controller_layer' => 'controller', + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, + // 全局请求缓存排除规则 + 'request_cache_except' => [], + + // +---------------------------------------------------------------------- + // | 模板设置 + // +---------------------------------------------------------------------- + + 'template' => [ + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'Think', + // 模板路径 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'html', + // 模板文件名分隔符 + 'view_depr' => DS, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}', + // 标签库标签开始标记 + 'taglib_begin' => '{', + // 标签库标签结束标记 + 'taglib_end' => '}', + ], + + // 视图输出字符串内容替换 + 'view_replace_str' => [], + // 默认跳转页面对应的模板文件 + 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + + // +---------------------------------------------------------------------- + // | 异常及错误设置 + // +---------------------------------------------------------------------- + + // 异常页面的模板文件 + 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', + + // 错误显示信息,非调试模式有效 + 'error_message' => '页面错误!请稍后再试~', + // 显示错误信息 + 'show_error_msg' => false, + // 异常处理handle类 留空使用 \think\exception\Handle + 'exception_handle' => '\\app\\common\\exception\\ExceptionHandler', + + // +---------------------------------------------------------------------- + // | 日志设置 + // +---------------------------------------------------------------------- + + 'log' => [ + // 日志记录方式,内置 file socket 支持扩展 + 'type' => 'File', + // 日志保存目录 + 'path' => LOG_PATH, + // 日志记录级别 + 'level' => [], + // error和sql日志单独记录 + 'apart_level' => ['begin', 'error', 'sql', 'yoshop-info'], + ], + + // +---------------------------------------------------------------------- + // | Trace设置 开启 app_trace 后 有效 + // +---------------------------------------------------------------------- + 'trace' => [ + // 内置Html Console 支持扩展 + 'type' => 'Html', + ], + + // +---------------------------------------------------------------------- + // | 缓存设置 + // +---------------------------------------------------------------------- + + 'cache' => [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => CACHE_PATH, + // 缓存前缀 + 'prefix' => '', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + ], + + // +---------------------------------------------------------------------- + // | 会话设置 + // +---------------------------------------------------------------------- + + 'session' => [ + 'id' => '', + // SESSION_ID的提交变量,解决flash上传跨域 + 'var_session_id' => '', + // SESSION 前缀 + 'prefix' => '', + // 驱动方式 支持redis memcache memcached + 'type' => '', + // 是否自动开启 SESSION + 'auto_start' => true, + ], + + // +---------------------------------------------------------------------- + // | Cookie设置 + // +---------------------------------------------------------------------- + 'cookie' => [ + // cookie 名称前缀 + 'prefix' => '', + // cookie 保存时间 + 'expire' => 0, + // cookie 保存路径 + 'path' => '/', + // cookie 有效域名 + 'domain' => '', + // cookie 启用安全传输 + 'secure' => false, + // httponly设置 + 'httponly' => '', + // 是否使用 setcookie + 'setcookie' => true, + ], + + //分页配置 + 'paginate' => [ + 'type' => 'bootstrap', + 'var_page' => 'page', + 'list_rows' => 15, + ], +]; diff --git a/source/application/database.php b/source/application/database.php new file mode 100644 index 0000000..bede4b2 --- /dev/null +++ b/source/application/database.php @@ -0,0 +1,54 @@ + '127.0.0.1', + 'database' => 'yoshop_pro', + 'username' => 'root', + 'password' => 'root', + 'port' => '3306', + 'charset' => 'utf8', +]; + +return [ + // 数据库类型 + 'type' => 'mysql', + // 服务器地址 + 'hostname' => $config['host'], + // 数据库名 + 'database' => $config['database'], + // 用户名 + 'username' => $config['username'], + // 密码 + 'password' => $config['password'], + // 端口 + 'hostport' => $config['port'], + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => $config['charset'], + // 数据库表前缀 + 'prefix' => 'yoshop_', + // 数据库调试模式 + 'debug' => true, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 数据集返回类型 + 'resultset_type' => 'collection', + // 自动写入时间戳字段 + 'auto_timestamp' => true, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 是否需要进行SQL性能分析 + 'sql_explain' => false, +]; diff --git a/source/application/route.php b/source/application/route.php new file mode 100644 index 0000000..b46396b --- /dev/null +++ b/source/application/route.php @@ -0,0 +1,15 @@ + [ + 'name' => '\w+', + ], + '[hello]' => [ + ':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']], + ':name' => ['index/hello', ['method' => 'post']], + ], + +]; diff --git a/source/application/store/common.php b/source/application/store/common.php new file mode 100644 index 0000000..c6b068c --- /dev/null +++ b/source/application/store/common.php @@ -0,0 +1,20 @@ +checkPrivilege($url, $strict); + } catch (\Exception $e) { + return false; + } +} diff --git a/source/application/store/config.php b/source/application/store/config.php new file mode 100644 index 0000000..6ef0708 --- /dev/null +++ b/source/application/store/config.php @@ -0,0 +1,37 @@ + 'html', + + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => 'trim,htmlspecialchars', + + // 模板设置 + 'template' => [ + // layout布局 + 'layout_on' => true, + 'layout_name' => 'layouts/layout', + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'think', + // 模板路径 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'php', + // 模板文件名分隔符 + 'view_depr' => DS, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}}', + // 标签库标签开始标记 + 'taglib_begin' => '{{', + // 标签库标签结束标记 + 'taglib_end' => '}}', + ], + + // 默认跳转页面对应的模板文件 + 'dispatch_error_tmpl' => 'layouts/error', + +]; diff --git a/source/application/store/controller/Controller.php b/source/application/store/controller/Controller.php new file mode 100644 index 0000000..2986356 --- /dev/null +++ b/source/application/store/controller/Controller.php @@ -0,0 +1,230 @@ +store = Session::get('yoshop_store'); + // 当前路由信息 + $this->getRouteinfo(); + // 验证登录状态 + $this->checkLogin(); + // 验证当前页面权限 + $this->checkPrivilege(); + // 全局layout + $this->layout(); + } + + /** + * 验证当前页面权限 + * @throws BaseException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function checkPrivilege() + { + if ($this->routeUri === 'index/index') { + return true; + } + if (!Auth::getInstance()->checkPrivilege($this->routeUri)) { + throw new BaseException(['msg' => '很抱歉,没有访问权限']); + } + return true; + } + + /** + * 全局layout模板输出 + * @throws \think\exception\DbException + * @throws \Exception + */ + private function layout() + { + // 验证当前请求是否在白名单 + if (!in_array($this->routeUri, $this->notLayoutAction)) { + // 输出到view + $this->assign([ + 'base_url' => base_url(), // 当前域名 + 'store_url' => url('/store'), // 后台模块url + 'group' => $this->group, // 当前控制器分组 + 'menus' => $this->menus(), // 后台菜单 + 'store' => $this->store, // 商家登录信息 + 'setting' => Setting::getAll() ?: null, // 当前商城设置 + 'request' => Request::instance(), // Request对象 + 'version' => get_version(), // 系统版本号 + ]); + } + } + + /** + * 解析当前路由参数 (分组名称、控制器名称、方法名) + */ + protected function getRouteinfo() + { + // 控制器名称 + $this->controller = toUnderScore($this->request->controller()); + // 方法名称 + $this->action = $this->request->action(); + // 控制器分组 (用于定义所属模块) + $groupstr = strstr($this->controller, '.', true); + $this->group = $groupstr !== false ? $groupstr : $this->controller; + // 当前uri + $this->routeUri = $this->controller . '/' . $this->action; + } + + /** + * 后台菜单配置 + * @return mixed + * @throws \think\exception\DbException + */ + protected function menus() + { + static $menus = []; + if (empty($menus)) { + $menus = Menus::getInstance()->getMenus($this->routeUri, $this->group); + } + return $menus; + } + + /** + * 验证登录状态 + * @return bool + */ + private function checkLogin() + { + // 验证当前请求是否在白名单 + if (in_array($this->routeUri, $this->allowAllAction)) { + return true; + } + // 验证登录状态 + if (empty($this->store) + || (int)$this->store['is_login'] !== 1 + || !isset($this->store['wxapp']) + || empty($this->store['wxapp']) + ) { + $this->redirect('passport/login'); + return false; + } + return true; + } + + /** + * 获取当前wxapp_id + */ + protected function getWxappId() + { + return $this->store['wxapp']['wxapp_id']; + } + + /** + * 返回封装后的 API 数据到客户端 + * @param int $code + * @param string $msg + * @param string $url + * @param array $data + * @return array + */ + protected function renderJson($code = 1, $msg = '', $url = '', $data = []) + { + return compact('code', 'msg', 'url', 'data'); + } + + /** + * 返回操作成功json + * @param string $msg + * @param string $url + * @param array $data + * @return array + */ + protected function renderSuccess($msg = 'success', $url = '', $data = []) + { + return $this->renderJson(1, $msg, $url, $data); + } + + /** + * 返回操作失败json + * @param string $msg + * @param string $url + * @param array $data + * @return array|bool + */ + protected function renderError($msg = 'error', $url = '', $data = []) + { + if ($this->request->isAjax()) { + return $this->renderJson(0, $msg, $url, $data); + } + $this->error($msg); + return false; + } + + /** + * 获取post数据 (数组) + * @param $key + * @return mixed + */ + protected function postData($key = null) + { + return $this->request->post(is_null($key) ? '' : $key . '/a'); + } + + /** + * 获取post数据 (数组) + * @param $key + * @return mixed + */ + protected function getData($key = null) + { + return $this->request->get(is_null($key) ? '' : $key); + } + +} diff --git a/source/application/store/controller/Goods.php b/source/application/store/controller/Goods.php new file mode 100644 index 0000000..ae74845 --- /dev/null +++ b/source/application/store/controller/Goods.php @@ -0,0 +1,127 @@ +getList(array_merge(['status' => -1], $this->request->param())); + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + return $this->fetch('index', compact('list', 'catgory')); + } + + /** + * 添加商品 + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch( + 'add', + array_merge(GoodsService::getEditData(null, 'add'), []) + ); + } + $model = new GoodsModel; + if ($model->add($this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 一键复制 + * @param $goods_id + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function copy($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$this->request->isAjax()) { + return $this->fetch( + 'edit', + array_merge(GoodsService::getEditData($model, 'copy'), compact('model')) + ); + } + $model = new GoodsModel; + if ($model->add($this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 商品编辑 + * @param $goods_id + * @return array|bool|mixed + */ + public function edit($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$this->request->isAjax()) { + return $this->fetch( + 'edit', + array_merge(GoodsService::getEditData($model), compact('model')) + ); + } + // 更新记录 + if ($model->edit($this->postData('goods'))) { + return $this->renderSuccess('更新成功', url('goods/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 修改商品状态 + * @param $goods_id + * @param boolean $state + * @return array + */ + public function state($goods_id, $state) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$model->setStatus($state)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除商品 + * @param $goods_id + * @return array + */ + public function delete($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/Index.php b/source/application/store/controller/Index.php new file mode 100644 index 0000000..75f9025 --- /dev/null +++ b/source/application/store/controller/Index.php @@ -0,0 +1,32 @@ +menus(); + $url = current(array_values($menus))['index']; + if ($url !== 'index/index') { + $this->redirect($url); + } + $model = new StoreModel; + return $this->fetch('index', ['data' => $model->getHomeData()]); + } + +} diff --git a/source/application/store/controller/Order.php b/source/application/store/controller/Order.php new file mode 100644 index 0000000..c1c4123 --- /dev/null +++ b/source/application/store/controller/Order.php @@ -0,0 +1,147 @@ +getList('待发货订单列表', 'delivery'); + } + + /** + * 待收货订单列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function receipt_list() + { + return $this->getList('待收货订单列表', 'receipt'); + } + + /** + * 待付款订单列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function pay_list() + { + return $this->getList('待付款订单列表', 'pay'); + } + + /** + * 已完成订单列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function complete_list() + { + return $this->getList('已完成订单列表', 'complete'); + } + + /** + * 已取消订单列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function cancel_list() + { + return $this->getList('已取消订单列表', 'cancel'); + } + + /** + * 全部订单列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function all_list() + { + return $this->getList('全部订单列表', 'all'); + } + + /** + * 订单详情 + * @param $order_id + * @return mixed + * @throws \think\exception\DbException + */ + public function detail($order_id) + { + // 订单详情 + $detail = OrderModel::detail($order_id); + // 物流公司列表 + $expressList = ExpressModel::getAll(); + // 门店店员列表 + $shopClerkList = (new ShopClerkModel)->getList(true); + return $this->fetch('detail', compact( + 'detail', + 'expressList', + 'shopClerkList' + )); + } + + /** + * 确认发货 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function delivery($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->delivery($this->postData('order'))) { + return $this->renderSuccess('发货成功'); + } + return $this->renderError($model->getError() ?: '发货失败'); + } + + /** + * 修改订单价格 + * @param $order_id + * @return array + * @throws \think\exception\DbException + */ + public function updatePrice($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->updatePrice($this->postData('order'))) { + return $this->renderSuccess('修改成功'); + } + return $this->renderError($model->getError() ?: '修改失败'); + } + + /** + * 订单列表 + * @param string $title + * @param string $dataType + * @return mixed + * @throws \think\exception\DbException + */ + private function getList($title, $dataType) + { + // 订单列表 + $model = new OrderModel; + $list = $model->getList($dataType, $this->request->param()); + // 自提门店列表 + $shopList = ShopModel::getAllList(); + return $this->fetch('index', compact('title', 'dataType', 'list', 'shopList')); + } + +} diff --git a/source/application/store/controller/Passport.php b/source/application/store/controller/Passport.php new file mode 100644 index 0000000..f6c8bb9 --- /dev/null +++ b/source/application/store/controller/Passport.php @@ -0,0 +1,48 @@ +request->isAjax()) { + $model = new StoreUser; + if ($model->login($this->postData('User'))) { + return $this->renderSuccess('登录成功', url('index/index')); + } + return $this->renderError($model->getError() ?: '登录失败'); + } + $this->view->engine->layout(false); + return $this->fetch('login', [ + // 系统版本号 + 'version' => get_version() + ]); + } + + /** + * 退出登录 + */ + public function logout() + { + Session::clear('yoshop_store'); + $this->redirect('passport/login'); + } + +} diff --git a/source/application/store/controller/Setting.php b/source/application/store/controller/Setting.php new file mode 100644 index 0000000..1367206 --- /dev/null +++ b/source/application/store/controller/Setting.php @@ -0,0 +1,132 @@ +updateEvent('store'); + } + + /** + * 交易设置 + * @return mixed + * @throws \think\exception\DbException + */ + public function trade() + { + return $this->updateEvent('trade'); + } + + /** + * 短信通知 + * @return mixed + * @throws \think\exception\DbException + */ + public function sms() + { + return $this->updateEvent('sms'); + } + + /** + * 模板消息 + * @return mixed + * @throws \think\exception\DbException + */ + public function tplMsg() + { + return $this->updateEvent('tplMsg'); + } + + /** + * 发送短信通知测试 + * @param $AccessKeyId + * @param $AccessKeySecret + * @param $sign + * @param $msg_type + * @param $template_code + * @param $accept_phone + * @return array + * @throws \think\Exception + */ + public function smsTest($AccessKeyId, $AccessKeySecret, $sign, $msg_type, $template_code, $accept_phone) + { + $SmsDriver = new SmsDriver([ + 'default' => 'aliyun', + 'engine' => [ + 'aliyun' => [ + 'AccessKeyId' => $AccessKeyId, + 'AccessKeySecret' => $AccessKeySecret, + 'sign' => $sign, + $msg_type => compact('template_code', 'accept_phone'), + ], + ], + ]); + $templateParams = []; + if ($msg_type === 'order_pay') { + $templateParams = ['order_no' => '2018071200000000']; + } + if ($SmsDriver->sendSms($msg_type, $templateParams, true)) { + return $this->renderSuccess('发送成功'); + } + return $this->renderError('发送失败 ' . $SmsDriver->getError()); + } + + /** + * 上传设置 + * @return mixed + * @throws \think\exception\DbException + */ + public function storage() + { + return $this->updateEvent('storage'); + } + + /** + * 小票打印设置 + * @return mixed + * @throws \think\exception\DbException + */ + public function printer() + { + // 获取打印机列表 + $printerList = PrinterModel::getAll(); + return $this->updateEvent('printer', compact('printerList')); + } + + /** + * 更新商城设置事件 + * @param $key + * @param $vars + * @return array|mixed + * @throws \think\exception\DbException + */ + private function updateEvent($key, $vars = []) + { + if (!$this->request->isAjax()) { + $vars['values'] = SettingModel::getItem($key); + return $this->fetch($key, $vars); + } + $model = new SettingModel; + if ($model->edit($key, $this->postData($key))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} diff --git a/source/application/store/controller/Shop.php b/source/application/store/controller/Shop.php new file mode 100644 index 0000000..9c8410d --- /dev/null +++ b/source/application/store/controller/Shop.php @@ -0,0 +1,90 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 腾讯地图坐标选取器 + * @return mixed + */ + public function getpoint() + { + $this->view->engine->layout(false); + return $this->fetch('getpoint'); + } + + /** + * 添加门店 + * @return array|bool|mixed + * @throws \Exception + */ + public function add() + { + $model = new ShopModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('shop'))) { + return $this->renderSuccess('添加成功', url('shop/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑门店 + * @param $shop_id + * @return array|bool|mixed + * @throws \think\exception\DbException + */ + public function edit($shop_id) + { + // 门店详情 + $model = ShopModel::detail($shop_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 新增记录 + if ($model->edit($this->postData('shop'))) { + return $this->renderSuccess('更新成功', url('shop/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除门店 + * @param $shop_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($shop_id) + { + // 门店详情 + $model = ShopModel::detail($shop_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/Upload.php b/source/application/store/controller/Upload.php new file mode 100644 index 0000000..e5484d8 --- /dev/null +++ b/source/application/store/controller/Upload.php @@ -0,0 +1,88 @@ +config = SettingModel::getItem('storage'); + } + + /** + * 图片上传接口 + * @param int $group_id + * @return array + * @throws \think\Exception + */ + public function image($group_id = -1) + { + // 实例化存储驱动 + $StorageDriver = new StorageDriver($this->config); + // 设置上传文件的信息 + $StorageDriver->setUploadFile('iFile'); + // 上传图片 + if (!$StorageDriver->upload()) { + return json(['code' => 0, 'msg' => '图片上传失败' . $StorageDriver->getError()]); + } + + // 图片上传路径 + $fileName = $StorageDriver->getFileName(); + // 图片信息 + $fileInfo = $StorageDriver->getFileInfo(); + // 添加文件库记录 + $uploadFile = $this->addUploadFile($group_id, $fileName, $fileInfo, 'image'); + // 图片上传成功 + return json(['code' => 1, 'msg' => '图片上传成功', 'data' => $uploadFile]); + } + + /** + * 添加文件库上传记录 + * @param $group_id + * @param $fileName + * @param $fileInfo + * @param $fileType + * @return UploadFile + */ + private function addUploadFile($group_id, $fileName, $fileInfo, $fileType) + { + // 存储引擎 + $storage = $this->config['default']; + // 存储域名 + $fileUrl = isset($this->config['engine'][$storage]['domain']) + ? $this->config['engine'][$storage]['domain'] : ''; + // 添加文件库记录 + $model = new UploadFile; + $model->add([ + 'group_id' => $group_id > 0 ? (int)$group_id : 0, + 'storage' => $storage, + 'file_url' => $fileUrl, + 'file_name' => $fileName, + 'file_size' => $fileInfo['size'], + 'file_type' => $fileType, + 'extension' => pathinfo($fileInfo['name'], PATHINFO_EXTENSION), + ]); + return $model; + } + +} diff --git a/source/application/store/controller/User.php b/source/application/store/controller/User.php new file mode 100644 index 0000000..f10cef9 --- /dev/null +++ b/source/application/store/controller/User.php @@ -0,0 +1,83 @@ +getList($nickName, $gender, $grade); + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + return $this->fetch('index', compact('list', 'gradeList')); + } + + /** + * 删除用户 + * @param $user_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($user_id) + { + // 用户详情 + $model = UserModel::detail($user_id); + if ($model->setDelete()) { + return $this->renderSuccess('删除成功'); + } + return $this->renderError($model->getError() ?: '删除失败'); + } + + /** + * 用户充值 + * @param $user_id + * @param int $source 充值类型 + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function recharge($user_id, $source) + { + // 用户详情 + $model = UserModel::detail($user_id); + if ($model->recharge($this->store['user']['user_name'], $source, $this->postData('recharge'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 修改会员等级 + * @param $user_id + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function grade($user_id) + { + // 用户详情 + $model = UserModel::detail($user_id); + if ($model->updateGrade($this->postData('grade'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} diff --git a/source/application/store/controller/Wxapp.php b/source/application/store/controller/Wxapp.php new file mode 100644 index 0000000..b752b99 --- /dev/null +++ b/source/application/store/controller/Wxapp.php @@ -0,0 +1,52 @@ +request->isAjax()) { + return $this->fetch('setting', compact('model')); + } + // 更新小程序设置 + if ($model->edit($this->postData('wxapp'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 导航栏设置 + * @return array|mixed + * @throws \think\exception\DbException + */ + public function tabbar() + { + $model = WxappNavbarModel::detail(); + if (!$this->request->isAjax()) { + return $this->fetch('tabbar', compact('model')); + } + $data = $this->postData('tabbar'); + if (!$model->edit($data)) { + return $this->renderError('更新失败'); + } + return $this->renderSuccess('更新成功'); + } + +} diff --git a/source/application/store/controller/apps/bargain/Active.php b/source/application/store/controller/apps/bargain/Active.php new file mode 100644 index 0000000..a7182e4 --- /dev/null +++ b/source/application/store/controller/apps/bargain/Active.php @@ -0,0 +1,86 @@ +getList($search); + return $this->fetch('index', compact('list')); + } + + /** + * 新增砍价活动 + * @return array|bool|mixed + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + $model = new ActiveModel; + // 新增记录 + if ($model->add($this->postData('active'))) { + return $this->renderSuccess('添加成功', url('apps.bargain.active/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新砍价活动 + * @param $active_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($active_id) + { + // 砍价活动详情 + $model = ActiveModel::detail($active_id); + if (!$this->request->isAjax()) { + // 获取商品详情 + $goods = GoodsModel::detail($model['goods_id']); + return $this->fetch('edit', compact('model', 'goods')); + } + // 更新记录 + if ($model->edit($this->postData('active'))) { + return $this->renderSuccess('更新成功', url('apps.bargain.active/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除砍价活动 + * @param $active_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($active_id) + { + // 砍价活动详情 + $model = ActiveModel::detail($active_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/bargain/Setting.php b/source/application/store/controller/apps/bargain/Setting.php new file mode 100644 index 0000000..815b75d --- /dev/null +++ b/source/application/store/controller/apps/bargain/Setting.php @@ -0,0 +1,33 @@ +request->isAjax()) { + $values = SettingModel::getItem('basic'); + return $this->fetch('index', compact('values')); + } + $model = new SettingModel; + if ($model->edit('basic', $this->postData('basic'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/bargain/Task.php b/source/application/store/controller/apps/bargain/Task.php new file mode 100644 index 0000000..545a258 --- /dev/null +++ b/source/application/store/controller/apps/bargain/Task.php @@ -0,0 +1,60 @@ +getList($search); + return $this->fetch('index', compact('list')); + } + + /** + * 砍价榜 + * @param $task_id + * @return mixed + * @throws \think\exception\DbException + */ + public function help($task_id) + { + $model = new TaskHelpModel; + $list = $model->getList($task_id); + return $this->fetch('help', compact('list')); + } + + /** + * 删除砍价任务 + * @param $task_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($task_id) + { + // 砍价活动详情 + $model = TaskModel::detail($task_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/dealer/Apply.php b/source/application/store/controller/apps/dealer/Apply.php new file mode 100644 index 0000000..bfbefb5 --- /dev/null +++ b/source/application/store/controller/apps/dealer/Apply.php @@ -0,0 +1,46 @@ +fetch('index', [ + 'list' => $model->getList($search), + ]); + } + + /** + * 分销商审核 + * @param $apply_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function submit($apply_id) + { + $model = ApplyModel::detail($apply_id); + if ($model->submit($this->postData('apply'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/dealer/Order.php b/source/application/store/controller/apps/dealer/Order.php new file mode 100644 index 0000000..9b6664c --- /dev/null +++ b/source/application/store/controller/apps/dealer/Order.php @@ -0,0 +1,29 @@ +getList($user_id, $is_settled); + return $this->fetch('index', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/dealer/Setting.php b/source/application/store/controller/apps/dealer/Setting.php new file mode 100644 index 0000000..38bd1e1 --- /dev/null +++ b/source/application/store/controller/apps/dealer/Setting.php @@ -0,0 +1,59 @@ +request->isAjax()) { + $data = SettingModel::getAll(); + // 购买指定商品成为分销商:商品列表 + $goodsList = (new GoodsModel)->getListByIds($data['condition']['values']['become__buy_goods_ids']); + return $this->fetch('index', compact('data', 'goodsList')); + } + $model = new SettingModel; + if ($model->edit($this->postData('setting'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 分销海报 + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function qrcode() + { + if (!$this->request->isAjax()) { + $data = SettingModel::getItem('qrcode'); + return $this->fetch('qrcode', [ + 'data' => json_encode($data, JSON_UNESCAPED_UNICODE) + ]); + } + $model = new SettingModel; + if ($model->edit(['qrcode' => $this->postData('qrcode')])) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/dealer/User.php b/source/application/store/controller/apps/dealer/User.php new file mode 100644 index 0000000..315d682 --- /dev/null +++ b/source/application/store/controller/apps/dealer/User.php @@ -0,0 +1,108 @@ +fetch('index', [ + 'list' => $model->getList($search), + 'basicSetting' => SettingModel::getItem('basic'), + ]); + } + + /** + * 分销商用户列表 + * @param string $user_id + * @param int $level + * @return mixed + * @throws \think\exception\DbException + */ + public function fans($user_id, $level = -1) + { + $model = new RefereeModel; + return $this->fetch('fans', [ + 'list' => $model->getList($user_id, $level), + 'basicSetting' => SettingModel::getItem('basic'), + ]); + } + + /** + * 编辑分销商 + * @param $dealer_id + * @return mixed + * @throws \think\exception\DbException + */ + public function edit($dealer_id) + { + $model = UserModel::detail($dealer_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + if ($model->edit($this->postData('model'))) { + return $this->renderSuccess('更新成功', url('apps.dealer.user/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除分销商 + * @param $dealer_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function delete($dealer_id) + { + $model = UserModel::detail($dealer_id); + if (!$model->setDelete()) { + return $this->renderError('删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 分销商二维码 + * @param $dealer_id + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function qrcode($dealer_id) + { + $model = UserModel::detail($dealer_id); + $Qrcode = new Poster($model); + $this->redirect($Qrcode->getImage()); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/dealer/Withdraw.php b/source/application/store/controller/apps/dealer/Withdraw.php new file mode 100644 index 0000000..4aa53d4 --- /dev/null +++ b/source/application/store/controller/apps/dealer/Withdraw.php @@ -0,0 +1,79 @@ +fetch('index', [ + 'list' => $model->getList($user_id, $apply_status, $pay_type, $search) + ]); + } + + /** + * 提现审核 + * @param $id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function submit($id) + { + $model = WithdrawModel::detail($id); + if ($model->submit($this->postData('withdraw'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 确认打款 + * @param $id + * @return array + * @throws \think\exception\DbException + */ + public function money($id) + { + $model = WithdrawModel::detail($id); + if ($model->money()) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 分销商提现:微信支付企业付款 + * @param $id + * @return array|bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function wechat_pay($id) + { + $model = WithdrawModel::detail($id); + if ($model->wechatPay()) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharing/Active.php b/source/application/store/controller/apps/sharing/Active.php new file mode 100644 index 0000000..ff167c4 --- /dev/null +++ b/source/application/store/controller/apps/sharing/Active.php @@ -0,0 +1,42 @@ +getList($active_id); + return $this->fetch('index', compact('list')); + } + + /** + * + * @param $active_id + * @return mixed + * @throws \think\exception\DbException + */ + public function users($active_id) + { + $model = new ActiveUsersModel; + $list = $model->getList($active_id); + return $this->fetch('users', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharing/Category.php b/source/application/store/controller/apps/sharing/Category.php new file mode 100644 index 0000000..dadaa6a --- /dev/null +++ b/source/application/store/controller/apps/sharing/Category.php @@ -0,0 +1,82 @@ +getCacheTree(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加商品分类 + * @return array|mixed + */ + public function add() + { + $model = new CategoryModel; + if (!$this->request->isAjax()) { + // 获取所有地区 + $list = $model->getCacheTree(); + return $this->fetch('add', compact('list')); + } + // 新增记录 + if ($model->add($this->postData('category'))) { + return $this->renderSuccess('添加成功', url('apps.sharing.category/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑商品分类 + * @param $category_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($category_id) + { + // 模板详情 + $model = CategoryModel::detail($category_id); + if (!$this->request->isAjax()) { + // 获取所有地区 + $list = $model->getCacheTree(); + return $this->fetch('edit', compact('model', 'list')); + } + // 更新记录 + if ($model->edit($this->postData('category'))) { + return $this->renderSuccess('更新成功', url('apps.sharing.category/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除商品分类 + * @param $category_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($category_id) + { + $model = CategoryModel::detail($category_id); + if (!$model->remove($category_id)) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/apps/sharing/Comment.php b/source/application/store/controller/apps/sharing/Comment.php new file mode 100644 index 0000000..0e5fa7a --- /dev/null +++ b/source/application/store/controller/apps/sharing/Comment.php @@ -0,0 +1,62 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 评价详情 + * @param $comment_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function detail($comment_id) + { + // 评价详情 + $model = CommentModel::detail($comment_id); + if (!$this->request->isAjax()) { + return $this->fetch('detail', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('comment'))) { + return $this->renderSuccess('更新成功', url('apps.sharing.comment/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除评价 + * @param $comment_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($comment_id) + { + $model = CommentModel::get($comment_id); + if (!$model->setDelete()) { + return $this->renderError('删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharing/Goods.php b/source/application/store/controller/apps/sharing/Goods.php new file mode 100644 index 0000000..bfe7a33 --- /dev/null +++ b/source/application/store/controller/apps/sharing/Goods.php @@ -0,0 +1,184 @@ +getList(array_merge(['status' => -1], $this->request->param())); + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + return $this->fetch('index', compact('list', 'catgory')); + } + + /** + * 添加商品 + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function add() + { + if (!$this->request->isAjax()) { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 配送模板 + $delivery = DeliveryModel::getAll(); + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + return $this->fetch('add', compact('catgory', 'delivery', 'gradeList')); + } + $model = new GoodsModel; + if ($model->add($this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('apps.sharing.goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 复制主商城商品 + * @param $goods_id + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function copy_master($goods_id) + { + // 商品详情 + $model = \app\store\model\Goods::detail($goods_id); + if (!$model || $model['is_delete']) { + return $this->renderError('商品信息不存在'); + } + if (!$this->request->isAjax()) { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 配送模板 + $delivery = DeliveryModel::getAll(); + // 商品sku数据 + $specData = 'null'; + if ($model['spec_type'] == 20) { + $specData = json_encode($model->getManySpecData($model['spec_rel'], $model['sku']), JSON_UNESCAPED_SLASHES); + } + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + return $this->fetch('copy_master', compact('model', 'catgory', 'delivery', 'specData', 'gradeList')); + } + // 新增拼团商品 + $model = new GoodsModel; + if ($model->add($this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('apps.sharing.goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 一键复制 + * @param $goods_id + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function copy($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$this->request->isAjax()) { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 配送模板 + $delivery = DeliveryModel::getAll(); + // 商品sku数据 + $specData = 'null'; + if ($model['spec_type'] == 20) { + $specData = json_encode($model->getManySpecData($model['spec_rel'], $model['sku']), JSON_UNESCAPED_SLASHES); + } + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + return $this->fetch('edit', compact('model', 'catgory', 'delivery', 'specData', 'gradeList')); + } + $model = new GoodsModel; + if ($model->add($this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('apps.sharing.goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 商品编辑 + * @param $goods_id + * @return array|mixed + * @throws \think\exception\PDOException + */ + public function edit($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$this->request->isAjax()) { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 配送模板 + $delivery = DeliveryModel::getAll(); + // 商品sku数据 + $specData = 'null'; + if ($model['spec_type'] == 20) { + $specData = json_encode($model->getManySpecData($model['spec_rel'], $model['sku']), JSON_UNESCAPED_SLASHES); + } + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + return $this->fetch('edit', compact('model', 'catgory', 'delivery', 'specData', 'gradeList')); + } + // 更新记录 + if ($model->edit($this->postData('goods'))) { + return $this->renderSuccess('更新成功', url('apps.sharing.goods/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 修改商品状态 + * @param $goods_id + * @param boolean $state + * @return array + */ + public function state($goods_id, $state) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$model->setStatus($state)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除商品 + * @param $goods_id + * @return array + */ + public function delete($goods_id) + { + // 商品详情 + $model = GoodsModel::detail($goods_id); + if (!$model->setDelete()) { + return $this->renderError('删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/apps/sharing/Order.php b/source/application/store/controller/apps/sharing/Order.php new file mode 100644 index 0000000..8b06509 --- /dev/null +++ b/source/application/store/controller/apps/sharing/Order.php @@ -0,0 +1,87 @@ +getList($dataType, $this->request->param()); + // 自提门店列表 + $shopList = ShopModel::getAllList(); + return $this->fetch('index', compact('dataType', 'list', 'shopList')); + } + + /** + * 订单详情 + * @param $order_id + * @return mixed + * @throws \think\exception\DbException + */ + public function detail($order_id) + { + // 订单详情 + $detail = OrderModel::detail($order_id); + // 物流公司列表 + $expressList = ExpressModel::getAll(); + // 门店店员列表 + $shopClerkList = (new ShopClerkModel)->getList(true); + return $this->fetch('detail', compact( + 'detail', + 'expressList', + 'shopClerkList' + )); + } + + /** + * 确认发货 + * @param $order_id + * @return array + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function delivery($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->delivery($this->postData('order'))) { + return $this->renderSuccess('发货成功'); + } + return $this->renderError($model->getError() ?: '发货失败'); + } + + /** + * 修改订单价格 + * @param $order_id + * @return array + * @throws \think\exception\DbException + */ + public function updatePrice($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->updatePrice($this->postData('order'))) { + return $this->renderSuccess('修改成功'); + } + return $this->renderError($model->getError() ?: '修改失败'); + } + +} diff --git a/source/application/store/controller/apps/sharing/Setting.php b/source/application/store/controller/apps/sharing/Setting.php new file mode 100644 index 0000000..8a8f94f --- /dev/null +++ b/source/application/store/controller/apps/sharing/Setting.php @@ -0,0 +1,33 @@ +request->isAjax()) { + $values = SettingModel::getItem('basic'); + return $this->fetch('index', compact('values')); + } + $model = new SettingModel; + if ($model->edit('basic', $this->postData('basic'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharing/order/Operate.php b/source/application/store/controller/apps/sharing/order/Operate.php new file mode 100644 index 0000000..640aaa5 --- /dev/null +++ b/source/application/store/controller/apps/sharing/order/Operate.php @@ -0,0 +1,118 @@ +model = new OrderModel; + } + + /** + * 订单导出 + * @param string $dataType + * @throws \think\exception\DbException + */ + public function export($dataType) + { + return $this->model->exportList($dataType, $this->request->param()); + } + + /** + * 批量发货 + * @return array|bool|mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function batchDelivery() + { + if (!$this->request->isAjax()) { + return $this->fetch('batchDelivery', [ + 'express_list' => ExpressModel::getAll() + ]); + } + if ($this->model->batchDelivery($this->postData('order'))) { + return $this->renderSuccess('发货成功'); + } + return $this->renderError($this->model->getError() ?: '发货失败'); + } + +// /** +// * 批量发货模板 +// */ +// public function deliveryTpl() +// { +// return $this->model->deliveryTpl(); +// } + + /** + * 审核:用户取消订单 + * @param $order_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function confirmCancel($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->confirmCancel($this->postData('order'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 拼团失败手动退款 + * @param $order_id + * @return array|bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function refund($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->refund()) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 门店自提核销 + * @param $order_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function extract($order_id) + { + $model = OrderModel::detail($order_id); + $data = $this->postData('order'); + if ($model->verificationOrder($data['extract_clerk_id'])) { + return $this->renderSuccess('核销成功'); + } + return $this->renderError($model->getError() ?: '核销失败'); + } + +} diff --git a/source/application/store/controller/apps/sharing/order/Refund.php b/source/application/store/controller/apps/sharing/order/Refund.php new file mode 100644 index 0000000..00addc1 --- /dev/null +++ b/source/application/store/controller/apps/sharing/order/Refund.php @@ -0,0 +1,82 @@ +getList($this->getData()); + return $this->fetch('index', compact('list')); + } + + /** + * 售后单详情 + * @param $order_refund_id + * @return mixed + * @throws \think\exception\DbException + */ + public function detail($order_refund_id) + { + // 售后单详情 + $detail = OrderRefundModel::detail($order_refund_id); + // 订单详情 + $order = OrderModel::detail($detail['order_id']); + // 退货地址 + $address = (new ReturnAddressModel)->getAll(); + return $this->fetch('detail', compact('detail', 'order', 'address')); + } + + /** + * 商家审核 + * @param $order_refund_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function audit($order_refund_id) + { + if (!$this->request->isAjax()) { + return false; + } + $model = OrderRefundModel::detail($order_refund_id); + if ($model->audit($this->postData('refund'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 确认收货并退款 + * @param $order_refund_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function receipt($order_refund_id) + { + if (!$this->request->isAjax()) { + return false; + } + $model = OrderRefundModel::detail($order_refund_id); + if ($model->receipt($this->postData('refund'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharp/Active.php b/source/application/store/controller/apps/sharp/Active.php new file mode 100644 index 0000000..7434c8b --- /dev/null +++ b/source/application/store/controller/apps/sharp/Active.php @@ -0,0 +1,78 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 新增活动会场 + * @return array|bool|mixed + * @throws \Exception + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + $model = new ActiveModel; + // 新增记录 + if ($model->add($this->postData('active'))) { + return $this->renderSuccess('添加成功', url('apps.sharp.active/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 修改活动状态 + * @param $active_id + * @param $state + * @return array|bool + * @throws \think\exception\DbException + */ + public function state($active_id, $state) + { + // 活动详情 + $model = ActiveModel::detail($active_id); + if (!$model->setStatus($state)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除活动会场 + * @param $active_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($active_id) + { + // 活动会场详情 + $model = ActiveModel::detail($active_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharp/ActiveTime.php b/source/application/store/controller/apps/sharp/ActiveTime.php new file mode 100644 index 0000000..cafa791 --- /dev/null +++ b/source/application/store/controller/apps/sharp/ActiveTime.php @@ -0,0 +1,120 @@ +getList($active_id); + return $this->fetch('index', compact('list')); + } + + /** + * 新增活动会场 + * @param $active_id + * @return array|bool|mixed + * @throws \think\exception\DbException + */ + + /** + * 新增活动会场 + * @param $active_id + * @return array|bool|mixed + * @throws \think\exception\DbException + */ + public function add($active_id) + { + // 活动详情 + $active = ActiveModel::detail($active_id); + // 已存在的场次 + $model = new ActiveTimeModel; + $existTimes = $model->getActiveTimeData($active_id); + if (!$this->request->isAjax()) { + return $this->fetch('add', compact('active', 'existTimes')); + } + // 新增记录 + if ($model->add($active_id, $this->postData('active'))) { + $url = url('apps.sharp.active_time/index', ['active_id' => $active_id]); + return $this->renderSuccess('添加成功', $url); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑活动会场 + * @param $id + * @return array|bool|mixed + * @throws \think\exception\DbException + * @throws \Exception + */ + public function edit($id) + { + // 场次详情 + $model = ActiveTimeModel::detail($id, ['active']); + // 当前场次关联的商品 + $goodsList = $model->getGoodsListByActiveTimeId($id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model', 'goodsList')); + } + // 新增记录 + if ($model->edit($this->postData('active'))) { + $url = url('apps.sharp.active_time/index', ['active_id' => $model['active_id']]); + return $this->renderSuccess('更新成功', $url); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 修改场次状态 + * @param $id + * @param $state + * @return array|bool + * @throws \think\exception\DbException + */ + public function state($id, $state) + { + // 场次详情 + $model = ActiveTimeModel::detail($id); + if (!$model->setStatus($state)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除活动场次 + * @param $id + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function delete($id) + { + // 场次详情 + $model = ActiveTimeModel::detail($id); + if (!$model->onDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharp/Goods.php b/source/application/store/controller/apps/sharp/Goods.php new file mode 100644 index 0000000..855e108 --- /dev/null +++ b/source/application/store/controller/apps/sharp/Goods.php @@ -0,0 +1,124 @@ +getList($search); + return $this->fetch('index', compact('list')); + } + + /** + * 添加秒杀商品 + * @param int $step + * @param null $goods_id + * @return array|bool|mixed + * @throws \Exception + */ + public function add($step = 1, $goods_id = null) + { + if ($step == 2) { + return $this->step2($goods_id); + } + return $this->step1(); + } + + /** + * 添加秒杀商品:步骤1 + * @return mixed + */ + private function step1() + { + return $this->fetch('step1'); + } + + /** + * 添加秒杀商品:步骤2 + * @param $goodsId + * @return array|bool|mixed + * @throws \Exception + */ + private function step2($goodsId) + { + $model = new SharpGoodsModel; + // 验证商品ID能否被添加 + if (!$model->validateGoodsId($goodsId)) { + $this->renderError($model->getError()); + } + // 商品信息 + $goods = GoodsModel::detail($goodsId); + $specData = GoodsService::getSpecData($goods); + // 填写商品信息页面 + if (!$this->request->isAjax()) { + return $this->fetch('step2', compact('goods', 'specData')); + } + // 表单提交 + if ($model->add($goods, $this->postData('goods'))) { + return $this->renderSuccess('添加成功', url('apps.sharp.goods/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑秒杀商品 + * @param $sharp_goods_id + * @return array|bool|mixed + * @throws \think\exception\DbException + * @throws \Exception + */ + public function edit($sharp_goods_id) + { + // 秒杀商品详情 + $model = SharpGoodsModel::detail($sharp_goods_id, ['sku']); + // 商品信息 + $goods = GoodsModel::detail($model['goods_id']); + // 商品多规格信息 + $specData = $model->getSpecData($goods, $model['sku']); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model', 'goods', 'specData')); + } + // 更新记录 + if ($model->edit($goods, $this->postData('goods'))) { + return $this->renderSuccess('更新成功', url('apps.sharp.goods/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除秒杀商品 + * @param $sharp_goods_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($sharp_goods_id) + { + // 秒杀商品详情 + $model = SharpGoodsModel::detail($sharp_goods_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/sharp/Setting.php b/source/application/store/controller/apps/sharp/Setting.php new file mode 100644 index 0000000..8c506a1 --- /dev/null +++ b/source/application/store/controller/apps/sharp/Setting.php @@ -0,0 +1,33 @@ +request->isAjax()) { + $values = SettingModel::getItem('basic'); + return $this->fetch('index', compact('values')); + } + $model = new SettingModel; + if ($model->edit('basic', $this->postData('basic'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/wow/Order.php b/source/application/store/controller/apps/wow/Order.php new file mode 100644 index 0000000..91c85ed --- /dev/null +++ b/source/application/store/controller/apps/wow/Order.php @@ -0,0 +1,44 @@ +getList($search); + return $this->fetch('index', compact('list')); + } + + /** + * 取消同步 + * @param $id + * @return array|bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function delete($id) + { + // 删除微信好物圈收藏 + $WechatWow = new WowOrderService($this->getWxappId()); + $WechatWow->delete($id); + return $this->renderSuccess('操作成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/wow/Setting.php b/source/application/store/controller/apps/wow/Setting.php new file mode 100644 index 0000000..5623f67 --- /dev/null +++ b/source/application/store/controller/apps/wow/Setting.php @@ -0,0 +1,33 @@ +request->isAjax()) { + $values = SettingModel::getItem('basic'); + return $this->fetch('index', compact('values')); + } + $model = new SettingModel; + if ($model->edit('basic', $this->postData('basic'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/apps/wow/Shoping.php b/source/application/store/controller/apps/wow/Shoping.php new file mode 100644 index 0000000..f147db0 --- /dev/null +++ b/source/application/store/controller/apps/wow/Shoping.php @@ -0,0 +1,44 @@ +getList($search); + return $this->fetch('index', compact('list')); + } + + /** + * 取消同步 + * @param $id + * @return array|bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function delete($id) + { + // 删除微信好物圈收藏 + $WechatWow = new WowService($this->getWxappId()); + $WechatWow->delete($id); + return $this->renderSuccess('操作成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/content/Article.php b/source/application/store/controller/content/Article.php new file mode 100644 index 0000000..425d280 --- /dev/null +++ b/source/application/store/controller/content/Article.php @@ -0,0 +1,85 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加文章 + * @return array|mixed + */ + public function add() + { + $model = new ArticleModel; + if (!$this->request->isAjax()) { + // 文章分类 + $catgory = CategoryModel::getAll(); + return $this->fetch('add', compact('catgory')); + } + // 新增记录 + if ($model->add($this->postData('article'))) { + return $this->renderSuccess('添加成功', url('content.article/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新文章 + * @param $article_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($article_id) + { + // 文章详情 + $model = ArticleModel::detail($article_id); + if (!$this->request->isAjax()) { + // 文章分类 + $catgory = CategoryModel::getAll(); + return $this->fetch('edit', compact('model', 'catgory')); + } + // 更新记录 + if ($model->edit($this->postData('article'))) { + return $this->renderSuccess('更新成功', url('content.article/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除文章 + * @param $article_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($article_id) + { + // 文章详情 + $model = ArticleModel::detail($article_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/content/Files.php b/source/application/store/controller/content/Files.php new file mode 100644 index 0000000..e5a472b --- /dev/null +++ b/source/application/store/controller/content/Files.php @@ -0,0 +1,88 @@ +getList(-1, '', 0); + return $this->fetch('index', compact('list')); + } + + /** + * 回收站列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function recycle() + { + $model = new UploadFileModel; + $list = $model->getList(-1, '', 1); + return $this->fetch('recycle', compact('list')); + } + + /** + * 移入回收站 + * @param $file_id + * @return array + * @throws \think\exception\DbException + */ + public function recovery($file_id) + { + // 文章详情 + $model = UploadFileModel::detail($file_id); + if (!$model->setRecycle(true)) { + return $this->renderError($model->getError() ?: '操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 移出回收站 + * @param $file_id + * @return array + * @throws \think\exception\DbException + */ + public function restore($file_id) + { + // 商品详情 + $model = UploadFileModel::detail($file_id); + if (!$model->setRecycle(false)) { + return $this->renderError('操作失败'); + } + return $this->renderSuccess('操作成功'); + } + + /** + * 删除文件 + * @param $file_id + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function delete($file_id) + { + // 商品详情 + $model = UploadFileModel::detail($file_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '操作失败'); + } + return $this->renderSuccess('操作成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/content/article/Category.php b/source/application/store/controller/content/article/Category.php new file mode 100644 index 0000000..9e6ca07 --- /dev/null +++ b/source/application/store/controller/content/article/Category.php @@ -0,0 +1,78 @@ +getAll(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加文章分类 + * @return array|mixed + */ + public function add() + { + $model = new CategoryModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('category'))) { + return $this->renderSuccess('添加成功', url('content.article.category/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑文章分类 + * @param $category_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($category_id) + { + // 分类详情 + $model = CategoryModel::detail($category_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('category'))) { + return $this->renderSuccess('更新成功', url('content.article.category/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除文章分类 + * @param $category_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($category_id) + { + $model = CategoryModel::detail($category_id); + if (!$model->remove($category_id)) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/content/files/Group.php b/source/application/store/controller/content/files/Group.php new file mode 100644 index 0000000..1fff87c --- /dev/null +++ b/source/application/store/controller/content/files/Group.php @@ -0,0 +1,81 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加文件分组 + * @return array|mixed + */ + public function add() + { + $model = new GroupModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('group'))) { + return $this->renderSuccess('添加成功', url('content.files.group/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑文件分组 + * @param $group_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($group_id) + { + // 分组详情 + $model = GroupModel::detail($group_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('group'))) { + return $this->renderSuccess('更新成功', url('content.files.group/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除文件分组 + * @param $group_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($group_id) + { + $model = GroupModel::detail($group_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/data/Goods.php b/source/application/store/controller/data/Goods.php new file mode 100644 index 0000000..09f83e4 --- /dev/null +++ b/source/application/store/controller/data/Goods.php @@ -0,0 +1,47 @@ +model = new GoodsModel; + $this->view->engine->layout(false); + } + + /** + * 商品列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function lists() + { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 商品列表 + $list = $this->model->getList($this->request->param()); + return $this->fetch('list', compact('list', 'catgory')); + } + +} diff --git a/source/application/store/controller/data/Shop.php b/source/application/store/controller/data/Shop.php new file mode 100644 index 0000000..83eaa15 --- /dev/null +++ b/source/application/store/controller/data/Shop.php @@ -0,0 +1,39 @@ +model = new ShopModel; + $this->view->engine->layout(false); + } + + /** + * 门店列表 + * @param null $status + * @return mixed + * @throws \think\exception\DbException + */ + public function lists($status = null) + { + $list = $this->model->getList($status); + return $this->fetch('list', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/data/User.php b/source/application/store/controller/data/User.php new file mode 100644 index 0000000..0554db4 --- /dev/null +++ b/source/application/store/controller/data/User.php @@ -0,0 +1,50 @@ +model = new UserModel; + $this->view->engine->layout(false); + } + + /** + * 用户列表 + * @return mixed + * @param string $nickName 昵称 + * @param int $gender 性别 + * @param int $grade 会员等级 + * @throws \think\exception\DbException + */ + public function lists($nickName = '', $gender = null, $grade = null) + { + // 会员等级列表 + $gradeList = GradeModel::getUsableList(); + // 用户列表 + $list = $this->model->getList($nickName, $gender, $grade); + return $this->fetch('list', compact('list', 'gradeList')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/data/bargain/Goods.php b/source/application/store/controller/data/bargain/Goods.php new file mode 100644 index 0000000..5c6cd8d --- /dev/null +++ b/source/application/store/controller/data/bargain/Goods.php @@ -0,0 +1,43 @@ +view->engine->layout(false); + } + + /** + * 商品列表 + * @param string $search + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function lists($search = '') + { + $model = new ActiveModel; + $list = $model->getList($search); + return $this->fetch('list', compact('list')); + } + +} diff --git a/source/application/store/controller/data/sharing/Goods.php b/source/application/store/controller/data/sharing/Goods.php new file mode 100644 index 0000000..be07ac8 --- /dev/null +++ b/source/application/store/controller/data/sharing/Goods.php @@ -0,0 +1,47 @@ +model = new GoodsModel; + $this->view->engine->layout(false); + } + + /** + * 商品列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function lists() + { + // 商品分类 + $catgory = CategoryModel::getCacheTree(); + // 商品列表 + $list = $this->model->getList($this->request->param()); + return $this->fetch('list', compact('list', 'catgory')); + } + +} diff --git a/source/application/store/controller/data/sharp/Goods.php b/source/application/store/controller/data/sharp/Goods.php new file mode 100644 index 0000000..39f4954 --- /dev/null +++ b/source/application/store/controller/data/sharp/Goods.php @@ -0,0 +1,43 @@ +view->engine->layout(false); + } + + /** + * 商品列表 + * @param string $search + * @return mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function lists($search = '') + { + $model = new GoodsModel; + $list = $model->getList($search); + return $this->fetch('list', compact('list')); + } + +} diff --git a/source/application/store/controller/goods/Category.php b/source/application/store/controller/goods/Category.php new file mode 100644 index 0000000..2ceb76d --- /dev/null +++ b/source/application/store/controller/goods/Category.php @@ -0,0 +1,83 @@ +getCacheTree(); + return $this->fetch('index', compact('list')); + } + + /** + * 删除商品分类 + * @param $category_id + * @return array|bool + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function delete($category_id) + { + $model = CategoryModel::get($category_id); + if (!$model->remove($category_id)) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 添加商品分类 + * @return array|mixed + */ + public function add() + { + $model = new CategoryModel; + if (!$this->request->isAjax()) { + // 获取所有地区 + $list = $model->getCacheTree(); + return $this->fetch('add', compact('list')); + } + // 新增记录 + if ($model->add($this->postData('category'))) { + return $this->renderSuccess('添加成功', url('goods.category/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑商品分类 + * @param $category_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($category_id) + { + // 模板详情 + $model = CategoryModel::get($category_id, ['image']); + if (!$this->request->isAjax()) { + // 获取所有地区 + $list = $model->getCacheTree(); + return $this->fetch('edit', compact('model', 'list')); + } + // 更新记录 + if ($model->edit($this->postData('category'))) { + return $this->renderSuccess('更新成功', url('goods.category/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} diff --git a/source/application/store/controller/goods/Comment.php b/source/application/store/controller/goods/Comment.php new file mode 100644 index 0000000..2f9cf09 --- /dev/null +++ b/source/application/store/controller/goods/Comment.php @@ -0,0 +1,62 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 评价详情 + * @param $comment_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function detail($comment_id) + { + // 评价详情 + $model = CommentModel::detail($comment_id); + if (!$this->request->isAjax()) { + return $this->fetch('detail', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('comment'))) { + return $this->renderSuccess('更新成功', url('goods.comment/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除评价 + * @param $comment_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($comment_id) + { + $model = CommentModel::get($comment_id); + if (!$model->setDelete()) { + return $this->renderError('删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/goods/Spec.php b/source/application/store/controller/goods/Spec.php new file mode 100644 index 0000000..874ff45 --- /dev/null +++ b/source/application/store/controller/goods/Spec.php @@ -0,0 +1,93 @@ +SpecModel = new SpecModel; + $this->SpecValueModel = new SpecValueModel; + } + + /** + * 添加规则组 + * @param $spec_name + * @param $spec_value + * @return array + */ + public function addSpec($spec_name, $spec_value) + { + // 判断规格组是否存在 + if (!$specId = $this->SpecModel->getSpecIdByName($spec_name)) { + // 新增规格组and规则值 + if ($this->SpecModel->add($spec_name) + && $this->SpecValueModel->add($this->SpecModel['spec_id'], $spec_value)) + return $this->renderSuccess('', '', [ + 'spec_id' => (int)$this->SpecModel['spec_id'], + 'spec_value_id' => (int)$this->SpecValueModel['spec_value_id'], + ]); + return $this->renderError(); + } + // 判断规格值是否存在 + if ($specValueId = $this->SpecValueModel->getSpecValueIdByName($specId, $spec_value)) { + return $this->renderSuccess('', '', [ + 'spec_id' => (int)$specId, + 'spec_value_id' => (int)$specValueId, + ]); + } + // 添加规则值 + if ($this->SpecValueModel->add($specId, $spec_value)) + return $this->renderSuccess('', '', [ + 'spec_id' => (int)$specId, + 'spec_value_id' => (int)$this->SpecValueModel['spec_value_id'], + ]); + return $this->renderError(); + } + + /** + * 添加规格值 + * @param $spec_id + * @param $spec_value + * @return array + */ + public function addSpecValue($spec_id, $spec_value) + { + // 判断规格值是否存在 + if ($specValueId = $this->SpecValueModel->getSpecValueIdByName($spec_id, $spec_value)) { + return $this->renderSuccess('', '', [ + 'spec_value_id' => (int)$specValueId, + ]); + } + // 添加规则值 + if ($this->SpecValueModel->add($spec_id, $spec_value)) + return $this->renderSuccess('', '', [ + 'spec_value_id' => (int)$this->SpecValueModel['spec_value_id'], + ]); + return $this->renderError(); + } + +} diff --git a/source/application/store/controller/market/Basic.php b/source/application/store/controller/market/Basic.php new file mode 100644 index 0000000..82c5dd6 --- /dev/null +++ b/source/application/store/controller/market/Basic.php @@ -0,0 +1,39 @@ +request->isAjax()) { + $values = SettingModel::getItem('full_free'); + return $this->fetch('full_free', [ + 'goodsList' => (new Goods)->getListByIds($values['notin_goods']), + 'regionData' => RegionModel::getCacheTree(), // 所有地区 + 'values' => $values + ]); + } + $model = new SettingModel; + if ($model->edit('full_free', $this->postData('model'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/market/Coupon.php b/source/application/store/controller/market/Coupon.php new file mode 100644 index 0000000..bd3ac1c --- /dev/null +++ b/source/application/store/controller/market/Coupon.php @@ -0,0 +1,108 @@ +model = new CouponModel; + } + + /** + * 优惠券列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function index() + { + $list = $this->model->getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加优惠券 + * @return array|mixed + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($this->model->add($this->postData('coupon'))) { + return $this->renderSuccess('添加成功', url('market.coupon/index')); + } + return $this->renderError($this->model->getError() ?: '添加失败'); + } + + /** + * 更新优惠券 + * @param $coupon_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($coupon_id) + { + // 优惠券详情 + $model = CouponModel::detail($coupon_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('coupon'))) { + return $this->renderSuccess('更新成功', url('market.coupon/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除优惠券 + * @param $coupon_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function delete($coupon_id) + { + // 优惠券详情 + $model = CouponModel::detail($coupon_id); + // 更新记录 + if ($model->setDelete()) { + return $this->renderSuccess('删除成功', url('market.coupon/index')); + } + return $this->renderError($model->getError() ?: '删除成功'); + } + + /** + * 领取记录 + * @return mixed + * @throws \think\exception\DbException + */ + public function receive() + { + $model = new UserCouponModel; + $list = $model->getList(); + return $this->fetch('receive', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/market/Points.php b/source/application/store/controller/market/Points.php new file mode 100644 index 0000000..236a510 --- /dev/null +++ b/source/application/store/controller/market/Points.php @@ -0,0 +1,47 @@ +request->isAjax()) { + $values = SettingModel::getItem('points'); + return $this->fetch('setting', compact('values')); + } + $model = new SettingModel; + if ($model->edit('points', $this->postData('points'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 积分明细 + * @return mixed + * @throws \think\exception\DbException + */ + public function log() + { + // 积分明细列表 + $model = new PointsLogModel; + $list = $model->getList($this->request->param()); + return $this->fetch('log', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/market/Push.php b/source/application/store/controller/market/Push.php new file mode 100644 index 0000000..896efc3 --- /dev/null +++ b/source/application/store/controller/market/Push.php @@ -0,0 +1,46 @@ +request->isAjax()) { + return $this->fetch('send'); + } + // 执行发送 + $MessageService = new MessageService; + $MessageService->send($this->postData('send')); + return $this->renderSuccess('', '', [ + 'stateSet' => $MessageService->getStateSet() + ]); + } + + /** + * 活跃用户列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function user() + { + $list = (new FormidModel)->getUserList(); + return $this->fetch('user', compact('list')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/market/Recharge.php b/source/application/store/controller/market/Recharge.php new file mode 100644 index 0000000..59c3ba5 --- /dev/null +++ b/source/application/store/controller/market/Recharge.php @@ -0,0 +1,28 @@ +request->isAjax()) { + $values = SettingModel::getItem('recharge'); + return $this->fetch('setting', ['values' => $values]); + } + $model = new SettingModel; + if ($model->edit('recharge', $this->postData('recharge'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/market/recharge/Plan.php b/source/application/store/controller/market/recharge/Plan.php new file mode 100644 index 0000000..d9a61f6 --- /dev/null +++ b/source/application/store/controller/market/recharge/Plan.php @@ -0,0 +1,95 @@ +model = new PlanModel; + } + + /** + * 充值套餐列表 + * @return mixed + * @throws \think\exception\DbException + */ + public function index() + { + $list = $this->model->getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加充值套餐 + * @return array|mixed + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($this->model->add($this->postData('plan'))) { + return $this->renderSuccess('添加成功', url('market.recharge.plan/index')); + } + return $this->renderError($this->model->getError() ?: '添加失败'); + } + + /** + * 更新充值套餐 + * @param $plan_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($plan_id) + { + // 充值套餐详情 + $model = PlanModel::detail($plan_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('plan'))) { + return $this->renderSuccess('更新成功', url('market.recharge.plan/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除充值套餐 + * @param $plan_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function delete($plan_id) + { + // 套餐详情 + $model = PlanModel::detail($plan_id); + // 更新记录 + if ($model->setDelete()) { + return $this->renderSuccess('删除成功', url('market.recharge.plan/index')); + } + return $this->renderError($model->getError() ?: '删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/order/Operate.php b/source/application/store/controller/order/Operate.php new file mode 100644 index 0000000..773490a --- /dev/null +++ b/source/application/store/controller/order/Operate.php @@ -0,0 +1,102 @@ +model = new OrderModel; + } + + /** + * 订单导出 + * @param string $dataType + * @throws \think\exception\DbException + */ + public function export($dataType) + { + return $this->model->exportList($dataType, $this->request->param()); + } + + /** + * 批量发货 + * @return array|bool|mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function batchDelivery() + { + if (!$this->request->isAjax()) { + return $this->fetch('batchDelivery', [ + 'express_list' => ExpressModel::getAll() + ]); + } + if ($this->model->batchDelivery($this->postData('order'))) { + return $this->renderSuccess('发货成功'); + } + return $this->renderError($this->model->getError() ?: '发货失败'); + } + + /** + * 批量发货模板 + */ + public function deliveryTpl() + { + return $this->model->deliveryTpl(); + } + + /** + * 审核:用户取消订单 + * @param $order_id + * @return array|bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function confirmCancel($order_id) + { + $model = OrderModel::detail($order_id); + if ($model->confirmCancel($this->postData('order'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 门店自提核销 + * @param $order_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function extract($order_id) + { + $model = OrderModel::detail($order_id); + $data = $this->postData('order'); + if ($model->verificationOrder($data['extract_clerk_id'])) { + return $this->renderSuccess('核销成功'); + } + return $this->renderError($model->getError() ?: '核销失败'); + } + +} diff --git a/source/application/store/controller/order/Refund.php b/source/application/store/controller/order/Refund.php new file mode 100644 index 0000000..83ef7d1 --- /dev/null +++ b/source/application/store/controller/order/Refund.php @@ -0,0 +1,82 @@ +getList($this->getData()); + return $this->fetch('index', compact('list')); + } + + /** + * 售后单详情 + * @param $order_refund_id + * @return mixed + * @throws \think\exception\DbException + */ + public function detail($order_refund_id) + { + // 售后单详情 + $detail = OrderRefundModel::detail($order_refund_id); + // 订单详情 + $order = OrderModel::detail($detail['order_id']); + // 退货地址 + $address = (new ReturnAddressModel)->getAll(); + return $this->fetch('detail', compact('detail', 'order', 'address')); + } + + /** + * 商家审核 + * @param $order_refund_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function audit($order_refund_id) + { + if (!$this->request->isAjax()) { + return false; + } + $model = OrderRefundModel::detail($order_refund_id); + if ($model->audit($this->postData('refund'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + + /** + * 确认收货并退款 + * @param $order_refund_id + * @return array|bool + * @throws \think\exception\DbException + */ + public function receipt($order_refund_id) + { + if (!$this->request->isAjax()) { + return false; + } + $model = OrderRefundModel::detail($order_refund_id); + if ($model->receipt($this->postData('refund'))) { + return $this->renderSuccess('操作成功'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/setting/Address.php b/source/application/store/controller/setting/Address.php new file mode 100644 index 0000000..c5efa4e --- /dev/null +++ b/source/application/store/controller/setting/Address.php @@ -0,0 +1,79 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加退货地址 + * @return array|mixed + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + $model = new ReturnAddressModel; + if ($model->add($this->postData('address'))) { + return $this->renderSuccess('添加成功', url('setting.address/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑退货地址 + * @param $address_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($address_id) + { + // 模板详情 + $model = ReturnAddressModel::detail($address_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('address'))) { + return $this->renderSuccess('更新成功', url('setting.address/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除退货地址 + * @param $address_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($address_id) + { + $model = ReturnAddressModel::detail($address_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/setting/Cache.php b/source/application/store/controller/setting/Cache.php new file mode 100644 index 0000000..6ad76ae --- /dev/null +++ b/source/application/store/controller/setting/Cache.php @@ -0,0 +1,137 @@ +request->isAjax()) { + $data = $this->postData('cache'); + $this->rmCache($data['keys']); + return $this->renderSuccess('操作成功'); + } + return $this->fetch('clear', [ + 'cacheList' => $this->getItems() + ]); + } + + /** + * 数据缓存项目 + * @return array + */ + private function getItems() + { + $wxapp_id = $this->store['wxapp']['wxapp_id']; + return [ + 'category' => [ + 'type' => 'cache', + 'key' => 'category_' . $wxapp_id, + 'name' => '商品分类' + ], + 'setting' => [ + 'type' => 'cache', + 'key' => 'setting_' . $wxapp_id, + 'name' => '商城设置' + ], + 'wxapp' => [ + 'type' => 'cache', + 'key' => 'wxapp_' . $wxapp_id, + 'name' => '小程序设置' + ], + 'dealer' => [ + 'type' => 'cache', + 'key' => 'dealer_setting_' . $wxapp_id, + 'name' => '分销设置' + ], + 'sharing' => [ + 'type' => 'cache', + 'key' => 'sharing_setting_' . $wxapp_id, + 'name' => '拼团设置' + ], + 'temp' => [ + 'type' => 'file', + 'name' => '临时图片', + 'dirPath' => [ + 'web' => WEB_PATH . 'temp/' . $wxapp_id . '/', + 'runtime' => RUNTIME_PATH . '/' . 'image/' . $wxapp_id . '/' + ] + ], + ]; + } + + /** + * 删除缓存 + * @param $keys + */ + private function rmCache($keys) + { + $cacheList = $this->getItems(); + $keys = array_intersect(array_keys($cacheList), $keys); + foreach ($keys as $key) { + $item = $cacheList[$key]; + if ($item['type'] === 'cache') { + Driver::has($item['key']) && Driver::rm($item['key']); + } elseif ($item['type'] === 'file') { + $this->deltree($item['dirPath']); + } + } + } + + /** + * 删除目录下所有文件 + * @param $dirPath + * @return bool + */ + private function deltree($dirPath) + { + if (is_array($dirPath)) { + foreach ($dirPath as $path) + $this->deleteFolder($path); + } else { + return $this->deleteFolder($dirPath); + } + return true; + } + + /** + * 递归删除指定目录下所有文件 + * @param $path + * @return bool + */ + private function deleteFolder($path) + { + if (!is_dir($path)) + return false; + // 扫描一个文件夹内的所有文件夹和文件 + foreach (scandir($path) as $val) { + // 排除目录中的.和.. + if (!in_array($val, ['.', '..'])) { + // 如果是目录则递归子目录,继续操作 + if (is_dir($path . $val)) { + // 子目录中操作删除文件夹和文件 + $this->deleteFolder($path . $val . DS); + // 目录清空后删除空文件夹 + rmdir($path . $val . DS); + } else { + // 如果是文件直接删除 + unlink($path . $val); + } + } + } + return true; + } + +} diff --git a/source/application/store/controller/setting/Delivery.php b/source/application/store/controller/setting/Delivery.php new file mode 100644 index 0000000..79e4355 --- /dev/null +++ b/source/application/store/controller/setting/Delivery.php @@ -0,0 +1,96 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 删除模板 + * @param $delivery_id + * @return array + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function delete($delivery_id) + { + $model = DeliveryModel::detail($delivery_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 添加配送模板 + * @return array|mixed + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function add() + { + if (!$this->request->isAjax()) { + // 获取所有地区 + $regionData = json_encode(RegionModel::getCacheTree()); + // 地区总数 + $cityCount = RegionModel::getCacheCounts()['city']; + return $this->fetch('add', compact('regionData', 'cityCount')); + } + // 新增记录 + $model = new DeliveryModel; + if ($model->add($this->postData('delivery'))) { + return $this->renderSuccess('添加成功', url('setting.delivery/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑配送模板 + * @param $delivery_id + * @return array|mixed + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function edit($delivery_id) + { + // 模板详情 + $model = DeliveryModel::detail($delivery_id); + if (!$this->request->isAjax()) { + // 获取所有地区 + $regionData = json_encode(RegionModel::getCacheTree()); + // 地区总数 + $cityCount = RegionModel::getCacheCounts()['city']; + // 获取配送区域及运费设置项 + $formData = json_encode($model->getFormList()); + return $this->fetch('add', compact('model', 'regionData', 'cityCount', 'formData')); + } + // 更新记录 + if ($model->edit($this->postData('delivery'))) { + return $this->renderSuccess('更新成功', url('setting.delivery/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + +} diff --git a/source/application/store/controller/setting/Express.php b/source/application/store/controller/setting/Express.php new file mode 100644 index 0000000..e82a849 --- /dev/null +++ b/source/application/store/controller/setting/Express.php @@ -0,0 +1,89 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 删除物流公司 + * @param $express_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($express_id) + { + $model = ExpressModel::detail($express_id); + if (!$model->remove()) { + $error = $model->getError() ?: '删除失败'; + return $this->renderError($error); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 添加物流公司 + * @return array|mixed + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + $model = new ExpressModel; + if ($model->add($this->postData('express'))) { + return $this->renderSuccess('添加成功', url('setting.express/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑物流公司 + * @param $express_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($express_id) + { + // 模板详情 + $model = ExpressModel::detail($express_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('express'))) { + return $this->renderSuccess('更新成功', url('setting.express/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 物流公司编码表 + * @return mixed + */ + public function company() + { + return $this->fetch('company'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/setting/Help.php b/source/application/store/controller/setting/Help.php new file mode 100644 index 0000000..5ebaaab --- /dev/null +++ b/source/application/store/controller/setting/Help.php @@ -0,0 +1,19 @@ +fetch('tplMsg'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/setting/Printer.php b/source/application/store/controller/setting/Printer.php new file mode 100644 index 0000000..30914a7 --- /dev/null +++ b/source/application/store/controller/setting/Printer.php @@ -0,0 +1,99 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加打印机 + * @return array|mixed + */ + public function add() + { + $model = new PrinterModel; + if (!$this->request->isAjax()) { + // 打印机类型列表 + $printerType = $model::getPrinterTypeList(); + return $this->fetch('add', compact('printerType')); + } + // 新增记录 + if ($model->add($this->postData('printer'))) { + return $this->renderSuccess('添加成功', url('setting.printer/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑打印机 + * @param $printer_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($printer_id) + { + // 模板详情 + $model = PrinterModel::detail($printer_id); + if (!$this->request->isAjax()) { + // 打印机类型列表 + $printerType = $model::getPrinterTypeList(); + return $this->fetch('edit', compact('model', 'printerType')); + } + // 更新记录 + if ($model->edit($this->postData('printer'))) { + return $this->renderSuccess('更新成功', url('setting.printer/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除打印机 + * @param $printer_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($printer_id) + { + $model = PrinterModel::detail($printer_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 测试打印接口 + * @param int $order_id + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function test($order_id = 180) + { + // 订单信息 + $order = \app\store\model\Order::detail($order_id); + // 实例化打印机驱动 + $Printer = new \app\common\service\order\Printer(); + $Printer->printTicket($order, \app\common\enum\OrderStatus::ORDER_PAYMENT); + } + + +} \ No newline at end of file diff --git a/source/application/store/controller/shop/Clerk.php b/source/application/store/controller/shop/Clerk.php new file mode 100644 index 0000000..b79c00f --- /dev/null +++ b/source/application/store/controller/shop/Clerk.php @@ -0,0 +1,91 @@ +getList(-1, $shop_id, $search); + // 门店列表 + $shopList = (new ShopModel)->getList(); + return $this->fetch('index', compact('list', 'shopList')); + } + + /** + * 添加店员 + * @return array|bool|mixed + * @throws \Exception + */ + public function add() + { + $model = new ClerkModel; + if (!$this->request->isAjax()) { + // 门店列表 + $shopList = (new ShopModel)->getList(); + return $this->fetch('add', compact('shopList')); + } + // 新增记录 + if ($model->add($this->postData('clerk'))) { + return $this->renderSuccess('添加成功', url('shop.clerk/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑店员 + * @param $clerk_id + * @return array|bool|mixed + * @throws \think\exception\DbException + */ + public function edit($clerk_id) + { + // 店员详情 + $model = ClerkModel::detail($clerk_id); + if (!$this->request->isAjax()) { + // 门店列表 + $shopList = (new ShopModel)->getList(); + return $this->fetch('edit', compact('model', 'shopList')); + } + // 新增记录 + if ($model->edit($this->postData('clerk'))) { + return $this->renderSuccess('更新成功', url('shop.clerk/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除店员 + * @param $clerk_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($clerk_id) + { + // 店员详情 + $model = ClerkModel::detail($clerk_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/shop/Order.php b/source/application/store/controller/shop/Order.php new file mode 100644 index 0000000..78a61ff --- /dev/null +++ b/source/application/store/controller/shop/Order.php @@ -0,0 +1,33 @@ +getList($shop_id, $search); + // 门店列表 + $shopList = (new ShopModel)->getList(); + return $this->fetch('index', compact('list', 'shopList')); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/statistics/Data.php b/source/application/store/controller/statistics/Data.php new file mode 100644 index 0000000..8629466 --- /dev/null +++ b/source/application/store/controller/statistics/Data.php @@ -0,0 +1,63 @@ +statisticsDataService = new StatisticsDataService; + } + + /** + * 数据统计主页 + * @return mixed + * @throws \think\Exception + */ + public function index() + { + return $this->fetch('index', [ + // 数据概况 + 'survey' => $this->statisticsDataService->getSurveyData(), + // 近七日交易走势 + 'echarts7days' => $this->statisticsDataService->getTransactionTrend(), + // 商品销售榜 + 'goodsRanking' => $this->statisticsDataService->getGoodsRanking(), + // 用户消费榜 + 'userExpendRanking' => $this->statisticsDataService->geUserExpendRanking(), + ]); + } + + /** + * 数据概况API + * @param null $startDate + * @param null $endDate + * @return array + * @throws \think\Exception + */ + public function survey($startDate = null, $endDate = null) + { + return $this->renderSuccess('', '', + $this->statisticsDataService->getSurveyData($startDate, $endDate)); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/store/Role.php b/source/application/store/controller/store/Role.php new file mode 100644 index 0000000..6365243 --- /dev/null +++ b/source/application/store/controller/store/Role.php @@ -0,0 +1,100 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加角色 + * @return array|mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function add() + { + $model = new RoleModel; + if (!$this->request->isAjax()) { + // 权限列表 + $accessList = (new AccessModel)->getJsTree(); + // 角色列表 + $roleList = $model->getList(); + return $this->fetch('add', compact('accessList', 'roleList')); + } + // 新增记录 + if ($model->add($this->postData('role'))) { + return $this->renderSuccess('添加成功', url('store.role/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新角色 + * @param $role_id + * @return array|mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function edit($role_id) + { + // 角色详情 + $model = RoleModel::detail($role_id); + if (!$this->request->isAjax()) { + // 权限列表 + $accessList = (new AccessModel)->getJsTree($model['role_id']); + // 角色列表 + $roleList = $model->getList(); + return $this->fetch('edit', compact('model', 'accessList', 'roleList')); + } + // 更新记录 + if ($model->edit($this->postData('role'))) { + return $this->renderSuccess('更新成功', url('store.role/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除角色 + * @param $role_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($role_id) + { + // 角色详情 + $model = RoleModel::detail($role_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/store/User.php b/source/application/store/controller/store/User.php new file mode 100644 index 0000000..781d139 --- /dev/null +++ b/source/application/store/controller/store/User.php @@ -0,0 +1,115 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加管理员 + * @return array|mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function add() + { + $model = new StoreUserModel; + if (!$this->request->isAjax()) { + // 角色列表 + $roleList = (new RoleModel)->getList(); + return $this->fetch('add', compact('roleList')); + } + // 新增记录 + if ($model->add($this->postData('user'))) { + return $this->renderSuccess('添加成功', url('store.user/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新管理员 + * @param $user_id + * @return array|mixed + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function edit($user_id) + { + // 管理员详情 + $model = StoreUserModel::detail($user_id); + $model['roleIds'] = UserRole::getRoleIds($model['store_user_id']); + if (!$this->request->isAjax()) { + return $this->fetch('edit', [ + 'model' => $model, + // 角色列表 + 'roleList' => (new RoleModel)->getList(), + // 所有角色id + 'roleIds' => UserRole::getRoleIds($model['store_user_id']), + ]); + } + // 更新记录 + if ($model->edit($this->postData('user'))) { + return $this->renderSuccess('更新成功', url('store.user/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除管理员 + * @param $user_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($user_id) + { + // 管理员详情 + $model = StoreUserModel::detail($user_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 更新当前管理员信息 + * @return array|mixed + * @throws \think\exception\DbException + */ + public function renew() + { + // 管理员详情 + $model = StoreUserModel::detail($this->store['user']['store_user_id']); + if ($this->request->isAjax()) { + if ($model->renew($this->postData('user'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + return $this->fetch('renew', compact('model')); + } +} diff --git a/source/application/store/controller/upload/Library.php b/source/application/store/controller/upload/Library.php new file mode 100644 index 0000000..ec9675c --- /dev/null +++ b/source/application/store/controller/upload/Library.php @@ -0,0 +1,104 @@ +getList($type); + // 文件列表 + $file_list = (new UploadFileModel)->getlist(intval($group_id), $type, 0); + return $this->renderSuccess('success', '', compact('group_list', 'file_list')); + } + + /** + * 新增分组 + * @param $group_name + * @param string $group_type + * @return array + */ + public function addGroup($group_name, $group_type = 'image') + { + $model = new UploadGroupModel; + if ($model->add(compact('group_name', 'group_type'))) { + $group_id = $model->getLastInsID(); + return $this->renderSuccess('添加成功', '', compact('group_id', 'group_name')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑分组 + * @param $group_id + * @param $group_name + * @return array + * @throws \think\exception\DbException + */ + public function editGroup($group_id, $group_name) + { + $model = UploadGroupModel::detail($group_id); + if ($model->edit(compact('group_name'))) { + return $this->renderSuccess('修改成功'); + } + return $this->renderError($model->getError() ?: '修改失败'); + } + + /** + * 删除分组 + * @param $group_id + * @return array + * @throws \think\exception\DbException + */ + public function deleteGroup($group_id) + { + $model = UploadGroupModel::detail($group_id); + if ($model->remove()) { + return $this->renderSuccess('删除成功'); + } + return $this->renderError($model->getError() ?: '删除失败'); + } + + /** + * 批量删除文件 + * @param $fileIds + * @return array + */ + public function deleteFiles($fileIds) + { + $model = new UploadFileModel; + if ($model->softDelete($fileIds)) { + return $this->renderSuccess('删除成功'); + } + return $this->renderError($model->getError() ?: '删除失败'); + } + + /** + * 批量移动文件分组 + * @param $group_id + * @param $fileIds + * @return array + */ + public function moveFiles($group_id, $fileIds) + { + $model = new UploadFileModel; + if ($model->moveGroup($group_id, $fileIds) !== false) { + return $this->renderSuccess('移动成功'); + } + return $this->renderError($model->getError() ?: '移动失败'); + } +} diff --git a/source/application/store/controller/user/Balance.php b/source/application/store/controller/user/Balance.php new file mode 100644 index 0000000..841b083 --- /dev/null +++ b/source/application/store/controller/user/Balance.php @@ -0,0 +1,31 @@ +fetch('log', [ + // 充值记录列表 + 'list' => $model->getList($this->request->param()), + // 属性集 + 'attributes' => $model::getAttributes(), + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/user/Grade.php b/source/application/store/controller/user/Grade.php new file mode 100644 index 0000000..cac6056 --- /dev/null +++ b/source/application/store/controller/user/Grade.php @@ -0,0 +1,81 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加等级 + * @return array|bool|mixed + * @throws \Exception + */ + public function add() + { + $model = new GradeModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('grade'))) { + return $this->renderSuccess('添加成功', url('user.grade/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 编辑会员等级 + * @param $grade_id + * @return array|bool|mixed + * @throws \think\exception\DbException + */ + public function edit($grade_id) + { + // 会员等级详情 + $model = GradeModel::detail($grade_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 新增记录 + if ($model->edit($this->postData('grade'))) { + return $this->renderSuccess('更新成功', url('user.grade/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除会员等级 + * @param $grade_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($grade_id) + { + // 会员等级详情 + $model = GradeModel::detail($grade_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/user/Recharge.php b/source/application/store/controller/user/Recharge.php new file mode 100644 index 0000000..7de910b --- /dev/null +++ b/source/application/store/controller/user/Recharge.php @@ -0,0 +1,31 @@ +fetch('order', [ + // 充值记录列表 + 'list' => $model->getList($this->request->param()), + // 属性集 + 'attributes' => $model::getAttributes(), + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/controller/wxapp/Help.php b/source/application/store/controller/wxapp/Help.php new file mode 100644 index 0000000..1196ec2 --- /dev/null +++ b/source/application/store/controller/wxapp/Help.php @@ -0,0 +1,82 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 添加帮助 + * @return array|mixed + */ + public function add() + { + $model = new WxappHelpModel; + if (!$this->request->isAjax()) { + return $this->fetch('add'); + } + // 新增记录 + if ($model->add($this->postData('help'))) { + return $this->renderSuccess('添加成功', url('wxapp.help/index')); + } + return $this->renderError($model->getError() ?: '添加失败'); + } + + /** + * 更新帮助 + * @param $help_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($help_id) + { + // 帮助详情 + $model = WxappHelpModel::detail($help_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', compact('model')); + } + // 更新记录 + if ($model->edit($this->postData('help'))) { + return $this->renderSuccess('更新成功', url('wxapp.help/index')); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + + /** + * 删除帮助 + * @param $help_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($help_id) + { + // 帮助详情 + $model = WxappHelpModel::detail($help_id); + if (!$model->remove()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + +} diff --git a/source/application/store/controller/wxapp/Page.php b/source/application/store/controller/wxapp/Page.php new file mode 100644 index 0000000..35ee006 --- /dev/null +++ b/source/application/store/controller/wxapp/Page.php @@ -0,0 +1,145 @@ +getList(); + return $this->fetch('index', compact('list')); + } + + /** + * 新增页面 + * @return array|mixed + */ + public function add() + { + $model = new WxappPageModel; + if (!$this->request->isAjax()) { + return $this->fetch('edit', [ + 'defaultData' => json_encode($model->getDefaultItems()), + 'jsonData' => json_encode(['page' => $model->getDefaultPage(), 'items' => []]), + 'opts' => json_encode([ + 'catgory' => CategoryModel::getCacheTree(), + 'sharingCatgory' => SharingCategoryModel::getCacheTree(), + 'articleCatgory' => ArticleCategoryModel::getALL(), + ]) + ]); + } + // 接收post数据 + $post = $this->request->post('data', null, null); + if (!$model->add(json_decode($post, true))) { + return $this->renderError('添加失败'); + } + return $this->renderSuccess('添加成功', url('wxapp.page/index')); + } + + /** + * 编辑页面 + * @param $page_id + * @return array|mixed + * @throws \think\exception\DbException + */ + public function edit($page_id) + { + $model = WxappPageModel::detail($page_id); + if (!$this->request->isAjax()) { + return $this->fetch('edit', [ + 'defaultData' => json_encode($model->getDefaultItems()), + 'jsonData' => json_encode($model['page_data']), + 'opts' => json_encode([ + 'catgory' => CategoryModel::getCacheTree(), + 'sharingCatgory' => SharingCategoryModel::getCacheTree(), + 'articleCatgory' => ArticleCategoryModel::getALL(), + ]) + ]); + } + // 接收post数据 + $post = $this->request->post('data', null, null); + if (!$model->edit(json_decode($post, true))) { + return $this->renderError('更新失败'); + } + return $this->renderSuccess('更新成功'); + } + + /** + * 删除页面 + * @param $page_id + * @return array + * @throws \think\exception\DbException + */ + public function delete($page_id) + { + // 帮助详情 + $model = WxappPageModel::detail($page_id); + if (!$model->setDelete()) { + return $this->renderError($model->getError() ?: '删除失败'); + } + return $this->renderSuccess('删除成功'); + } + + /** + * 设置默认首页 + * @param $page_id + * @return array + * @throws \think\exception\DbException + */ + public function setHome($page_id) + { + // 帮助详情 + $model = WxappPageModel::detail($page_id); + if (!$model->setHome()) { + return $this->renderError($model->getError() ?: '设置失败'); + } + return $this->renderSuccess('设置成功'); + } + + /** + * 分类模板 + * @return array|mixed + * @throws \think\exception\DbException + */ + public function category() + { + $model = WxappCategoryModel::detail(); + if ($this->request->isAjax()) { + if ($model->edit($this->postData('category'))) { + return $this->renderSuccess('更新成功'); + } + return $this->renderError($model->getError() ?: '更新失败'); + } + return $this->fetch('category', compact('model')); + } + + /** + * 页面链接 + * @return mixed + */ + public function links() + { + return $this->fetch('links'); + } + +} diff --git a/source/application/store/extra/menus.php b/source/application/store/extra/menus.php new file mode 100644 index 0000000..a1af21e --- /dev/null +++ b/source/application/store/extra/menus.php @@ -0,0 +1,651 @@ + [ + * 'name' => '首页', // 菜单名称 + * 'icon' => 'icon-home', // 图标 (class) + * 'index' => 'index/index', // 链接 + * ], + */ +return [ + 'index' => [ + 'name' => '首页', + 'icon' => 'icon-home', + 'index' => 'index/index', + ], + 'store' => [ + 'name' => '管理员', + 'icon' => 'icon-guanliyuan', + 'index' => 'store.user/index', + 'submenu' => [ + [ + 'name' => '管理员列表', + 'index' => 'store.user/index', + 'uris' => [ + 'store.user/index', + 'store.user/add', + 'store.user/edit', + 'store.user/delete', + ], + ], + [ + 'name' => '角色管理', + 'index' => 'store.role/index', + 'uris' => [ + 'store.role/index', + 'store.role/add', + 'store.role/edit', + 'store.role/delete', + ], + ], + ] + ], + 'goods' => [ + 'name' => '商品管理', + 'icon' => 'icon-goods', + 'index' => 'goods/index', + 'submenu' => [ + [ + 'name' => '商品列表', + 'index' => 'goods/index', + 'uris' => [ + 'goods/index', + 'goods/add', + 'goods/edit', + 'goods/copy' + ], + ], + [ + 'name' => '商品分类', + 'index' => 'goods.category/index', + 'uris' => [ + 'goods.category/index', + 'goods.category/add', + 'goods.category/edit', + ], + ], + [ + 'name' => '商品评价', + 'index' => 'goods.comment/index', + 'uris' => [ + 'goods.comment/index', + 'goods.comment/detail', + ], + ] + ], + ], + 'order' => [ + 'name' => '订单管理', + 'icon' => 'icon-order', + 'index' => 'order/all_list', + 'submenu' => [ + [ + 'name' => '全部订单', + 'index' => 'order/all_list', + ], + [ + 'name' => '待发货', + 'index' => 'order/delivery_list', + ], + [ + 'name' => '待收货', + 'index' => 'order/receipt_list', + ], + [ + 'name' => '待付款', + 'index' => 'order/pay_list', + ], + [ + 'name' => '已完成', + 'index' => 'order/complete_list', + + ], + [ + 'name' => '已取消', + 'index' => 'order/cancel_list', + ], + [ + 'name' => '售后管理', + 'index' => 'order.refund/index', + 'uris' => [ + 'order.refund/index', + 'order.refund/detail', + ] + ], + ] + ], + 'user' => [ + 'name' => '用户管理', + 'icon' => 'icon-user', + 'index' => 'user/index', + 'submenu' => [ + [ + 'name' => '用户列表', + 'index' => 'user/index', + ], + [ + 'name' => '会员等级', + 'active' => true, + 'submenu' => [ + [ + 'name' => '等级管理', + 'index' => 'user.grade/index', + 'uris' => [ + 'user.grade/index', + 'user.grade/add', + 'user.grade/edit', + 'user.grade/delete', + ] + ], + ] + ], + [ + 'name' => '余额记录', + 'active' => true, + 'submenu' => [ + [ + 'name' => '充值记录', + 'index' => 'user.recharge/order', + ], + [ + 'name' => '余额明细', + 'index' => 'user.balance/log', + ], + ] + ], + ] + ], + 'shop' => [ + 'name' => '门店管理', + 'icon' => 'icon-shop', + 'index' => 'shop/index', + 'submenu' => [ + [ + 'name' => '门店管理', + 'active' => true, + 'index' => 'shop/index', + 'submenu' => [ + [ + 'name' => '门店列表', + 'index' => 'shop/index', + 'uris' => [ + 'shop/index', + 'shop/add', + 'shop/edit', + ] + ], + [ + 'name' => '店员管理', + 'index' => 'shop.clerk/index', + 'uris' => [ + 'shop.clerk/index', + 'shop.clerk/add', + 'shop.clerk/edit', + ] + ], + ] + ], + [ + 'name' => '订单核销记录', + 'index' => 'shop.order/index', + ] + ] + ], + 'content' => [ + 'name' => '内容管理', + 'icon' => 'icon-wenzhang', + 'index' => 'content.article/index', + 'submenu' => [ + [ + 'name' => '文章管理', + 'active' => true, + 'submenu' => [ + [ + 'name' => '文章列表', + 'index' => 'content.article/index', + 'uris' => [ + 'content.article/index', + 'content.article/add', + 'content.article/edit', + ] + ], + [ + 'name' => '文章分类', + 'index' => 'content.article.category/index', + 'uris' => [ + 'content.article.category/index', + 'content.article.category/add', + 'content.article.category/edit', + ] + ], + ] + ], + [ + 'name' => '文件库管理', + 'submenu' => [ + [ + 'name' => '文件分组', + 'index' => 'content.files.group/index', + 'uris' => [ + 'content.files.group/index', + 'content.files.group/add', + 'content.files.group/edit', + ] + ], + [ + 'name' => '文件列表', + 'index' => 'content.files/index' + ], + [ + 'name' => '回收站', + 'index' => 'content.files/recycle', + ], + ] + ], + ] + ], + 'market' => [ + 'name' => '营销管理', + 'icon' => 'icon-marketing', + 'index' => 'market.coupon/index', + 'submenu' => [ + [ + 'name' => '优惠券', +// 'active' => true, + 'submenu' => [ + [ + 'name' => '优惠券列表', + 'index' => 'market.coupon/index', + 'uris' => [ + 'market.coupon/index', + 'market.coupon/add', + 'market.coupon/edit', + ] + ], + [ + 'name' => '领取记录', + 'index' => 'market.coupon/receive' + ], + ] + ], + [ + 'name' => '用户充值', + 'submenu' => [ + [ + 'name' => '充值套餐', + 'index' => 'market.recharge.plan/index', + 'uris' => [ + 'market.recharge.plan/index', + 'market.recharge.plan/add', + 'market.recharge.plan/edit', + ] + ], + [ + 'name' => '充值设置', + 'index' => 'market.recharge/setting' + ], + ] + ], + [ + 'name' => '积分管理', + 'submenu' => [ + [ + 'name' => '积分设置', + 'index' => 'market.points/setting' + ], + [ + 'name' => '积分明细', + 'index' => 'market.points/log' + ], + ] + ], + [ + 'name' => '消息推送', + 'submenu' => [ + [ + 'name' => '发送消息', + 'index' => 'market.push/send', + ], + [ + 'name' => '活跃用户', + 'index' => 'market.push/user', + ], +// [ +// 'name' => '发送日志', +// 'index' => 'market.push/log', +// ], + ] + ], + [ + 'name' => '满额包邮', + 'index' => 'market.basic/full_free', + ], + ], + ], + 'statistics' => [ + 'name' => '数据统计', + 'icon' => 'icon-qushitu', + 'index' => 'statistics.data/index', + ], + 'wxapp' => [ + 'name' => '小程序', + 'icon' => 'icon-wxapp', + 'color' => '#36b313', + 'index' => 'wxapp/setting', + 'submenu' => [ + [ + 'name' => '小程序设置', + 'index' => 'wxapp/setting', + ], + [ + 'name' => '页面管理', + 'active' => true, + 'submenu' => [ + [ + 'name' => '页面设计', + 'index' => 'wxapp.page/index', + 'uris' => [ + 'wxapp.page/index', + 'wxapp.page/add', + 'wxapp.page/edit', + ] + ], + [ + 'name' => '分类模板', + 'index' => 'wxapp.page/category' + ], + [ + 'name' => '页面链接', + 'index' => 'wxapp.page/links' + ] + ] + ], + [ + 'name' => '帮助中心', + 'index' => 'wxapp.help/index', + 'uris' => [ + 'wxapp.help/index', + 'wxapp.help/add', + 'wxapp.help/edit' + ] + ], + ], + ], + 'apps' => [ + 'name' => '应用中心', + 'icon' => 'icon-application', + 'is_svg' => true, // 多色图标 + 'index' => 'apps.dealer.apply/index', + 'submenu' => [ + [ + 'name' => '分销中心', + 'submenu' => [ + [ + 'name' => '入驻申请', + 'index' => 'apps.dealer.apply/index', + ], + [ + 'name' => '分销商用户', + 'index' => 'apps.dealer.user/index', + 'uris' => [ + 'apps.dealer.user/index', + 'apps.dealer.user/edit', + 'apps.dealer.user/fans', + ] + ], + [ + 'name' => '分销订单', + 'index' => 'apps.dealer.order/index', + ], + [ + 'name' => '提现申请', + 'index' => 'apps.dealer.withdraw/index', + ], + [ + 'name' => '分销设置', + 'index' => 'apps.dealer.setting/index', + ], + [ + 'name' => '分销海报', + 'index' => 'apps.dealer.setting/qrcode', + ], + ] + ], + [ + 'name' => '拼团管理', + 'submenu' => [ + [ + 'name' => '商品分类', + 'index' => 'apps.sharing.category/index', + 'uris' => [ + 'apps.sharing.category/index', + 'apps.sharing.category/add', + 'apps.sharing.category/edit', + ] + ], + [ + 'name' => '商品列表', + 'index' => 'apps.sharing.goods/index', + 'uris' => [ + 'apps.sharing.goods/index', + 'apps.sharing.goods/add', + 'apps.sharing.goods/edit', + 'apps.sharing.goods/copy', + 'apps.sharing.goods/copy_master', + ] + ], + [ + 'name' => '拼单管理', + 'index' => 'apps.sharing.active/index', + 'uris' => [ + 'apps.sharing.active/index', + 'apps.sharing.active/users', + ] + ], + [ + 'name' => '订单管理', + 'index' => 'apps.sharing.order/index', + 'uris' => [ + 'apps.sharing.order/index', + 'apps.sharing.order/detail', + 'apps.sharing.order.operate/batchdelivery' + ] + ], + [ + 'name' => '售后管理', + 'index' => 'apps.sharing.order.refund/index', + 'uris' => [ + 'apps.sharing.order.refund/index', + 'apps.sharing.order.refund/detail', + ] + ], + [ + 'name' => '商品评价', + 'index' => 'apps.sharing.comment/index', + 'uris' => [ + 'apps.sharing.comment/index', + 'apps.sharing.comment/detail', + ], + ], + [ + 'name' => '拼团设置', + 'index' => 'apps.sharing.setting/index' + ] + ] + ], + [ + 'name' => '砍价活动', + 'index' => 'apps.bargain.active/index', + 'submenu' => [ + [ + 'name' => '活动列表', + 'index' => 'apps.bargain.active/index', + 'uris' => [ + 'apps.bargain.active/index', + 'apps.bargain.active/add', + 'apps.bargain.active/edit', + 'apps.bargain.active/delete', + ], + ], + [ + 'name' => '砍价记录', + 'index' => 'apps.bargain.task/index', + 'uris' => [ + 'apps.bargain.task/index', + 'apps.bargain.task/add', + 'apps.bargain.task/edit', + 'apps.bargain.task/delete', + 'apps.bargain.task/help', + ], + ], + [ + 'name' => '砍价设置', + 'index' => 'apps.bargain.setting/index', + ] + ] + ], + [ + 'name' => '整点秒杀', + 'index' => 'apps.sharp.goods/index', + 'submenu' => [ + [ + 'name' => '秒杀商品', + 'index' => 'apps.sharp.goods/index', + 'uris' => [ + 'apps.sharp.goods/index', + 'apps.sharp.goods/add', + 'apps.sharp.goods/select', + 'apps.sharp.goods/edit', + 'apps.sharp.goods/delete', + ], + ], + [ + 'name' => '活动会场', + 'index' => 'apps.sharp.active/index', + 'uris' => [ + 'apps.sharp.active/index', + 'apps.sharp.active/add', + 'apps.sharp.active/edit', + 'apps.sharp.active/state', + 'apps.sharp.active/delete', + + 'apps.sharp.active_time/index', + 'apps.sharp.active_time/add', + 'apps.sharp.active_time/edit', + 'apps.sharp.active_time/state', + 'apps.sharp.active_time/delete', + ], + ], + [ + 'name' => '基础设置', + 'index' => 'apps.sharp.setting/index', + ] + ] + ], + [ + 'name' => '好物圈', + 'index' => 'apps.wow.order/index', + 'submenu' => [ + [ + 'name' => '商品收藏', + 'index' => 'apps.wow.shoping/index', + ], + [ + 'name' => '订单信息', + 'index' => 'apps.wow.order/index', + ], + [ + 'name' => '基础设置', + 'index' => 'apps.wow.setting/index', + ] + ] + ], + ] + ], + 'setting' => [ + 'name' => '设置', + 'icon' => 'icon-setting', + 'index' => 'setting/store', + 'submenu' => [ + [ + 'name' => '商城设置', + 'index' => 'setting/store', + ], + [ + 'name' => '交易设置', + 'index' => 'setting/trade', + ], + [ + 'name' => '运费模板', + 'index' => 'setting.delivery/index', + 'uris' => [ + 'setting.delivery/index', + 'setting.delivery/add', + 'setting.delivery/edit', + ], + ], + [ + 'name' => '物流公司', + 'index' => 'setting.express/index', + 'uris' => [ + 'setting.express/index', + 'setting.express/add', + 'setting.express/edit', + ], + ], + [ + 'name' => '短信通知', + 'index' => 'setting/sms' + ], + [ + 'name' => '模板消息', + 'index' => 'setting/tplmsg', + 'uris' => [ + 'setting/tplmsg', + 'setting.help/tplmsg' + + ], + ], + [ + 'name' => '退货地址', + 'index' => 'setting.address/index', + 'uris' => [ + 'setting.address/index', + 'setting.address/add', + 'setting.address/edit', + ], + ], + [ + 'name' => '上传设置', + 'index' => 'setting/storage', + ], + [ + 'name' => '小票打印机', + 'submenu' => [ + [ + 'name' => '打印机管理', + 'index' => 'setting.printer/index', + 'uris' => [ + 'setting.printer/index', + 'setting.printer/add', + 'setting.printer/edit' + ] + ], + [ + 'name' => '打印设置', + 'index' => 'setting/printer' + ] + ] + ], + [ + 'name' => '其他', + 'submenu' => [ + [ + 'name' => '清理缓存', + 'index' => 'setting.cache/clear' + ] + ] + ] + ], + ], +]; diff --git a/source/application/store/model/Article.php b/source/application/store/model/Article.php new file mode 100644 index 0000000..7e5eb9e --- /dev/null +++ b/source/application/store/model/Article.php @@ -0,0 +1,88 @@ +with(['image', 'category']) + ->where('is_delete', '=', 0) + ->order(['article_sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + + } + + /** + * 新增记录 + * @param $data + * @return false|int + */ + public function add($data) + { + if (empty($data['image_id'])) { + $this->error = '请上传封面图'; + return false; + } + if (empty($data['article_content'])) { + $this->error = '请输入文章内容'; + return false; + } + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + if (empty($data['image_id'])) { + $this->error = '请上传封面图'; + return false; + } + if (empty($data['article_content'])) { + $this->error = '请输入文章内容'; + return false; + } + return $this->allowField(true)->save($data) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + + /** + * 获取文章总数量 + * @param array $where + * @return int|string + */ + public static function getArticleTotal($where = []) + { + $model = new static; + !empty($where) && $model->where($where); + return $model->where('is_delete', '=', 0)->count(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/Category.php b/source/application/store/model/Category.php new file mode 100644 index 0000000..fe9c4a9 --- /dev/null +++ b/source/application/store/model/Category.php @@ -0,0 +1,77 @@ +deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + // 验证:一级分类如果存在子类,则不允许移动 + if ($data['parent_id'] > 0 && static::hasSubCategory($this['category_id'])) { + $this->error = '该分类下存在子分类,不可以移动'; + return false; + } + $this->deleteCache(); + !array_key_exists('image_id', $data) && $data['image_id'] = 0; + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除商品分类 + * @param $categoryId + * @return bool|int + */ + public function remove($categoryId) + { + // 判断是否存在商品 + if ($goodsCount = (new Goods)->getGoodsTotal(['category_id' => $categoryId])) { + $this->error = '该分类下存在' . $goodsCount . '个商品,不允许删除'; + return false; + } + // 判断是否存在子分类 + if (static::hasSubCategory($categoryId)) { + $this->error = '该分类下存在子分类,请先删除'; + return false; + } + $this->deleteCache(); + return $this->delete(); + } + + /** + * 删除缓存 + * @return bool + */ + private function deleteCache() + { + return Cache::rm('category_' . static::$wxapp_id); + } + +} diff --git a/source/application/store/model/Comment.php b/source/application/store/model/Comment.php new file mode 100644 index 0000000..7559c12 --- /dev/null +++ b/source/application/store/model/Comment.php @@ -0,0 +1,32 @@ +save(['is_delete' => 1]); + } + + /** + * 获取评价总数量 + * @return int|string + */ + public function getCommentTotal() + { + return $this->where(['is_delete' => 0])->count(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/CommentImage.php b/source/application/store/model/CommentImage.php new file mode 100644 index 0000000..f4704f0 --- /dev/null +++ b/source/application/store/model/CommentImage.php @@ -0,0 +1,14 @@ +where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 添加新记录 + * @param $data + * @return false|int + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + if ($data['expire_type'] == '20') { + $data['start_time'] = strtotime($data['start_time']); + $data['end_time'] = strtotime($data['end_time']); + } + return $this->allowField(true)->save($data); + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + if ($data['expire_type'] == '20') { + $data['start_time'] = strtotime($data['start_time']); + $data['end_time'] = strtotime($data['end_time']); + } + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除记录 (软删除) + * @return bool|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]) !== false; + } + +} diff --git a/source/application/store/model/Delivery.php b/source/application/store/model/Delivery.php new file mode 100644 index 0000000..ded2745 --- /dev/null +++ b/source/application/store/model/Delivery.php @@ -0,0 +1,167 @@ +onValidate($data)) return false; + // 保存数据 + $data['wxapp_id'] = self::$wxapp_id; + if ($this->allowField(true)->save($data)) { + return $this->createDeliveryRule($data['rule']); + } + return false; + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function edit($data) + { + // 表单验证 + if (!$this->onValidate($data)) return false; + // 保存数据 + if ($this->allowField(true)->save($data)) { + return $this->createDeliveryRule($data['rule']); + } + return false; + } + + /** + * 表单验证 + * @param $data + * @return bool + */ + private function onValidate($data) + { + if (!isset($data['rule']) || empty($data['rule'])) { + $this->error = '请选择可配送区域'; + return false; + } + foreach ($data['rule']['first'] as $value) { + if ((int)$value <= 0 || (int)$value != $value) { + $this->error = '首件或首重必须是正整数'; + return false; + } + } + foreach ($data['rule']['additional'] as $value) { + if ((int)$value <= 0 || (int)$value != $value) { + $this->error = '续件或续重必须是正整数'; + return false; + } + } + return true; + } + + /** + * 获取配送区域及运费设置项 + * @return array + */ + public function getFormList() + { + // 所有地区 + $regions = Region::getCacheAll(); + $list = []; + foreach ($this['rule'] as $rule) { + $citys = explode(',', $rule['region']); + $province = []; + foreach ($citys as $cityId) { + if (!isset($regions[$cityId])) continue; + !in_array($regions[$cityId]['pid'], $province) && $province[] = $regions[$cityId]['pid']; + } + $list[] = [ + 'first' => $rule['first'], + 'first_fee' => $rule['first_fee'], + 'additional' => $rule['additional'], + 'additional_fee' => $rule['additional_fee'], + 'province' => $province, + 'citys' => $citys, + ]; + } + return $list; + } + + /** + * 添加模板区域及运费 + * @param $data + * @return int + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + private function createDeliveryRule($data) + { + $save = []; + $connt = count($data['region']); + for ($i = 0; $i < $connt; $i++) { + $save[] = [ + 'region' => $data['region'][$i], + 'first' => $data['first'][$i], + 'first_fee' => $data['first_fee'][$i], + 'additional' => $data['additional'][$i], + 'additional_fee' => $data['additional_fee'][$i], + 'wxapp_id' => self::$wxapp_id + ]; + } + $this->rule()->delete(); + return $this->rule()->saveAll($save); + } + + /** + * 删除记录 + * @return int + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function remove() + { + // 验证运费模板是否被商品使用 + if (!$this->checkIsUseGoods($this['delivery_id'])) { + return false; + } + // 删除运费模板 + $this->rule()->delete(); + return $this->delete(); + } + + /** + * 验证运费模板是否被商品使用 + * @param int $deliveryId + * @return bool + * @throws \think\Exception + */ + private function checkIsUseGoods($deliveryId) + { + // 判断是否存在商品 + $goodsCount = (new GoodsModel)->where('delivery_id', '=', $deliveryId) + ->where('is_delete', '=', 0) + ->count(); + if ($goodsCount > 0) { + $this->error = '该模板被' . $goodsCount . '个商品使用,不允许删除'; + return false; + } + return true; + } + +} diff --git a/source/application/store/model/DeliveryRule.php b/source/application/store/model/DeliveryRule.php new file mode 100644 index 0000000..2fa7e42 --- /dev/null +++ b/source/application/store/model/DeliveryRule.php @@ -0,0 +1,55 @@ + $citys) { + $str .= self::$regionTree[$provinceId]['name']; + if (count($citys) !== count(self::$regionTree[$provinceId]['city'])) { + $cityStr = ''; + foreach ($citys as $cityId) + $cityStr .= self::$regionTree[$provinceId]['city'][$cityId]['name']; + $str .= ' (' . mb_substr($cityStr, 0, -1, 'utf-8') . ')'; + } + $str .= '、'; + } + return mb_substr($str, 0, -1, 'utf-8'); + } + +} diff --git a/source/application/store/model/Express.php b/source/application/store/model/Express.php new file mode 100644 index 0000000..d9dc09d --- /dev/null +++ b/source/application/store/model/Express.php @@ -0,0 +1,45 @@ +allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + return $this->allowField(true)->save($data); + } + + /** + * 删除记录 + * @return bool|int + */ + public function remove() + { + // 判断当前物流公司是否已被订单使用 + $Order = new Order; + if ($orderCount = $Order->where(['express_id' => $this['express_id']])->count()) { + $this->error = '当前物流公司已被' . $orderCount . '个订单使用,不允许删除'; + return false; + } + return $this->delete(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/Goods.php b/source/application/store/model/Goods.php new file mode 100644 index 0000000..d2a5a6e --- /dev/null +++ b/source/application/store/model/Goods.php @@ -0,0 +1,149 @@ +error = '请上传商品图片'; + return false; + } + $data['content'] = isset($data['content']) ? $data['content'] : ''; + $data['wxapp_id'] = $data['sku']['wxapp_id'] = self::$wxapp_id; + + // 开启事务 + $this->startTrans(); + try { + // 添加商品 + $this->allowField(true)->save($data); + // 商品规格 + $this->addGoodsSpec($data); + // 商品图片 + $this->addGoodsImages($data['images']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 添加商品图片 + * @param $images + * @return int + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + private function addGoodsImages($images) + { + $this->image()->delete(); + $data = array_map(function ($image_id) { + return [ + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + }, $images); + return $this->image()->saveAll($data); + } + + /** + * 编辑商品 + * @param $data + * @return bool|mixed + */ + public function edit($data) + { + if (!isset($data['images']) || empty($data['images'])) { + $this->error = '请上传商品图片'; + return false; + } + $data['spec_type'] = isset($data['spec_type']) ? $data['spec_type'] : $this['spec_type']; + $data['content'] = isset($data['content']) ? $data['content'] : ''; + $data['wxapp_id'] = $data['sku']['wxapp_id'] = self::$wxapp_id; + return $this->transaction(function () use ($data) { + // 保存商品 + $this->allowField(true)->save($data); + // 商品规格 + $this->addGoodsSpec($data, true); + // 商品图片 + $this->addGoodsImages($data['images']); + return true; + }); + } + + /** + * 添加商品规格 + * @param $data + * @param $isUpdate + * @throws \Exception + */ + private function addGoodsSpec($data, $isUpdate = false) + { + // 更新模式: 先删除所有规格 + $model = new GoodsSku; + $isUpdate && $model->removeAll($this['goods_id']); + // 添加规格数据 + if ($data['spec_type'] == '10') { + // 单规格 + $this->sku()->save($data['sku']); + } else if ($data['spec_type'] == '20') { + // 添加商品与规格关系记录 + $model->addGoodsSpecRel($this['goods_id'], $data['spec_many']['spec_attr']); + // 添加商品sku + $model->addSkuList($this['goods_id'], $data['spec_many']['spec_list']); + } + } + + /** + * 修改商品状态 + * @param $state + * @return false|int + */ + public function setStatus($state) + { + return $this->allowField(true)->save(['goods_status' => $state ? 10 : 20]) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + if (!GoodsService::checkIsAllowDelete($this['goods_id'])) { + $this->error = '当前商品正在参与其他活动,不允许删除'; + return false; + } + return $this->allowField(true)->save(['is_delete' => 1]); + } + + /** + * 获取当前商品总数 + * @param array $where + * @return int|string + * @throws \think\Exception + */ + public function getGoodsTotal($where = []) + { + return $this->where('is_delete', '=', 0)->where($where)->count(); + } + +} diff --git a/source/application/store/model/GoodsImage.php b/source/application/store/model/GoodsImage.php new file mode 100644 index 0000000..01bb1bf --- /dev/null +++ b/source/application/store/model/GoodsImage.php @@ -0,0 +1,14 @@ + $item['spec_sku_id'], + 'goods_id' => $goods_id, + 'wxapp_id' => self::$wxapp_id, + ]); + } + return $this->allowField(true)->saveAll($data); + } + + /** + * 添加商品规格关系记录 + * @param $goods_id + * @param $spec_attr + * @return array|false + * @throws \Exception + */ + public function addGoodsSpecRel($goods_id, $spec_attr) + { + $data = []; + array_map(function ($val) use (&$data, $goods_id) { + array_map(function ($item) use (&$val, &$data, $goods_id) { + $data[] = [ + 'goods_id' => $goods_id, + 'spec_id' => $val['group_id'], + 'spec_value_id' => $item['item_id'], + 'wxapp_id' => self::$wxapp_id, + ]; + }, $val['spec_items']); + }, $spec_attr); + $model = new GoodsSpecRel; + return $model->saveAll($data); + } + + /** + * 移除指定商品的所有sku + * @param $goods_id + * @return int + */ + public function removeAll($goods_id) + { + $model = new GoodsSpecRel; + $model->where('goods_id','=', $goods_id)->delete(); + return $this->where('goods_id','=', $goods_id)->delete(); + } + +} diff --git a/source/application/store/model/GoodsSpecRel.php b/source/application/store/model/GoodsSpecRel.php new file mode 100644 index 0000000..c958492 --- /dev/null +++ b/source/application/store/model/GoodsSpecRel.php @@ -0,0 +1,14 @@ +setWhere($query); + // 获取数据列表 + return $this->with(['goods.image', 'address', 'user']) + ->alias('order') + ->field('order.*') + ->join('user', 'user.user_id = order.user_id') + ->where($this->transferDataType($dataType)) + ->where('order.is_delete', '=', 0) + ->order(['order.create_time' => 'desc']) + ->paginate(10, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 订单列表(全部) + * @param $dataType + * @param array $query + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListAll($dataType, $query = []) + { + // 检索查询条件 + !empty($query) && $this->setWhere($query); + // 获取数据列表 + return $this->with(['goods.image', 'address', 'user', 'extract', 'extract_shop']) + ->alias('order') + ->field('order.*') + ->join('user', 'user.user_id = order.user_id') + ->where($this->transferDataType($dataType)) + ->where('order.is_delete', '=', 0) + ->order(['order.create_time' => 'desc']) + ->select(); + } + + /** + * 订单导出 + * @param $dataType + * @param $query + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function exportList($dataType, $query) + { + // 获取订单列表 + $list = $this->getListAll($dataType, $query); + // 导出csv文件 + return (new Exportservice)->orderList($list); + } + + /** + * 批量发货模板 + */ + public function deliveryTpl() + { + return (new Exportservice)->deliveryTpl(); + } + + /** + * 设置检索查询条件 + * @param $query + */ + private function setWhere($query) + { + if (isset($query['search']) && !empty($query['search'])) { + $this->where('order_no|user.nickName', 'like', '%' . trim($query['search']) . '%'); + } + if (isset($query['start_time']) && !empty($query['start_time'])) { + $this->where('order.create_time', '>=', strtotime($query['start_time'])); + } + if (isset($query['end_time']) && !empty($query['end_time'])) { + $this->where('order.create_time', '<', strtotime($query['end_time']) + 86400); + } + if (isset($query['delivery_type']) && !empty($query['delivery_type'])) { + $query['delivery_type'] > -1 && $this->where('delivery_type', '=', $query['delivery_type']); + } + if (isset($query['extract_shop_id']) && !empty($query['extract_shop_id'])) { + $query['extract_shop_id'] > -1 && $this->where('extract_shop_id', '=', $query['extract_shop_id']); + } + // 用户id + if (isset($query['user_id']) && $query['user_id'] > 0) { + $this->where('order.user_id', '=', (int)$query['user_id']); + } + } + + /** + * 转义数据类型条件 + * @param $dataType + * @return array + */ + private function transferDataType($dataType) + { + // 数据类型 + $filter = []; + switch ($dataType) { + case 'delivery': + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 10, + 'order_status' => ['in', [10, 21]] + ]; + break; + case 'receipt': + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 20, + 'receipt_status' => 10 + ]; + break; + case 'pay': + $filter = ['pay_status' => 10, 'order_status' => 10]; + break; + case 'complete': + $filter = ['order_status' => 30]; + break; + case 'cancel': + $filter = ['order_status' => 20]; + break; + case 'all': + $filter = []; + break; + } + return $filter; + } + + /** + * 确认发货(单独订单) + * @param $data + * @return array|bool|false + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \Exception + */ + public function delivery($data) + { + // 转义为订单列表 + $orderList = [$this]; + // 验证订单是否满足发货条件 + if (!$this->verifyDelivery($orderList)) { + return false; + } + // 整理更新的数据 + $updateList = [[ + 'order_id' => $this['order_id'], + 'express_id' => $data['express_id'], + 'express_no' => $data['express_no'] + ]]; + // 更新订单发货状态 + if ($status = $this->updateToDelivery($updateList)) { + // 获取已发货的订单 + $completed = self::detail($this['order_id'], ['user', 'address', 'goods', 'express']); + // 发送消息通知 + $this->sendDeliveryMessage([$completed]); + // 同步好物圈订单 + (new WowService($this['wxapp_id']))->update([$completed]); + } + return $status; + } + + /** + * 批量发货 + * @param $data + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function batchDelivery($data) + { + // 获取csv文件中的数据 + if (!$csvData = $this->getCsvData()) { + return false; + } + // 整理订单id集 + $orderNos = helper::getArrayColumn($csvData, 0); + // 获取订单列表数据 + $orderList = helper::arrayColumn2Key($this->getListByOrderNos($orderNos), 'order_no'); + // 验证订单是否存在 + $tempArr = array_values(array_diff($orderNos, array_keys($orderList))); + if (!empty($tempArr)) { + $this->error = "订单号[{$tempArr[0]}] 不存在!"; + return false; + } + // 整理物流单号 + $updateList = []; + foreach ($csvData as $item) { + $updateList[] = [ + 'order_id' => $orderList[$item[0]]['order_id'], + 'express_id' => $data['express_id'], + 'express_no' => $item[1], + ]; + } + // 验证订单是否满足发货条件 + if (!$this->verifyDelivery($orderList)) { + return false; + } + // 更新订单发货状态(批量) + if ($status = $this->updateToDelivery($updateList)) { + // 获取已发货的订单 + $completed = $this->getListByOrderNos($orderNos, ['user', 'address', 'goods', 'express']); + // 发送消息通知 + $this->sendDeliveryMessage($completed); + // 同步好物圈订单 + (new WowService(self::$wxapp_id))->update($completed); + } + return $status; + } + + /** + * 确认发货后发送消息通知 + * @param array|\think\Collection $orderList + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function sendDeliveryMessage($orderList) + { + // 实例化消息通知服务类 + $Service = new MessageService; + foreach ($orderList as $item) { + // 发送消息通知 + $Service->delivery($item, OrderTypeEnum::MASTER); + } + return true; + } + + /** + * 更新订单发货状态(批量) + * @param $orderList + * @return array|false + * @throws \Exception + */ + private function updateToDelivery($orderList) + { + $data = []; + foreach ($orderList as $item) { + $data[] = [ + 'order_id' => $item['order_id'], + 'express_no' => $item['express_no'], + 'express_id' => $item['express_id'], + 'delivery_status' => 20, + 'delivery_time' => time(), + ]; + } + return $this->isUpdate()->saveAll($data); + } + + /** + * 验证订单是否满足发货条件 + * @param $orderList + * @return bool + */ + private function verifyDelivery($orderList) + { + foreach ($orderList as $order) { + if ( + $order['pay_status']['value'] != 20 + || $order['delivery_type']['value'] != DeliveryTypeEnum::EXPRESS + || $order['delivery_status']['value'] != 10 + ) { + $this->error = "订单号[{$order['order_no']}] 不满足发货条件!"; + return false; + } + } + return true; + } + + /** + * 获取csv文件中的数据 + * @return array|bool + */ + private function getCsvData() + { + // 获取表单上传文件 例如上传了001.jpg + $file = \request()->file('iFile'); + if (empty($file)) { + $this->error = '请上传发货模板'; + return false; + } + // 设置区域信息 + setlocale(LC_ALL, 'zh_CN'); + // 打开上传的文件 + $csvFile = fopen($file->getInfo()['tmp_name'], 'r'); + // 忽略第一行(csv标题) + fgetcsv($csvFile); + // 遍历并记录订单信息 + $orderList = []; + while ($item = fgetcsv($csvFile)) { + if (!isset($item[0]) || empty($item[0]) || !isset($item[1]) || empty($item[1])) { + $this->error = '模板文件数据不合法'; + return false; + } + $orderList[] = $item; + } + if (empty($orderList)) { + $this->error = '模板文件中没有订单数据'; + return false; + } + return $orderList; + } + + /** + * 修改订单价格 + * @param $data + * @return bool + */ + public function updatePrice($data) + { + if ($this['pay_status']['value'] != 10) { + $this->error = '该订单不合法'; + return false; + } + // 实际付款金额 + $payPrice = bcadd($data['update_price'], $data['update_express_price'], 2); + if ($payPrice <= 0) { + $this->error = '订单实付款价格不能为0.00元'; + return false; + } + return $this->save([ + 'order_no' => $this->orderNo(), // 修改订单号, 否则微信支付提示重复 + 'order_price' => $data['update_price'], + 'pay_price' => $payPrice, + 'update_price' => helper::bcsub($data['update_price'], helper::bcsub($this['total_price'], $this['coupon_money'])), + 'express_price' => $data['update_express_price'] + ]) !== false; + } + + /** + * 审核:用户取消订单 + * @param $data + * @return bool|mixed + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function confirmCancel($data) + { + // 判断订单是否有效 + if ($this['pay_status']['value'] != 20) { + $this->error = '该订单不合法'; + return false; + } + // 订单取消事件 + $status = $this->transaction(function () use ($data) { + if ($data['is_cancel'] == true) { + // 执行退款操作 + (new RefundService)->execute($this); + // 回退商品库存 + FactoryStock::getFactory($this['order_source'])->backGoodsStock($this['goods'], true); + // 回退用户优惠券 + $this['coupon_id'] > 0 && UserCouponModel::setIsUse($this['coupon_id'], false); + // 回退用户积分 + $User = UserModel::detail($this['user_id']); + $describe = "订单取消:{$this['order_no']}"; + $this['points_num'] > 0 && $User->setIncPoints($this['points_num'], $describe); + } + // 更新订单状态 + return $this->save(['order_status' => $data['is_cancel'] ? 20 : 10]); + }); + if ($status == true) { + // 同步好物圈订单 + (new WowService(self::$wxapp_id))->update([$this]); + } + return $status; + } + + /** + * 获取已付款订单总数 (可指定某天) + * @param null $startDate + * @param null $endDate + * @return int|string + * @throws \think\Exception + */ + public function getPayOrderTotal($startDate = null, $endDate = null) + { + $filter = [ + 'pay_status' => PayStatusEnum::SUCCESS, + 'order_status' => ['<>', OrderStatusEnum::CANCELLED], + ]; + if (!is_null($startDate) && !is_null($endDate)) { + $filter['pay_time'] = [ + ['>=', strtotime($startDate)], + ['<', strtotime($endDate) + 86400], + ]; + } + return $this->getOrderTotal($filter); + } + + /** + * 获取订单总数量 + * @param array $filter + * @return int|string + * @throws \think\Exception + */ + private function getOrderTotal($filter = []) + { + return $this->where($filter) + ->where('is_delete', '=', 0) + ->count(); + } + + /** + * 获取某天的总销售额 + * @param null $startDate + * @param null $endDate + * @return float|int + */ + public function getOrderTotalPrice($startDate = null, $endDate = null) + { + if (!is_null($startDate) && !is_null($endDate)) { + $this->where('pay_time', '>=', strtotime($startDate)) + ->where('pay_time', '<', strtotime($endDate) + 86400); + } + return $this->where('pay_status', '=', 20) + ->where('order_status', '<>', 20) + ->where('is_delete', '=', 0) + ->sum('pay_price'); + } + + /** + * 获取某天的下单用户数 + * @param $day + * @return float|int + */ + public function getPayOrderUserTotal($day) + { + $startTime = strtotime($day); + $userIds = $this->distinct(true) + ->where('pay_time', '>=', $startTime) + ->where('pay_time', '<', $startTime + 86400) + ->where('pay_status', '=', 20) + ->where('is_delete', '=', 0) + ->column('user_id'); + return count($userIds); + } + +} diff --git a/source/application/store/model/OrderAddress.php b/source/application/store/model/OrderAddress.php new file mode 100644 index 0000000..a6393d3 --- /dev/null +++ b/source/application/store/model/OrderAddress.php @@ -0,0 +1,15 @@ +where('order.order_no', 'like', "%{$query['order_no']}%"); + } + // 查询条件:起始日期 + if (isset($query['start_time']) && !empty($query['start_time'])) { + $this->where('m.create_time', '>=', strtotime($query['start_time'])); + } + // 查询条件:截止日期 + if (isset($query['end_time']) && !empty($query['end_time'])) { + $this->where('m.create_time', '<', strtotime($query['end_time']) + 86400); + } + // 售后类型 + if (isset($query['type']) && $query['type'] > 0) { + $this->where('m.type', '=', $query['type']); + } + // 处理状态 + if (isset($query['state']) && is_numeric($query['state'])) { + $this->where('m.status', '=', $query['state']); + } + // 获取列表数据 + return $this->alias('m') + ->field('m.*, order.order_no') + ->with(['order_goods.image', 'orderMaster', 'user']) + ->join('order', 'order.order_id = m.order_id') + ->order(['m.create_time' => 'desc']) + ->paginate(10, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 商家审核 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function audit($data) + { + if ($data['is_agree'] == 20 && empty($data['refuse_desc'])) { + $this->error = '请输入拒绝原因'; + return false; + } + if ($data['is_agree'] == 10 && empty($data['address_id'])) { + $this->error = '请选择退货地址'; + return false; + } + $this->startTrans(); + try { + // 拒绝申请, 标记售后单状态为已拒绝 + $data['is_agree'] == 20 && $data['status'] = 10; + // 同意换货申请, 标记售后单状态为已完成 + $data['is_agree'] == 10 && $this['type']['value'] == 20 && $data['status'] = 20; + // 更新退款单状态 + $this->allowField(true)->save($data); + // 同意售后申请, 记录退货地址 + if ($data['is_agree'] == 10) { + $model = new OrderRefundAddress; + $model->add($this['order_refund_id'], $data['address_id']); + } + // 订单详情 + $order = Order::detail($this['order_id']); + // 发送模板消息 + (new MessageService)->refund(self::detail($this['order_refund_id']), $order['order_no'], OrderTypeEnum::MASTER); + // 事务提交 + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 确认收货并退款 + * @param $data + * @return bool + * @throws \think\exception\DbException + */ + public function receipt($data) + { + // 订单详情 + $order = Order::detail($this['order_id']); + if ($data['refund_money'] > min($order['pay_price'], $this['order_goods']['total_pay_price'])) { + $this->error = '退款金额不能大于商品实付款金额'; + return false; + } + $this->transaction(function () use ($order, $data) { + // 更新售后单状态 + $this->allowField(true)->save([ + 'refund_money' => $data['refund_money'], + 'is_receipt' => 1, + 'status' => 20 + ]); + // 消减用户的实际消费金额 + // 条件:判断订单是否已结算 + if ($order['is_settled'] == true) { + (new UserModel)->setDecUserExpend($order['user_id'], $data['refund_money']); + } + // 执行原路退款 + (new RefundService)->execute($order, $data['refund_money']); + // 发送模板消息 + (new MessageService)->refund(self::detail($this['order_refund_id']), $order['order_no'], OrderTypeEnum::MASTER); + }); + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/OrderRefundAddress.php b/source/application/store/model/OrderRefundAddress.php new file mode 100644 index 0000000..7b1fa74 --- /dev/null +++ b/source/application/store/model/OrderRefundAddress.php @@ -0,0 +1,33 @@ +save([ + 'order_refund_id' => $order_refund_id, + 'name' => $detail['name'], + 'phone' => $detail['phone'], + 'detail' => $detail['detail'], + 'wxapp_id' => self::$wxapp_id + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/OrderRefundImage.php b/source/application/store/model/OrderRefundImage.php new file mode 100644 index 0000000..635d155 --- /dev/null +++ b/source/application/store/model/OrderRefundImage.php @@ -0,0 +1,10 @@ +allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + $data['printer_config'] = $data[$data['printer_type']]; + return $this->allowField(true)->save($data); + } + + /** + * 删除记录 + * @return bool|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/Region.php b/source/application/store/model/Region.php new file mode 100644 index 0000000..dca2c32 --- /dev/null +++ b/source/application/store/model/Region.php @@ -0,0 +1,15 @@ +order(['sort' => 'asc']) + ->where('is_delete', '=', 0) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 获取全部收货地址 + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getAll() + { + return $this->order(['sort' => 'asc']) + ->where('is_delete', '=', 0) + ->select(); + } + + /** + * 添加新记录 + * @param $data + * @return false|int + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + return $this->allowField(true)->save($data); + } + + /** + * 删除记录 + * @return bool|int + */ + public function remove() + { + return $this->save(['is_delete' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/Setting.php b/source/application/store/model/Setting.php new file mode 100644 index 0000000..ca9d366 --- /dev/null +++ b/source/application/store/model/Setting.php @@ -0,0 +1,95 @@ +validValues($key, $values)) { + return false; + } + // 删除系统设置缓存 + Cache::rm('setting_' . self::$wxapp_id); + return $model->save([ + 'key' => $key, + 'describe' => SettingEnum::data()[$key]['describe'], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]) !== false; + } + + /** + * 数据验证 + * @param $key + * @param $values + * @return bool + */ + private function validValues($key, $values) + { + $callback = [ + 'store' => function ($values) { + return $this->validStore($values); + }, + 'printer' => function ($values) { + return $this->validPrinter($values); + }, + ]; + // 验证商城设置 + return isset($callback[$key]) ? $callback[$key]($values) : true; + } + + /** + * 验证商城设置 + * @param $values + * @return bool + */ + private function validStore($values) + { + if (!isset($values['delivery_type']) || empty($values['delivery_type'])) { + $this->error = '配送方式至少选择一个'; + return false; + } + return true; + } + + /** + * 验证小票打印机设置 + * @param $values + * @return bool + */ + private function validPrinter($values) + { + if ($values['is_open'] == false) { + return true; + } + if (!$values['printer_id']) { + $this->error = '请选择订单打印机'; + return false; + } + if (empty($values['order_status'])) { + $this->error = '请选择订单打印方式'; + return false; + } + return true; + } + +} diff --git a/source/application/store/model/Spec.php b/source/application/store/model/Spec.php new file mode 100644 index 0000000..25a52e5 --- /dev/null +++ b/source/application/store/model/Spec.php @@ -0,0 +1,35 @@ +value('spec_id'); + } + + /** + * 新增规格组 + * @param $spec_name + * @return false|int + */ + public function add($spec_name) + { + $wxapp_id = self::$wxapp_id; + return $this->save(compact('spec_name', 'wxapp_id')); + } + +} diff --git a/source/application/store/model/SpecValue.php b/source/application/store/model/SpecValue.php new file mode 100644 index 0000000..093383a --- /dev/null +++ b/source/application/store/model/SpecValue.php @@ -0,0 +1,38 @@ +value('spec_value_id'); + } + + /** + * 新增规格值 + * @param $spec_id + * @param $spec_value + * @return false|int + */ + public function add($spec_id, $spec_value) + { + $wxapp_id = self::$wxapp_id; + return $this->save(compact('spec_value', 'spec_id', 'wxapp_id')); + } + +} diff --git a/source/application/store/model/Store.php b/source/application/store/model/Store.php new file mode 100644 index 0000000..0c221cb --- /dev/null +++ b/source/application/store/model/Store.php @@ -0,0 +1,196 @@ +GoodsModel = new Goods; + $this->OrderModel = new Order; + $this->UserModel = new User; + } + + /** + * 后台首页数据 + * @return array + * @throws \think\Exception + */ + public function getHomeData() + { + $today = date('Y-m-d'); + $yesterday = date('Y-m-d', strtotime('-1 day')); + // 最近七天日期 + $lately7days = $this->getLately7days(); + $data = [ + 'widget-card' => [ + // 商品总量 + 'goods_total' => $this->getGoodsTotal(), + // 用户总量 + 'user_total' => $this->getUserTotal(), + // 订单总量 + 'order_total' => $this->getOrderTotal(), + // 评价总量 + 'comment_total' => $this->getCommentTotal() + ], + 'widget-outline' => [ + // 销售额(元) + 'order_total_price' => [ + 'tday' => $this->getOrderTotalPrice($today), + 'ytd' => $this->getOrderTotalPrice($yesterday) + ], + // 支付订单数 + 'order_total' => [ + 'tday' => $this->getOrderTotal($today), + 'ytd' => $this->getOrderTotal($yesterday) + ], + // 新增用户数 + 'new_user_total' => [ + 'tday' => $this->getUserTotal($today), + 'ytd' => $this->getUserTotal($yesterday) + ], + // 下单用户数 + 'order_user_total' => [ + 'tday' => $this->getPayOrderUserTotal($today), + 'ytd' => $this->getPayOrderUserTotal($yesterday) + ] + ], + 'widget-echarts' => [ + // 最近七天日期 + 'date' => helper::jsonEncode($lately7days), + 'order_total' => helper::jsonEncode($this->getOrderTotalByDate($lately7days)), + 'order_total_price' => helper::jsonEncode($this->getOrderTotalPriceByDate($lately7days)) + ] + ]; + + + return $data; + } + + /** + * 最近七天日期 + */ + private function getLately7days() + { + // 获取当前周几 + $date = []; + for ($i = 0; $i < 7; $i++) { + $date[] = date('Y-m-d', strtotime('-' . $i . ' days')); + } + return array_reverse($date); + } + + /** + * 获取商品总量 + * @return string + * @throws \think\Exception + */ + private function getGoodsTotal() + { + return number_format($this->GoodsModel->getGoodsTotal()); + } + + /** + * 获取用户总量 + * @param null $day + * @return string + * @throws \think\Exception + */ + private function getUserTotal($day = null) + { + return number_format($this->UserModel->getUserTotal($day)); + } + + /** + * 获取订单总量 + * @param null $day + * @return string + * @throws \think\Exception + */ + private function getOrderTotal($day = null) + { + return number_format($this->OrderModel->getPayOrderTotal($day, $day)); + } + + /** + * 获取订单总量 (指定日期) + * @param $days + * @return array + * @throws \think\Exception + */ + private function getOrderTotalByDate($days) + { + $data = []; + foreach ($days as $day) { + $data[] = $this->getOrderTotal($day); + } + return $data; + } + + /** + * 获取评价总量 + * @return string + */ + private function getCommentTotal() + { + $model = new Comment; + return number_format($model->getCommentTotal()); + } + + /** + * 获取某天的总销售额 + * @param null $day + * @return string + */ + private function getOrderTotalPrice($day = null) + { + return helper::number2($this->OrderModel->getOrderTotalPrice($day, $day)); + } + + /** + * 获取订单总量 (指定日期) + * @param $days + * @return array + */ + private function getOrderTotalPriceByDate($days) + { + $data = []; + foreach ($days as $day) { + $data[] = $this->getOrderTotalPrice($day); + } + return $data; + } + + /** + * 获取某天的下单用户数 + * @param $day + * @return float|int + */ + private function getPayOrderUserTotal($day) + { + return number_format($this->OrderModel->getPayOrderUserTotal($day)); + } + +} \ No newline at end of file diff --git a/source/application/store/model/UploadFile.php b/source/application/store/model/UploadFile.php new file mode 100644 index 0000000..b3030c9 --- /dev/null +++ b/source/application/store/model/UploadFile.php @@ -0,0 +1,92 @@ +where('group_id', '=', (int)$groupId); + // 文件类型 + !empty($fileType) && $this->where('file_type', '=', trim($fileType)); + // 是否在回收站 + $isRecycle > -1 && $this->where('is_recycle', '=', (int)$isRecycle); + // 查询列表数据 + return $this->with(['upload_group']) + ->where(['is_user' => 0, 'is_delete' => 0]) + ->order(['file_id' => 'desc']) + ->paginate(32, false, [ + 'query' => Request::instance()->request() + ]); + } + + /** + * 移入|移出回收站 + * @param bool $isRecycle + * @return false|int + */ + public function setRecycle($isRecycle = true) + { + return $this->save(['is_recycle' => (int)$isRecycle]); + } + + /** + * 删除文件 + * @return false|int + * @throws \think\Exception + */ + public function setDelete() + { + // 存储配置信息 + $config = SettingModel::getItem('storage'); + // 实例化存储驱动 + $StorageDriver = new StorageDriver($config, $this['storage']); + // 删除文件 + if (!$StorageDriver->delete($this['file_name'])) { + $this->error = '文件删除失败:' . $StorageDriver->getError(); + return false; + } + return $this->save(['is_delete' => 1]); + } + + /** + * 批量软删除 + * @param $fileIds + * @return $this + */ + public function softDelete($fileIds) + { + return $this->where('file_id', 'in', $fileIds)->update(['is_recycle' => 1]); + } + + /** + * 批量移动文件分组 + * @param $group_id + * @param $fileIds + * @return $this + */ + public function moveGroup($group_id, $fileIds) + { + return $this->where('file_id', 'in', $fileIds)->update(compact('group_id')); + } + +} diff --git a/source/application/store/model/UploadFileUsed.php b/source/application/store/model/UploadFileUsed.php new file mode 100644 index 0000000..e6018d9 --- /dev/null +++ b/source/application/store/model/UploadFileUsed.php @@ -0,0 +1,38 @@ +save($data); + } + + /** + * 移除记录 + * @param $from_type + * @param $file_id + * @param null $from_id + * @return int + */ + public function remove($from_type, $file_id, $from_id = null) + { + $where = compact('from_type', 'file_id'); + !is_null($from_id) && $where['from_id'] = $from_id; + return $this->where($where)->delete(); + } +} diff --git a/source/application/store/model/UploadGroup.php b/source/application/store/model/UploadGroup.php new file mode 100644 index 0000000..a6e57b2 --- /dev/null +++ b/source/application/store/model/UploadGroup.php @@ -0,0 +1,66 @@ +where('group_type', '=', trim($groupType)); + return $this->where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->select(); + } + + /** + * 添加新记录 + * @param $data + * @return false|int + */ + public function add($data) + { + return $this->save(array_merge([ + 'wxapp_id' => self::$wxapp_id, + 'sort' => 100 + ], $data)); + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除记录 + * @return int + */ + public function remove() + { + // 更新该分组下的所有文件 + (new UploadFile)->where('group_id', '=', $this['group_id']) + ->update(['group_id' => 0]); + // 删除分组 + return $this->save(['is_delete' => 1]); + } + +} diff --git a/source/application/store/model/User.php b/source/application/store/model/User.php new file mode 100644 index 0000000..d6cab13 --- /dev/null +++ b/source/application/store/model/User.php @@ -0,0 +1,208 @@ +where('create_time', '>=', $startTime) + ->where('create_time', '<', $startTime + 86400); + } + return $this->where('is_delete', '=', '0')->count(); + } + + /** + * 获取用户列表 + * @param string $nickName 昵称 + * @param int $gender 性别 + * @param int $grade 会员等级 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($nickName = '', $gender = -1, $grade = null) + { + // 检索:微信昵称 + !empty($nickName) && $this->where('nickName', 'like', "%$nickName%"); + // 检索:性别 + if ($gender !== '' && $gender > -1) { + $this->where('gender', '=', (int)$gender); + } + // 检索:会员等级 + $grade > 0 && $this->where('grade_id', '=', (int)$grade); + // 获取用户列表 + return $this->with(['grade']) + ->where('is_delete', '=', '0') + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 删除用户 + * @return bool|mixed + */ + public function setDelete() + { + // 判断是否为分销商 + if (DealerUserModel::isDealerUser($this['user_id'])) { + $this->error = '当前用户为分销商,不可删除'; + return false; + } + return $this->transaction(function () { + // 删除用户推荐关系 + (new DealerUserModel)->onDeleteReferee($this['user_id']); + // 标记为已删除 + return $this->save(['is_delete' => 1]); + }); + } + + /** + * 用户充值 + * @param string $storeUserName 当前操作人用户名 + * @param int $source 充值类型 + * @param array $data post数据 + * @return bool + */ + public function recharge($storeUserName, $source, $data) + { + if ($source == 0) { + return $this->rechargeToBalance($storeUserName, $data['balance']); + } elseif ($source == 1) { + return $this->rechargeToPoints($storeUserName, $data['points']); + } + return false; + } + + /** + * 用户充值:余额 + * @param $storeUserName + * @param $data + * @return bool + */ + private function rechargeToBalance($storeUserName, $data) + { + if (!isset($data['money']) || $data['money'] === '' || $data['money'] < 0) { + $this->error = '请输入正确的金额'; + return false; + } + // 判断充值方式,计算最终金额 + if ($data['mode'] === 'inc') { + $diffMoney = $data['money']; + } elseif ($data['mode'] === 'dec') { + $diffMoney = -$data['money']; + } else { + $diffMoney = helper::bcsub($data['money'], $this['balance']); + } + // 更新记录 + $this->transaction(function () use ($storeUserName, $data, $diffMoney) { + // 更新账户余额 + $this->setInc('balance', $diffMoney); + // 新增余额变动记录 + BalanceLogModel::add(SceneEnum::ADMIN, [ + 'user_id' => $this['user_id'], + 'money' => $diffMoney, + 'remark' => $data['remark'], + ], [$storeUserName]); + }); + return true; + } + + /** + * 用户充值:积分 + * @param $storeUserName + * @param $data + * @return bool + */ + private function rechargeToPoints($storeUserName, $data) + { + if (!isset($data['value']) || $data['value'] === '' || $data['value'] < 0) { + $this->error = '请输入正确的积分数量'; + return false; + } + // 判断充值方式,计算最终积分 + if ($data['mode'] === 'inc') { + $diffMoney = $data['value']; + } elseif ($data['mode'] === 'dec') { + $diffMoney = -$data['value']; + } else { + $diffMoney = $data['value'] - $this['points']; + } + // 更新记录 + $this->transaction(function () use ($storeUserName, $data, $diffMoney) { + // 更新账户积分 + $this->setInc('points', $diffMoney); + // 新增积分变动记录 + PointsLogModel::add([ + 'user_id' => $this['user_id'], + 'value' => $diffMoney, + 'describe' => "后台管理员 [{$storeUserName}] 操作", + 'remark' => $data['remark'], + ]); + }); + return true; + } + + /** + * 修改用户等级 + * @param $data + * @return mixed + */ + public function updateGrade($data) + { + // 变更前的等级id + $oldGradeId = $this['grade_id']; + return $this->transaction(function () use ($oldGradeId, $data) { + // 更新用户的等级 + $status = $this->save(['grade_id' => $data['grade_id']]); + // 新增用户等级修改记录 + if ($status) { + (new GradeLogModel)->record([ + 'user_id' => $this['user_id'], + 'old_grade_id' => $oldGradeId, + 'new_grade_id' => $data['grade_id'], + 'change_type' => ChangeTypeEnum::ADMIN_USER, + 'remark' => $data['remark'] + ]); + } + return $status !== false; + }); + } + + /** + * 消减用户的实际消费金额 + * @param $userId + * @param $expendMoney + * @return int|true + * @throws \think\Exception + */ + public function setDecUserExpend($userId, $expendMoney) + { + return $this->where(['user_id' => $userId])->setDec('expend_money', $expendMoney); + } + +} diff --git a/source/application/store/model/UserAddress.php b/source/application/store/model/UserAddress.php new file mode 100644 index 0000000..7db84c0 --- /dev/null +++ b/source/application/store/model/UserAddress.php @@ -0,0 +1,15 @@ +with(['user']) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/Wxapp.php b/source/application/store/model/Wxapp.php new file mode 100644 index 0000000..9c99b24 --- /dev/null +++ b/source/application/store/model/Wxapp.php @@ -0,0 +1,111 @@ +startTrans(); + try { + // 删除wxapp缓存 + self::deleteCache(); + // 写入微信支付证书文件 + $this->writeCertPemFiles($data['cert_pem'], $data['key_pem']); + // 更新小程序设置 + $this->allowField(true)->save($data); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 写入cert证书文件 + * @param string $cert_pem + * @param string $key_pem + * @return bool + */ + private function writeCertPemFiles($cert_pem = '', $key_pem = '') + { + if (empty($cert_pem) || empty($key_pem)) { + return false; + } + // 证书目录 + $filePath = APP_PATH . 'common/library/wechat/cert/' . self::$wxapp_id . '/'; + // 目录不存在则自动创建 + if (!is_dir($filePath)) { + mkdir($filePath, 0755, true); + } + // 写入cert.pem文件 + if (!empty($cert_pem)) { + file_put_contents($filePath . 'cert.pem', $cert_pem); + } + // 写入key.pem文件 + if (!empty($key_pem)) { + file_put_contents($filePath . 'key.pem', $key_pem); + } + return true; + } + + /** + * 记录图片信息 + * @param $wxapp_id + * @param $oldFileId + * @param $newFileName + * @param $fromType + * @return int|mixed + */ + private function uploadImage($wxapp_id, $oldFileId, $newFileName, $fromType) + { +// $UploadFile = new UploadFile; + $UploadFileUsed = new UploadFileUsed; + if ($oldFileId > 0) { + // 获取原图片path + $oldFileName = UploadFile::getFileName($oldFileId); + // 新文件与原来路径一致, 代表用户未修改, 不做更新 + if ($newFileName === $oldFileName) + return $oldFileId; + // 删除原文件使用记录 + $UploadFileUsed->remove('service', $oldFileId); + } + // 删除图片 + if (empty($newFileName)) return 0; + // 查询新文件file_id + $fileId = UploadFile::getFildIdByName($newFileName); + // 添加文件使用记录 + $UploadFileUsed->add([ + 'file_id' => $fileId, + 'wxapp_id' => $wxapp_id, + 'from_type' => $fromType + ]); + return $fileId; + } + + /** + * 删除wxapp缓存 + * @return bool + */ + public static function deleteCache() + { + return Cache::rm('wxapp_' . self::$wxapp_id); + } + +} diff --git a/source/application/store/model/WxappCategory.php b/source/application/store/model/WxappCategory.php new file mode 100644 index 0000000..29984c6 --- /dev/null +++ b/source/application/store/model/WxappCategory.php @@ -0,0 +1,24 @@ +allowField(true)->save($data) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/WxappHelp.php b/source/application/store/model/WxappHelp.php new file mode 100644 index 0000000..7ee0b5b --- /dev/null +++ b/source/application/store/model/WxappHelp.php @@ -0,0 +1,43 @@ +allowField(true)->save($data); + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除记录 + * @return int + */ + public function remove() { + return $this->delete(); + } + +} diff --git a/source/application/store/model/WxappNavbar.php b/source/application/store/model/WxappNavbar.php new file mode 100644 index 0000000..347db7f --- /dev/null +++ b/source/application/store/model/WxappNavbar.php @@ -0,0 +1,26 @@ +save($data) !== false; + } + +} diff --git a/source/application/store/model/WxappPage.php b/source/application/store/model/WxappPage.php new file mode 100644 index 0000000..0369561 --- /dev/null +++ b/source/application/store/model/WxappPage.php @@ -0,0 +1,87 @@ +where(['is_delete' => 0])->order(['create_time' => 'desc'])->select(); + } + + /** + * 新增页面 + * @param $data + * @return bool + */ + public function add($data) + { + // 删除wxapp缓存 + Wxapp::deleteCache(); + return $this->save([ + 'page_type' => 20, + 'page_name' => $data['page']['params']['name'], + 'page_data' => $data, + 'wxapp_id' => self::$wxapp_id + ]); + } + + /** + * 更新页面 + * @param $data + * @return bool + */ + public function edit($data) + { + // 删除wxapp缓存 + Wxapp::deleteCache(); + // 保存数据 + return $this->save([ + 'page_name' => $data['page']['params']['name'], + 'page_data' => $data + ]) !== false; + } + + /** + * 删除记录 + * @return int + */ + public function setDelete() + { + if ($this['page_type'] == 10) { + $this->error = '默认首页不可以删除'; + return false; + } + // 删除wxapp缓存 + Wxapp::deleteCache(); + return $this->save(['is_delete' => 1]); + } + + /** + * 设为默认首页 + * @return int + */ + public function setHome() + { + // 取消原默认首页 + $this->where(['page_type' => 10])->update(['page_type' => 20]); + // 删除wxapp缓存 + Wxapp::deleteCache(); + return $this->save(['page_type' => 10]); + } + +} diff --git a/source/application/store/model/article/Category.php b/source/application/store/model/article/Category.php new file mode 100644 index 0000000..e96970f --- /dev/null +++ b/source/application/store/model/article/Category.php @@ -0,0 +1,76 @@ +deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + $this->deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 删除商品分类 + * @param $category_id + * @return bool|int + */ + public function remove($category_id) + { + // 判断是否存在文章 + $articleCount = ArticleModel::getArticleTotal(['category_id' => $category_id]); + if ($articleCount > 0) { + $this->error = '该分类下存在' . $articleCount . '个文章,不允许删除'; + return false; + } + $this->deleteCache(); + return $this->delete(); + } + + /** + * 删除缓存 + * @return bool + */ + private function deleteCache() + { + return Cache::rm('article_category_' . self::$wxapp_id); + } + +} diff --git a/source/application/store/model/bargain/Active.php b/source/application/store/model/bargain/Active.php new file mode 100644 index 0000000..dfa949c --- /dev/null +++ b/source/application/store/model/bargain/Active.php @@ -0,0 +1,130 @@ +setBaseQuery($this->alias, [ + ['goods', 'goods_id'], + ]); + // 检索查询条件 + if (!empty($search)) { + $this->where('goods.goods_name', 'like', "%{$search}%"); + } + // 获取活动列表 + $list = $this->where("{$this->alias}.is_delete", '=', 0) + ->order(["{$this->alias}.sort" => 'asc', "{$this->alias}.create_time" => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + if (!$list->isEmpty()) { + // 设置商品数据 + $list = GoodsService::setGoodsData($list); + } + return $list; + } + + /** + * 新增记录 + * @param $data + * @return bool|int + */ + public function add($data) + { + if (!$this->onValidate($data, 'add')) { + return false; + } + $data = $this->createData($data); + return $this->allowField(true)->save($data) !== false; + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + if (!$this->onValidate($data, 'edit')) { + return false; + } + $data = $this->createData($data); + return $this->allowField(true)->save($data) !== false; + } + + private function createData($data) + { + $data['wxapp_id'] = static::$wxapp_id; + $data['start_time'] = strtotime($data['start_time']); + $data['end_time'] = strtotime($data['end_time']); + return $data; + } + + /** + * 表单验证 + * @param $data + * @param string $scene + * @return bool + */ + private function onValidate($data, $scene = 'add') + { + if ($scene === 'add') { + if (!isset($data['goods_id']) || empty($data['goods_id'])) { + $this->error = '请选择商品'; + return false; + } + } + // 验证活动时间 + if (empty($data['start_time']) || empty($data['end_time'])) { + $this->error = '请选择活动的开始时间与截止时间'; + return false; + } + $data['start_time'] = strtotime($data['start_time']); + $data['end_time'] = strtotime($data['end_time']); + if ($data['end_time'] <= $data['start_time']) { + $this->error = '活动结束时间必须大于开始时间'; + return false; + } + return true; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->allowField(true)->save(['is_delete' => 1]); + } + + /** + * 商品ID是否存在 + * @param $goodsId + * @return bool + */ + public static function isExistGoodsId($goodsId) + { + return !!(new static)->where('goods_id', '=', $goodsId) + ->where('is_delete', '=', 0) + ->value('active_id'); + } + +} \ No newline at end of file diff --git a/source/application/store/model/bargain/Setting.php b/source/application/store/model/bargain/Setting.php new file mode 100644 index 0000000..195295c --- /dev/null +++ b/source/application/store/model/bargain/Setting.php @@ -0,0 +1,43 @@ + '基础设置', + ]; + + /** + * 更新系统设置 + * @param $key + * @param $values + * @return bool + * @throws \think\exception\DbException + */ + public function edit($key, $values) + { + $model = self::detail($key) ?: $this; + // 删除系统设置缓存 + Cache::rm('bargain_setting_' . self::$wxapp_id); + return $model->save([ + 'key' => $key, + 'describe' => $this->describe[$key], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/bargain/Task.php b/source/application/store/model/bargain/Task.php new file mode 100644 index 0000000..b0acc6d --- /dev/null +++ b/source/application/store/model/bargain/Task.php @@ -0,0 +1,59 @@ +setBaseQuery($this->alias, [ + ['goods', 'goods_id'], + ['user', 'user_id'], + ]); + // 检索查询条件 + if (!empty($search)) { + $this->where(function ($query) use ($search) { + $query->whereOr('goods.goods_name', 'like', "%{$search}%"); + $query->whereOr('user.nickName', 'like', "%{$search}%"); + }); + } + // 获取活动列表 + $list = $this->with(['user']) + ->where("{$this->alias}.is_delete", '=', 0) + ->order(["{$this->alias}.create_time" => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + if (!$list->isEmpty()) { + // 设置商品数据 + $list = GoodsService::setGoodsData($list); + } + return $list; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->allowField(true)->save(['is_delete' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/bargain/TaskHelp.php b/source/application/store/model/bargain/TaskHelp.php new file mode 100644 index 0000000..43a1383 --- /dev/null +++ b/source/application/store/model/bargain/TaskHelp.php @@ -0,0 +1,31 @@ +with(['user']) + ->where('task_id', '=', $task_id) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + return $list; + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/Apply.php b/source/application/store/model/dealer/Apply.php new file mode 100644 index 0000000..af7bb44 --- /dev/null +++ b/source/application/store/model/dealer/Apply.php @@ -0,0 +1,69 @@ +alias('apply') + ->field('apply.*, user.nickName, user.avatarUrl') + ->with(['referee']) + ->join('user', 'user.user_id = apply.user_id') + ->order(['apply.create_time' => 'desc']); + // 查询条件 + !empty($search) && $this->where('user.nickName|apply.real_name|apply.mobile', 'like', "%$search%"); + // 获取列表数据 + return $this->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 分销商入驻审核 + * @param $data + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function submit($data) + { + if ($data['apply_status'] == '30' && empty($data['reject_reason'])) { + $this->error = '请填写驳回原因'; + return false; + } + $this->startTrans(); + if ($data['apply_status'] == '20') { + // 新增分销商用户 + User::add($this['user_id'], [ + 'real_name' => $this['real_name'], + 'mobile' => $this['mobile'], + 'referee_id' => $this['referee_id'], + ]); + } + // 更新申请记录 + $data['audit_time'] = time(); + $this->allowField(true)->save($data); + // 发送模板消息 + (new Message)->dealer($this); + $this->commit(); + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/Capital.php b/source/application/store/model/dealer/Capital.php new file mode 100644 index 0000000..7822c5a --- /dev/null +++ b/source/application/store/model/dealer/Capital.php @@ -0,0 +1,15 @@ + 1 && $this->where('first_user_id|second_user_id|third_user_id', '=', $user_id); + $is_settled > -1 && $this->where('is_settled', '=', !!$is_settled); + !empty($search) && $this->where('user.nickName', 'like', "%{$search}%"); + // 获取分销商订单列表 + $data = $this->with([ + 'dealer_first.user', + 'dealer_second.user', + 'dealer_third.user' + ]) + ->order(['create_time' => 'desc']) + ->paginate(10, false, [ + 'query' => \request()->request() + ]); + if ($data->isEmpty()) { + return $data; + } + // 获取订单的主信息 + $with = ['goods' => ['image', 'refund'], 'address', 'user']; + return OrderService::getOrderList($data, 'order_master', $with); + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/Referee.php b/source/application/store/model/dealer/Referee.php new file mode 100644 index 0000000..13a94a8 --- /dev/null +++ b/source/application/store/model/dealer/Referee.php @@ -0,0 +1,76 @@ + -1 && $this->where('m.level', '=', $level); + return $this->alias('m') + ->join('user', 'user.user_id = m.user_id') + ->where('m.dealer_id', '=', $dealerId) + ->where('user.is_delete', '=', 0) + ->column('m.user_id'); + } + + /** + * 获取指定用户的推荐人列表 + * @param $userId + * @return false|\PDOStatement|string|\think\Collection + */ + public static function getRefereeList($userId) + { + return (new static)->with(['dealer1'])->where('user_id', '=', $userId)->select(); + } + + /** + * 清空下级成员推荐关系 + * @param $dealerId + * @param int $level + * @return int + */ + public function onClearTeam($dealerId, $level = -1) + { + $level > -1 && $this->where('level', '=', $level); + return $this->where('dealer_id', '=', $dealerId)->delete(); + } + + /** + * 清空上级推荐关系 + * @param $userId + * @param int $level + * @return int + */ + public function onClearReferee($userId, $level = -1) + { + $level > -1 && $this->where('level', '=', $level); + return $this->where('user_id', '=', $userId)->delete(); + } + + /** + * 清空2-3级推荐人的关系记录 + * @param $teamIds + * @return int + */ + public function onClearTop($teamIds) + { + return $this->where('user_id', 'in', $teamIds) + ->where('level', 'in', [2, 3]) + ->delete(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/Setting.php b/source/application/store/model/dealer/Setting.php new file mode 100644 index 0000000..237ffbf --- /dev/null +++ b/source/application/store/model/dealer/Setting.php @@ -0,0 +1,111 @@ + '基础设置', + 'condition' => '分销商条件', + 'commission' => '佣金设置', + 'settlement' => '结算', + 'words' => '自定义文字', + 'license' => '申请协议', + 'background' => '页面背景图', + 'template_msg' => '模板消息', + 'qrcode' => '分销海报', + ]; + + /** + * 更新系统设置 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function edit($data) + { + $this->startTrans(); + try { + foreach ($data as $key => $values) + $this->saveValues($key, $values); + $this->commit(); + // 删除系统设置缓存 + Cache::rm('dealer_setting_' . self::$wxapp_id); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 保存设置项 + * @param $key + * @param $values + * @return false|int + * @throws BaseException + * @throws \think\exception\DbException + */ + private function saveValues($key, $values) + { + $model = self::detail($key) ?: new self; + // 数据验证 + if (!$this->validValues($key, $values)) { + throw new BaseException(['msg' => $this->error]); + } + return $model->save([ + 'key' => $key, + 'describe' => $this->describe[$key], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]); + } + + /** + * 数据验证 + * @param $key + * @param $values + * @return bool + */ + private function validValues($key, $values) + { + if ($key === 'settlement') { + // 验证结算方式 + return $this->validSettlement($values); + } +// if ($key === 'condition') { +// // 验证分销商条件 +// return $this->validCondition($values); +// } + return true; + } + + /** + * 验证结算方式 + * @param $values + * @return bool + */ + private function validSettlement($values) + { + if (!isset($values['pay_type']) || empty($values['pay_type'])) { + $this->error = '请设置 结算-提现方式'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/User.php b/source/application/store/model/dealer/User.php new file mode 100644 index 0000000..856a08a --- /dev/null +++ b/source/application/store/model/dealer/User.php @@ -0,0 +1,165 @@ +alias('dealer') + ->field('dealer.*, user.nickName, user.avatarUrl') + ->with(['referee']) + ->join('user', 'user.user_id = dealer.user_id') + ->where('dealer.is_delete', '=', 0) + ->order(['dealer.create_time' => 'desc']); + // 查询条件 + !empty($search) && $this->where('user.nickName|dealer.real_name|dealer.mobile', 'like', "%$search%"); + // 获取列表数据 + return $this->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 编辑分销商用户 + * @param $data + * @return bool + */ + public function edit($data) + { + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除分销商用户 + * @return mixed + */ + public function setDelete() + { + return $this->transaction(function () { + // 获取一级团队成员ID集 + $RefereeModel = new RefereeModel; + $team1Ids = $RefereeModel->getTeamUserIds($this['user_id'], 1); + if (!empty($team1Ids)) { + // 一级团队成员归属到平台 + $this->setFromplatform($team1Ids); + // 一级推荐人ID + $referee1Id = RefereeModel::getRefereeUserId($this['user_id'], 1, true); + if ($referee1Id > 0) { + // 一级推荐人的成员数量(二级) + $this->setDecTeamNum($referee1Id, 2, count($team1Ids)); + // 一级推荐人的成员数量(三级) + $team2Ids = $RefereeModel->getTeamUserIds($this['user_id'], 2); + !empty($team2Ids) && $this->setDecTeamNum($referee1Id, 3, count($team2Ids)); + // 二级推荐人的成员数量(三级) + $referee2Id = RefereeModel::getRefereeUserId($this['user_id'], 2, true); + $referee2Id > 0 && $this->setDecTeamNum($referee2Id, 3, count($team1Ids)); + // 清空分销商下级成员与上级推荐人的关系记录 + $RefereeModel->onClearTop(array_merge($team1Ids, $team2Ids)); + } + } + // 清空下级推荐记录 + $RefereeModel->onClearTeam($this['user_id']); + // 标记当前分销商记录为已删除 + return $this->delete(); + }); + } + + /** + * 一级团队成员归属到平台 + * @param $userIds + * @return false|int + */ + private function setFromplatform($userIds) + { + return $this->isUpdate(true)->save( + ['referee_id' => 0], + ['user_id' => ['in', $userIds], 'is_delete' => 0] + ); + } + + /** + * 删除用户的上级推荐关系 + * @param $userId + * @return bool + * @throws \think\Exception + */ + public function onDeleteReferee($userId) + { + // 获取推荐人列表 + $list = RefereeModel::getRefereeList($userId); + if (!$list->isEmpty()) { + // 递减推荐人的下级成员数量 + foreach ($list as $item) { + $item['dealer1'] && $this->setDecTeamNum($item['dealer_id'], $item['level'], 1); + } + // 清空上级推荐关系 + (new RefereeModel)->onClearReferee($userId); + } + return true; + } + + /** + * 递减分销商成员数量 + * @param $dealerId + * @param $level + * @param $number + * @return int|true + * @throws \think\Exception + */ + private function setDecTeamNum($dealerId, $level, $number) + { + $field = [1 => 'first_num', 2 => 'second_num', 3 => 'third_num']; + return $this->where('user_id', '=', $dealerId) + ->where('is_delete', '=', 0) + ->setDec($field[$level], $number); + } + + /** + * 提现打款成功:累积提现佣金 + * @param $user_id + * @param $money + * @return false|int + * @throws \think\exception\DbException + */ + public static function totalMoney($user_id, $money) + { + $model = self::detail($user_id); + return $model->save([ + 'freeze_money' => $model['freeze_money'] - $money, + 'total_money' => $model['total_money'] + $money, + ]); + } + + /** + * 提现驳回:解冻分销商资金 + * @param $user_id + * @param $money + * @return false|int + * @throws \think\exception\DbException + */ + public static function backFreezeMoney($user_id, $money) + { + $model = self::detail($user_id); + return $model->save([ + 'money' => $model['money'] + $money, + 'freeze_money' => $model['freeze_money'] - $money, + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/dealer/Withdraw.php b/source/application/store/model/dealer/Withdraw.php new file mode 100644 index 0000000..a9c591a --- /dev/null +++ b/source/application/store/model/dealer/Withdraw.php @@ -0,0 +1,152 @@ + 0 ? date('Y-m-d H:i:s', $value) : 0; + } + + /** + * 获取器:打款方式 + * @param $value + * @return mixed + */ + public function getPayTypeAttr($value) + { + return ['text' => $this->payType[$value], 'value' => $value]; + } + + /** + * 获取分销商提现列表 + * @param null $user_id + * @param int $apply_status + * @param int $pay_type + * @param string $search + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList($user_id = null, $apply_status = -1, $pay_type = -1, $search = '') + { + // 构建查询规则 + $this->alias('withdraw') + ->with(['user']) + ->field('withdraw.*, dealer.real_name, dealer.mobile, user.nickName, user.avatarUrl') + ->join('user', 'user.user_id = withdraw.user_id') + ->join('dealer_user dealer', 'dealer.user_id = withdraw.user_id') + ->order(['withdraw.create_time' => 'desc']); + // 查询条件 + $user_id > 0 && $this->where('withdraw.user_id', '=', $user_id); + !empty($search) && $this->where('dealer.real_name|dealer.mobile', 'like', "%$search%"); + $apply_status > 0 && $this->where('withdraw.apply_status', '=', $apply_status); + $pay_type > 0 && $this->where('withdraw.pay_type', '=', $pay_type); + // 获取列表数据 + return $this->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 分销商提现审核 + * @param $data + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function submit($data) + { + if ($data['apply_status'] == '30' && empty($data['reject_reason'])) { + $this->error = '请填写驳回原因'; + return false; + } + // 更新申请记录 + $data['audit_time'] = time(); + $this->allowField(true)->save($data); + // 提现驳回:解冻分销商资金 + $data['apply_status'] == '30' && User::backFreezeMoney($this['user_id'], $this['money']); + // 发送模板消息 + (new Message)->withdraw($this); + return true; + } + + /** + * 确认已打款 + * @return bool + * @throws \think\exception\PDOException + */ + public function money() + { + $this->startTrans(); + try { + // 更新申请状态 + $this->allowField(true)->save([ + 'apply_status' => 40, + 'audit_time' => time(), + ]); + // 更新分销商累积提现佣金 + User::totalMoney($this['user_id'], $this['money']); + // 记录分销商资金明细 + Capital::add([ + 'user_id' => $this['user_id'], + 'flow_type' => 20, + 'money' => -$this['money'], + 'describe' => '申请提现', + ]); + // 发送模板消息 + (new Message)->withdraw($this); + // 事务提交 + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 分销商提现:微信支付企业付款 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function wechatPay() + { + // 微信用户信息 + $user = $this['user']['user']; + // 生成付款订单号 + $orderNO = OrderService::createOrderNo(); + // 付款描述 + $desc = '分销商提现付款'; + // 微信支付api:企业付款到零钱 + $wxConfig = WxappModel::getWxappCache(); + $WxPay = new WxPay($wxConfig); + // 请求付款api + if ($WxPay->transfers($orderNO, $user['open_id'], $this['money'], $desc)) { + // 确认已打款 + $this->money(); + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/recharge/Order.php b/source/application/store/model/recharge/Order.php new file mode 100644 index 0000000..7a9d169 --- /dev/null +++ b/source/application/store/model/recharge/Order.php @@ -0,0 +1,61 @@ +setQueryWhere($query); + // 获取列表数据 + return $this->with(['user', 'order_plan']) + ->alias('order') + ->field('order.*') + ->join('user', 'user.user_id = order.user_id') + ->order(['order.create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 设置查询条件 + * @param $query + */ + private function setQueryWhere($query) + { + // 设置默认的检索数据 + $params = $this->setQueryDefaultValue($query, [ + 'user_id' => 0, + 'recharge_type' => '-1', + 'pay_status' => '-1', + ]); + // 用户ID + $params['user_id'] > 0 && $this->where('order.user_id', '=', $params['user_id']); + // 用户昵称/订单号 + !empty($params['search']) && $this->where('order.order_no|user.nickName', 'like', "%{$params['search']}%"); + // 充值方式 + $params['recharge_type'] > -1 && $this->where('order.recharge_type', '=', (int)$params['recharge_type']); + // 支付状态 + $params['pay_status'] > -1 && $this->where('order.pay_status', '=', (int)$params['pay_status']); + // 起始时间 + !empty($params['start_time']) && $this->where('order.create_time', '>=', strtotime($params['start_time'])); + // 截止时间 + !empty($params['end_time']) && $this->where('order.create_time', '<', strtotime($params['end_time']) + 86400); + } + +} \ No newline at end of file diff --git a/source/application/store/model/recharge/OrderPlan.php b/source/application/store/model/recharge/OrderPlan.php new file mode 100644 index 0000000..047fe56 --- /dev/null +++ b/source/application/store/model/recharge/OrderPlan.php @@ -0,0 +1,15 @@ +where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 添加新记录 + * @param $data + * @return false|int + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 更新记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + return $this->allowField(true)->save($data) !== false; + } + + /** + * 删除记录 (软删除) + * @return bool|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharing/Active.php b/source/application/store/model/sharing/Active.php new file mode 100644 index 0000000..ed35fb8 --- /dev/null +++ b/source/application/store/model/sharing/Active.php @@ -0,0 +1,30 @@ + 0 && $this->where('active_id', '=', $active_id); + return $this->with(['goods.image.file', 'user']) + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} diff --git a/source/application/store/model/sharing/ActiveUsers.php b/source/application/store/model/sharing/ActiveUsers.php new file mode 100644 index 0000000..7e543a3 --- /dev/null +++ b/source/application/store/model/sharing/ActiveUsers.php @@ -0,0 +1,30 @@ +with(['sharingOrder.address', 'user']) + ->where('active_id', '=', $active_id) + ->order(['create_time' => 'asc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} diff --git a/source/application/store/model/sharing/Category.php b/source/application/store/model/sharing/Category.php new file mode 100644 index 0000000..949394d --- /dev/null +++ b/source/application/store/model/sharing/Category.php @@ -0,0 +1,79 @@ +deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|int + */ + public function edit($data) + { + $this->deleteCache(); + return $this->allowField(true)->save($data); + } + + /** + * 删除商品分类 + * @param $category_id + * @return bool|int + */ + public function remove($category_id) + { + // 判断是否存在商品 + if ($goodsCount = (new Goods)->getGoodsTotal(['category_id' => $category_id])) { + $this->error = '该分类下存在' . $goodsCount . '个商品,不允许删除'; + return false; + } + // 判断是否存在子分类 + if ((new self)->where(['parent_id' => $category_id])->count()) { + $this->error = '该分类下存在子分类,请先删除'; + return false; + } + $this->deleteCache(); + return $this->delete(); + } + + /** + * 删除缓存 + * @return bool + */ + private function deleteCache() + { + return Cache::rm('sharing_category_' . self::$wxapp_id); + } + +} diff --git a/source/application/store/model/sharing/Comment.php b/source/application/store/model/sharing/Comment.php new file mode 100644 index 0000000..8681910 --- /dev/null +++ b/source/application/store/model/sharing/Comment.php @@ -0,0 +1,91 @@ +save(['is_delete' => 1]); + } + + /** + * 获取评价总数量 + * @return int|string + */ + public function getCommentTotal() + { + return $this->where(['is_delete' => 0])->count(); + } + + /** + * 更新记录 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function edit($data) + { + // 开启事务 + $this->startTrans(); + try { + // 删除评价图片 + $this->image()->delete(); + // 添加评论图片 + isset($data['images']) && $this->addCommentImages($data['images']); + // 是否为图片评价 + $data['is_picture'] = !$this->image()->select()->isEmpty(); + // 更新评论记录 + $this->allowField(true)->save($data); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 添加评论图片 + * @param $images + * @return int + */ + private function addCommentImages($images) + { + $data = array_map(function ($image_id) { + return [ + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + }, $images); + return $this->image()->saveAll($data); + } + + /** + * 获取评价列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->with(['user', 'orderM', 'OrderGoods']) + ->where('is_delete', '=', 0) + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharing/CommentImage.php b/source/application/store/model/sharing/CommentImage.php new file mode 100644 index 0000000..98d4f3e --- /dev/null +++ b/source/application/store/model/sharing/CommentImage.php @@ -0,0 +1,14 @@ +error = '请上传商品图片'; + return false; + } + $data['content'] = isset($data['content']) ? $data['content'] : ''; + $data['wxapp_id'] = $data['sku']['wxapp_id'] = self::$wxapp_id; + // 开启事务 + $this->startTrans(); + try { + // 添加商品 + $this->allowField(true)->save($data); + // 商品规格 + $this->addGoodsSpec($data); + // 商品图片 + $this->addGoodsImages($data['images']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 添加商品图片 + * @param $images + * @return int + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + private function addGoodsImages($images) + { + $this->image()->delete(); + $data = array_map(function ($image_id) { + return [ + 'image_id' => $image_id, + 'wxapp_id' => self::$wxapp_id + ]; + }, $images); + return $this->image()->saveAll($data); + } + + /** + * 编辑商品 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function edit($data) + { + if (!isset($data['images']) || empty($data['images'])) { + $this->error = '请上传商品图片'; + return false; + } + $data['content'] = isset($data['content']) ? $data['content'] : ''; + $data['wxapp_id'] = $data['sku']['wxapp_id'] = self::$wxapp_id; + // 开启事务 + $this->startTrans(); + try { + // 保存商品 + $this->allowField(true)->save($data); + // 商品规格 + $this->addGoodsSpec($data, true); + // 商品图片 + $this->addGoodsImages($data['images']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->rollback(); + $this->error = $e->getMessage(); + return false; + } + } + + /** + * 添加商品规格 + * @param $data + * @param $isUpdate + * @throws \Exception + */ + private function addGoodsSpec($data, $isUpdate = false) + { + // 更新模式: 先删除所有规格 + $model = new GoodsSku; + $isUpdate && $model->removeAll($this['goods_id']); + // 添加规格数据 + if ($data['spec_type'] == '10') { + // 单规格 + $this->sku()->save($data['sku']); + } else if ($data['spec_type'] == '20') { + // 添加商品与规格关系记录 + $model->addGoodsSpecRel($this['goods_id'], $data['spec_many']['spec_attr']); + // 添加商品sku + $model->addSkuList($this['goods_id'], $data['spec_many']['spec_list']); + } + } + + /** + * 修改商品状态 + * @param $state + * @return false|int + */ + public function setStatus($state) + { + return $this->allowField(true)->save(['goods_status' => $state ? 10 : 20]) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->allowField(true)->save(['is_delete' => 1]); + } + + /** + * 获取当前商品总数 + * @param array $where + * @return int|string + * @throws \think\Exception + */ + public function getGoodsTotal($where = []) + { + return $this->where('is_delete', '=', 0)->where($where)->count(); + } + +} diff --git a/source/application/store/model/sharing/GoodsImage.php b/source/application/store/model/sharing/GoodsImage.php new file mode 100644 index 0000000..ecde950 --- /dev/null +++ b/source/application/store/model/sharing/GoodsImage.php @@ -0,0 +1,14 @@ + $item['spec_sku_id'], + 'goods_id' => $goods_id, + 'wxapp_id' => self::$wxapp_id, + ]); + } + return $this->allowField(true)->saveAll($data); + } + + /** + * 添加商品规格关系记录 + * @param $goods_id + * @param $spec_attr + * @return array|false + * @throws \Exception + */ + public function addGoodsSpecRel($goods_id, $spec_attr) + { + $data = []; + array_map(function ($val) use (&$data, $goods_id) { + array_map(function ($item) use (&$val, &$data, $goods_id) { + $data[] = [ + 'goods_id' => $goods_id, + 'spec_id' => $val['group_id'], + 'spec_value_id' => $item['item_id'], + 'wxapp_id' => self::$wxapp_id, + ]; + }, $val['spec_items']); + }, $spec_attr); + $model = new GoodsSpecRel; + return $model->saveAll($data); + } + + /** + * 移除指定商品的所有sku + * @param $goods_id + * @return int + */ + public function removeAll($goods_id) + { + $model = new GoodsSpecRel; + $model->where('goods_id','=', $goods_id)->delete(); + return $this->where('goods_id','=', $goods_id)->delete(); + } + +} diff --git a/source/application/store/model/sharing/GoodsSpecRel.php b/source/application/store/model/sharing/GoodsSpecRel.php new file mode 100644 index 0000000..8014186 --- /dev/null +++ b/source/application/store/model/sharing/GoodsSpecRel.php @@ -0,0 +1,14 @@ +setWhere($query); + // 获取数据列表 + return $this->with(['active', 'goods.image', 'address', 'user']) + ->alias('order') + ->field('order.*, active.status as active_status') + ->join('user', 'user.user_id = order.user_id', 'LEFT') + ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT') + ->where($this->transferDataType($dataType)) + ->where('order.is_delete', '=', 0) + ->order(['order.create_time' => 'desc']) + ->paginate(10, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 订单列表(全部) + * @param $dataType + * @param array $query + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListAll($dataType, $query = []) + { + // 检索查询条件 + !empty($query) && $this->setWhere($query); + // 获取数据列表 + return $this->with(['goods.image', 'address', 'user', 'extract', 'extract_shop']) + ->alias('order') + ->field('order.*, active.status as active_status') + ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT') + ->where($this->transferDataType($dataType)) + ->where('order.is_delete', '=', 0) + ->order(['create_time' => 'desc']) + ->select(); + } + + /** + * 订单导出 + * @param $dataType + * @param $query + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function exportList($dataType, $query) + { + // 获取订单列表 + $list = $this->getListAll($dataType, $query); + // 导出csv文件 + return (new Exportservice)->orderList($list); + } + + /** + * 批量发货模板 + */ + public function deliveryTpl() + { + return (new Exportservice)->deliveryTpl(); + } + + /** + * 设置检索查询条件 + * @param $query + */ + private function setWhere($query) + { + if (isset($query['search']) && !empty($query['search'])) { + $this->where('order_no|user.nickName', 'like', '%' . trim($query['search']) . '%'); + } + if (isset($query['start_time']) && !empty($query['start_time'])) { + $this->where('order.create_time', '>=', strtotime($query['start_time'])); + } + if (isset($query['end_time']) && !empty($query['end_time'])) { + $this->where('order.create_time', '<', strtotime($query['end_time']) + 86400); + } + if (isset($query['active_id']) && $query['active_id'] > 0) { + $this->where('order.active_id', '=', (int)$query['active_id']); + } + if (isset($query['delivery_type']) && !empty($query['delivery_type'])) { + $query['delivery_type'] > -1 && $this->where('delivery_type', '=', $query['delivery_type']); + } + if (isset($query['extract_shop_id']) && !empty($query['extract_shop_id'])) { + $query['extract_shop_id'] > -1 && $this->where('extract_shop_id', '=', $query['extract_shop_id']); + } + } + + /** + * 转义数据类型条件 + * @param $dataType + * @return array + */ + private function transferDataType($dataType) + { + // 数据类型 + $filter = []; + switch ($dataType) { + case 'all': + // 全部 + $filter = []; + break; + case 'pay': + // 待支付 + $filter = ['pay_status' => 10, 'order_status' => 10]; + break; + case 'sharing'; + // 拼团中 + $filter['active.status'] = 10; + break; + case 'sharing_succeed'; + // 拼团成功 + $filter['active.status'] = 20; + break; + case 'sharing_fail'; + // 拼团失败 + $filter['active.status'] = 30; + break; + case 'delivery': + // 待发货 + $this->where('IF ( (`order`.`order_type` = 20), (`active`.`status` = 20), TRUE)'); + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 10, + 'order_status' => ['in', [10, 21]] + ]; + break; + case 'receipt': + // 待收货 + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 20, + 'receipt_status' => 10 + ]; + break; + case 'complete': + // 已完成 + $filter = ['order_status' => 30]; + break; + case 'cancel': + // 已取消 + $filter = ['order_status' => 20]; + break; + } + return $filter; + } + + /** + * 确认发货(单独订单) + * @param $data + * @return array|bool|false + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + * @throws \Exception + */ + public function delivery($data) + { + // 转义为订单列表 + $orderList = [$this]; + // 验证订单是否满足发货条件 + if (!$this->verifyDelivery($orderList)) { + return false; + } + // 整理更新的数据 + $updateList = [[ + 'order_id' => $this['order_id'], + 'express_id' => $data['express_id'], + 'express_no' => $data['express_no'] + ]]; + // 更新订单发货状态 + if ($status = $this->updateToDelivery($updateList)) { + // 获取已发货的订单 + $completed = self::detail($this['order_id'], ['user', 'address', 'goods', 'express']); + // 发送消息通知 + $this->sendDeliveryMessage([$completed]); + } + return $status; + } + + /** + * 批量发货 + * @param $data + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function batchDelivery($data) + { + // 获取csv文件中的数据 + if (!$csvData = $this->getCsvData()) { + return false; + } + // 整理订单id集 + $orderNos = helper::getArrayColumn($csvData, 0); + // 获取订单列表数据 + $orderList = helper::arrayColumn2Key($this->getListByOrderNos($orderNos), 'order_no'); + // 验证订单是否存在 + $tempArr = array_values(array_diff($orderNos, array_keys($orderList))); + if (!empty($tempArr)) { + $this->error = "订单号[{$tempArr[0]}] 不存在!"; + return false; + } + // 整理物流单号 + $updateList = []; + foreach ($csvData as $item) { + $updateList[] = [ + 'order_id' => $orderList[$item[0]]['order_id'], + 'express_id' => $data['express_id'], + 'express_no' => $item[1], + ]; + } + // 验证订单是否满足发货条件 + if (!$this->verifyDelivery($orderList)) { + return false; + } + // 更新订单发货状态(批量) + if ($status = $this->updateToDelivery($updateList)) { + // 获取已发货的订单 + $completed = $this->getListByOrderNos($orderNos, ['user', 'address', 'goods', 'express']); + // 发送消息通知 + $this->sendDeliveryMessage($completed); + } + return $status; + } + + /** + * 确认发货后发送消息通知 + * @param array|\think\Collection $orderList + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function sendDeliveryMessage($orderList) + { + // 实例化消息通知服务类 + $Service = new MessageService; + foreach ($orderList as $item) { + // 发送消息通知 + $Service->delivery($item, OrderTypeEnum::SHARING); + } + return true; + } + + /** + * 更新订单发货状态(批量) + * @param $orderList + * @return array|false + * @throws \Exception + */ + private function updateToDelivery($orderList) + { + $data = []; + foreach ($orderList as $item) { + $data[] = [ + 'order_id' => $item['order_id'], + 'express_no' => $item['express_no'], + 'express_id' => $item['express_id'], + 'delivery_status' => 20, + 'delivery_time' => time(), + ]; + } + return $this->isUpdate()->saveAll($data); + } + + /** + * 验证订单是否满足发货条件 + * @param $orderList + * @return bool + */ + private function verifyDelivery($orderList) + { + foreach ($orderList as $order) { + if ( + $order['pay_status']['value'] != 20 + || $order['delivery_type']['value'] != DeliveryTypeEnum::EXPRESS + || $order['delivery_status']['value'] != 10 + || ( + // 拼团订单验证拼单状态 + $order['order_type']['value'] == 20 + && $order['active']['status']['value'] != 20 + ) + ) { + $this->error = "订单号[{$order['order_no']}] 不满足发货条件!"; + return false; + } + } + return true; + } + + /** + * 获取csv文件中的数据 + * @return array|bool + */ + private function getCsvData() + { + // 获取表单上传文件 例如上传了001.jpg + $file = \request()->file('iFile'); + if (empty($file)) { + $this->error = '请上传发货模板'; + return false; + } + // 设置区域信息 + setlocale(LC_ALL, 'zh_CN'); + // 打开上传的文件 + $csvFile = fopen($file->getInfo()['tmp_name'], 'r'); + // 忽略第一行(csv标题) + fgetcsv($csvFile); + // 遍历并记录订单信息 + $orderList = []; + while ($item = fgetcsv($csvFile)) { + if (!isset($item[0]) || empty($item[0]) || !isset($item[1]) || empty($item[1])) { + $this->error = '模板文件数据不合法'; + return false; + } + $orderList[] = $item; + } + if (empty($orderList)) { + $this->error = '模板文件中没有订单数据'; + return false; + } + return $orderList; + } + + /** + * 修改订单价格 + * @param $data + * @return bool + */ + public function updatePrice($data) + { + if ($this['pay_status']['value'] != 10) { + $this->error = '该订单不合法'; + return false; + } + // 实际付款金额 + $payPrice = bcadd($data['update_price'], $data['update_express_price'], 2); + if ($payPrice <= 0) { + $this->error = '订单实付款价格不能为0.00元'; + return false; + } + return $this->save([ + 'order_no' => $this->orderNo(), // 修改订单号, 否则微信支付提示重复 + 'order_price' => $data['update_price'], + 'pay_price' => $payPrice, + 'update_price' => helper::bcsub($data['update_price'], helper::bcsub($this['total_price'], $this['coupon_money'])), + 'express_price' => $data['update_express_price'] + ]) !== false; + } + + /** + * 审核:用户取消订单 + * @param $data + * @return bool + */ + public function confirmCancel($data) + { + // 判断订单是否有效 + if ($this['pay_status']['value'] != 20) { + $this->error = '该订单不合法'; + return false; + } + // 订单取消事件 + return $this->transaction(function () use ($data) { + if ($data['is_cancel'] == true) { + // 执行退款操作 + (new RefundService)->execute($this); + // 回退商品库存 + (new OrderGoods)->backGoodsStock($this['goods'], true); + // 回退用户优惠券 + $this['coupon_id'] > 0 && UserCouponModel::setIsUse($this['coupon_id'], false); + // 回退用户积分 + $User = UserModel::detail($this['user_id']); + $describe = "订单取消:{$this['order_no']}"; + $this['points_num'] > 0 && $User->setIncPoints($this['points_num'], $describe); + } + // 更新订单状态 + return $this->save(['order_status' => $data['is_cancel'] ? 20 : 10]); + }); + } + + /** + * 拼团失败手动退款 + * @return bool|false|int + * @throws \app\common\exception\BaseException + * @throws \think\Exception + * @throws \think\exception\DbException + */ + public function refund() + { + if ( + $this['order_type']['value'] != 20 + || $this['pay_status']['value'] != 20 + || $this['active']['status']['value'] != 30 + || $this['is_refund'] == 1 + ) { + $this->error = '该订单不合法'; + return false; + } + // 执行退款操作 + (new RefundService)->execute($this); + // 更新订单状态 + return $this->save(['order_status' => 20, 'is_refund' => 1]); + } + + /** + * 获取已付款订单总数 (可指定某天) + * @param null $day + * @return int|string + * @throws \think\Exception + */ + public function getPayOrderTotal($day = null) + { + $filter = ['pay_status' => 20]; + if (!is_null($day)) { + $startTime = strtotime($day); + $filter['pay_time'] = [ + ['>=', $startTime], + ['<', $startTime + 86400], + ]; + } + return $this->getOrderTotal($filter); + } + + /** + * 获取订单总数量 + * @param array $filter + * @return int|string + * @throws \think\Exception + */ + public function getOrderTotal($filter = []) + { + return $this->where($filter) + ->where('is_delete', '=', 0) + ->count(); + } + + /** + * 获取某天的总销售额 + * @param $day + * @return float|int + */ + public function getOrderTotalPrice($day) + { + $startTime = strtotime($day); + return $this->where('pay_time', '>=', $startTime) + ->where('pay_time', '<', $startTime + 86400) + ->where('pay_status', '=', 20) + ->where('is_delete', '=', 0) + ->sum('pay_price'); + } + + /** + * 获取某天的下单用户数 + * @param $day + * @return float|int + */ + public function getPayOrderUserTotal($day) + { + $startTime = strtotime($day); + $userIds = $this->distinct(true) + ->where('pay_time', '>=', $startTime) + ->where('pay_time', '<', $startTime + 86400) + ->where('pay_status', '=', 20) + ->where('is_delete', '=', 0) + ->column('user_id'); + return count($userIds); + } + +} diff --git a/source/application/store/model/sharing/OrderAddress.php b/source/application/store/model/sharing/OrderAddress.php new file mode 100644 index 0000000..0909921 --- /dev/null +++ b/source/application/store/model/sharing/OrderAddress.php @@ -0,0 +1,15 @@ +where('order.order_no', 'like', "%{$query['order_no']}%"); + } + // 查询条件:起始日期 + if (isset($query['start_time']) && !empty($query['start_time'])) { + $this->where('m.create_time', '>=', strtotime($query['start_time'])); + } + // 查询条件:截止日期 + if (isset($query['end_time']) && !empty($query['end_time'])) { + $this->where('m.create_time', '<', strtotime($query['end_time']) + 86400); + } + // 售后类型 + if (isset($query['type']) && $query['type'] > 0) { + $this->where('m.type', '=', $query['type']); + } + // 处理状态 + if (isset($query['state']) && is_numeric($query['state'])) { + $this->where('m.status', '=', $query['state']); + } + // 获取列表数据 + return $this->alias('m') + ->field('m.*, order.order_no') + ->with(['order_goods.image', 'orderMaster', 'user']) + ->join('sharing_order order', 'order.order_id = m.order_id') + ->order(['m.create_time' => 'desc']) + ->paginate(10, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 商家审核 + * @param $data + * @return bool + * @throws \think\exception\PDOException + */ + public function audit($data) + { + if ($data['is_agree'] == 20 && empty($data['refuse_desc'])) { + $this->error = '请输入拒绝原因'; + return false; + } + if ($data['is_agree'] == 10 && empty($data['address_id'])) { + $this->error = '请选择退货地址'; + return false; + } + $this->startTrans(); + try { + // 拒绝申请, 标记售后单状态为已拒绝 + $data['is_agree'] == 20 && $data['status'] = 10; + // 同意换货申请, 标记售后单状态为已完成 + $data['is_agree'] == 10 && $this['type']['value'] == 20 && $data['status'] = 20; + // 更新退款单状态 + $this->allowField(true)->save($data); + // 同意售后申请, 记录退货地址 + if ($data['is_agree'] == 10) { + $model = new OrderRefundAddress; + $model->add($this['order_refund_id'], $data['address_id']); + } + // 订单详情 + $order = Order::detail($this['order_id']); + // 发送模板消息 + (new MessageService)->refund(static::detail($this['order_refund_id']), $order['order_no'], OrderTypeEnum::SHARING); + // 事务提交 + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 确认收货并退款 + * @param $data + * @return bool + * @throws \think\exception\DbException + */ + public function receipt($data) + { + // 订单详情 + $order = Order::detail($this['order_id']); + if ($data['refund_money'] > min($order['pay_price'], $this['order_goods']['total_pay_price'])) { + $this->error = '退款金额不能大于商品实付款金额'; + return false; + } + $this->transaction(function () use ($order, $data) { + // 更新售后单状态 + $this->allowField(true)->save([ + 'refund_money' => $data['refund_money'], + 'is_receipt' => 1, + 'status' => 20 + ]); + // 消减用户的实际消费金额 + // 条件:判断订单是否已结算 + if ($order['is_settled'] == true) { + (new UserModel)->setDecUserExpend($order['user_id'], $data['refund_money']); + } + // 执行原路退款 + (new RefundService)->execute($order, $data['refund_money']); + // 发送模板消息 + (new MessageService)->refund(self::detail($this['order_refund_id']), $order['order_no'], OrderTypeEnum::SHARING); + }); + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharing/OrderRefundAddress.php b/source/application/store/model/sharing/OrderRefundAddress.php new file mode 100644 index 0000000..5ec22a4 --- /dev/null +++ b/source/application/store/model/sharing/OrderRefundAddress.php @@ -0,0 +1,34 @@ +save([ + 'order_refund_id' => $order_refund_id, + 'name' => $detail['name'], + 'phone' => $detail['phone'], + 'detail' => $detail['detail'], + 'wxapp_id' => self::$wxapp_id + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharing/OrderRefundImage.php b/source/application/store/model/sharing/OrderRefundImage.php new file mode 100644 index 0000000..9a7ac42 --- /dev/null +++ b/source/application/store/model/sharing/OrderRefundImage.php @@ -0,0 +1,15 @@ + '基础设置', + ]; + + /** + * 更新系统设置 + * @param $key + * @param $values + * @return bool + * @throws \think\exception\DbException + */ + public function edit($key, $values) + { + $model = self::detail($key) ?: $this; + // 删除系统设置缓存 + Cache::rm('sharing_setting_' . self::$wxapp_id); + return $model->save([ + 'key' => $key, + 'describe' => $this->describe[$key], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/Active.php b/source/application/store/model/sharp/Active.php new file mode 100644 index 0000000..372e205 --- /dev/null +++ b/source/application/store/model/sharp/Active.php @@ -0,0 +1,132 @@ +with(['active_time']) + ->where('is_delete', '=', 0) + ->order(['active_date' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + return $this->getActiveTimeCount($list); + } + + private function getActiveTimeCount($list) + { + foreach ($list as &$item) { + $activeTimeArr = helper::getArrayColumn($item['active_time'], 'active_time'); + $item['active_time_count'] = count($activeTimeArr); + } + return $list; + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \Exception + */ + public function add($data) + { + // 表单验证 + if (!$this->onValidate($data)) return false; + // 新增活动 + $data['wxapp_id'] = static::$wxapp_id; + $data['active_date'] = strtotime($data['active_date']); + !isset($data['sharp_goods']) && $data['sharp_goods'] = []; + return $this->transaction(function () use ($data) { + // 新增活动 + $this->allowField(true)->save($data); + // 新增活动场次 + (new ActiveTimeModel)->onBatchAdd( + $this['active_id'], + $data['active_times'], + $data['sharp_goods'] + ); + return true; + }); + } + + /** + * 表单验证 + * @param $data + * @return bool + */ + private function onValidate($data) + { + // 活动日期是否已存在 + if ($this->isExistByActiveDate($data['active_date'])) { + $this->error = '该活动日期已存在'; + return false; + } +// // 验证是否选择商品 +// if (!isset($data['sharp_goods']) || empty($data['sharp_goods'])) { +// $this->error = '您还没有选择秒杀商品'; +// return false; +// } + return true; + } + + /** + * 活动日期是否已存在 + * @param $date + * @return bool + */ + private function isExistByActiveDate($date) + { + return !!(new static)->where('active_date', '=', strtotime($date)) + ->where('is_delete', '=', 0) + ->value('active_id'); + } + + /** + * 修改商品状态 + * @param $state + * @return false|int + */ + public function setStatus($state) + { + return $this->allowField(true)->save(['status' => (int)$state]) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + // 同步删除场次和商品关联 + (new ActiveTimeModel)->onDeleteByActiveId($this['active_id']); + // 将该活动设置为已删除 + return $this->allowField(true)->save(['is_delete' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/ActiveGoods.php b/source/application/store/model/sharp/ActiveGoods.php new file mode 100644 index 0000000..55ec9f4 --- /dev/null +++ b/source/application/store/model/sharp/ActiveGoods.php @@ -0,0 +1,33 @@ +where('sharp_goods_id', '=', $sharpGoodsId)->delete(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/ActiveTime.php b/source/application/store/model/sharp/ActiveTime.php new file mode 100644 index 0000000..e24df10 --- /dev/null +++ b/source/application/store/model/sharp/ActiveTime.php @@ -0,0 +1,248 @@ +with(['active']) + ->withCount(['goods']) + ->where('active_id', '=', $activeId) + ->order(['active_time' => 'asc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + return $list; + } + + /** + * 修改商品状态 + * @param $state + * @return false|int + */ + public function setStatus($state) + { + return $this->allowField(true)->save(['status' => (int)$state]) !== false; + } + + /** + * 获取指定会场的所有场次时间 + * @param $activeId + * @return array + */ + public function getActiveTimeData($activeId) + { + return $this->where('active_id', '=', $activeId)->column('active_time'); + } + + /** + * 根据活动场次ID获取商品列表 (格式化后用于编辑页) + * @param $activeTimeId + * @return array + */ + public function getGoodsListByActiveTimeId($activeTimeId) + { + $data = []; + foreach (ActiveGoodsModel::getGoodsListByActiveTimeId($activeTimeId) as $item) { + $data[] = [ + 'goods_id' => $item['sharp_goods_id'], + 'goods_name' => $item['goods']['goods_name'], + 'goods_image' => $item['goods']['goods_image'], + ]; + } + return $data; + } + + /** + * 新增记录 + * @param $activeId + * @param $data + * @return bool|mixed + */ + public function add($activeId, $data) + { + // 表单验证 + if (!$this->onValidate($data)) return false; + // 事务处理 + return $this->transaction(function () use ($activeId, $data) { + // 新增活动场次 + $this->onBatchAdd( + $activeId, + $data['active_times'], + $data['sharp_goods'], + $data['status'] + ); + return true; + }); + } + + /** + * 更新记录 + * @param $data + * @return bool|mixed + */ + public function edit($data) + { + // 验证是否选择商品 + if (!$this->onValidateSharpGoods($data)) { + return false; + } + // 事务处理 + return $this->transaction(function () use ($data) { + // 更新活动场次 + $this->allowField(true)->save($data); + // 更新场次的商品关联记录 + $this->onUpdateActiveGoodsRec($data['sharp_goods']); + return true; + }); + } + + /** + * 更新当前场次的商品关联记录 + * @param $sharpGoodsIds + * @return array|false + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + private function onUpdateActiveGoodsRec($sharpGoodsIds) + { + $saveData = []; + foreach ($sharpGoodsIds as $goodsId) { + $saveData[] = [ + 'active_id' => $this['active_id'], + 'active_time_id' => $this['active_time_id'], + 'sharp_goods_id' => $goodsId, + 'wxapp_id' => static::$wxapp_id, + ]; + } + $this->goods()->delete(); + return (new ActiveGoodsModel)->isUpdate(false)->saveAll($saveData); + } + + /** + * 表单验证 + * @param $data + * @return bool + */ + private function onValidate($data) + { + // 验证是否选择活动场次 + if (!isset($data['active_times']) || empty($data['active_times'])) { + $this->error = '您还没有选择活动场次'; + return false; + } + // 验证是否选择商品 + if (!$this->onValidateSharpGoods($data)) { + return false; + } + return true; + } + + /** + * 验证是否选择商品 + * @param $data + * @return bool + */ + private function onValidateSharpGoods($data) + { + // 验证是否选择商品 + if (!isset($data['sharp_goods']) || empty($data['sharp_goods'])) { + $this->error = '您还没有选择秒杀商品'; + return false; + } + return true; + } + + /** + * 批量新增活动场次 + * @param $activeId + * @param array $times + * @param array $sharpGoodsIds + * @param int $status + * @return bool + * @throws \Exception + */ + public function onBatchAdd($activeId, $times, $sharpGoodsIds, $status = 1) + { + $saveData = []; + foreach ($times as $time) { + $saveData[] = [ + 'active_id' => $activeId, + 'active_time' => (int)$time, + 'status' => (int)$status, + 'wxapp_id' => static::$wxapp_id, + ]; + } + // 批量更新 + $activeTimes = $this->isUpdate(false)->saveAll($saveData); + // 新增活动场次与商品关联关系记录 + if (!empty($sharpGoodsIds)) { + $this->onBatchAddActiveGoodsRec($activeTimes, $sharpGoodsIds); + } + return true; + } + + /** + * 新增活动场次与商品关联记录 + * @param $activeTimes + * @param $sharpGoodsIds + * @return array|false + * @throws \Exception + */ + private function onBatchAddActiveGoodsRec($activeTimes, $sharpGoodsIds) + { + $saveData = []; + foreach ($activeTimes as $item) { + foreach ($sharpGoodsIds as $goodsId) { + $saveData[] = [ + 'active_id' => $item['active_id'], + 'active_time_id' => $item['active_time_id'], + 'sharp_goods_id' => $goodsId, + 'wxapp_id' => static::$wxapp_id, + ]; + } + } + return (new ActiveGoodsModel)->isUpdate(false)->saveAll($saveData); + } + + /** + * 根据活动ID删除全部场次和商品关系 + * @param $activeId + * @return bool + */ + public function onDeleteByActiveId($activeId) + { + $this->where('active_id', '=', $activeId)->delete(); + (new ActiveGoodsModel)->where('active_id', '=', $activeId)->delete(); + return true; + } + + /** + * 删除当前场次 + * @return bool + * @throws \think\Exception + * @throws \think\exception\PDOException + */ + public function onDelete() + { + $this->delete(); + $this->goods()->delete(); + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/Goods.php b/source/application/store/model/sharp/Goods.php new file mode 100644 index 0000000..8c6ccee --- /dev/null +++ b/source/application/store/model/sharp/Goods.php @@ -0,0 +1,183 @@ +setBaseQuery($this->alias, [ + ['goods', 'goods_id'], + ]); + // 检索查询条件 + if (!empty($search)) { + $this->where('goods.goods_name', 'like', "%{$search}%"); + } + // 获取活动列表 + $list = $this->where("{$this->alias}.is_delete", '=', 0) + ->order(["{$this->alias}.sort" => 'asc', "{$this->alias}.create_time" => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + // 设置商品数据 + return $this->setGoodsListData($list, true); + } + + /** + * 根据商品id集获取商品列表 + * @param array $goodsIds + * @param array $param + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getListByIds($goodsIds, $param = []) + { + // 获取商品列表数据 + $list = parent::getListByIds($goodsIds, $param); + // 整理列表数据并返回 + return $this->setGoodsListData($list, true); + } + + /** + * 添加商品 + * @param $goods + * @param $data + * @return bool + * @throws \Exception + */ + public function add($goods, $data) + { + // 添加商品 + $this->allowField(true)->save(array_merge($data, [ + 'goods_id' => $goods['goods_id'], + 'seckill_stock' => $this->getSeckillStock($goods, $data), + 'wxapp_id' => self::$wxapp_id, + ])); + // 商品规格 + $this->addGoodsSpec($goods, $data); + return true; + } + + /** + * 编辑商品 + * @param $goods + * @param $data + * @return bool + * @throws \Exception + */ + public function edit($goods, $data) + { + // 更新商品 + $this->allowField(true)->save(array_merge($data, [ + 'seckill_stock' => $this->getSeckillStock($goods, $data), + ])); + // 商品规格 + $this->addGoodsSpec($goods, $data, true); + return true; + } + + /** + * 获取总库存数量 + * @param $goods + * @param $data + * @return int + */ + private function getSeckillStock($goods, $data) + { + if ($goods['spec_type'] == '10') { + return $data['sku']['seckill_stock']; + } + $seckillStock = 0; + foreach ($data['spec_many']['spec_list'] as $item) { + $seckillStock += $item['form']['seckill_stock']; + } + return $seckillStock; + } + + /** + * 验证商品ID能否被添加 + * @param $goodsId + * @return bool + */ + public function validateGoodsId($goodsId) + { + if ($goodsId <= 0) { + $this->error = '很抱歉,您还没有选择商品'; + return false; + } + // 验证是否存在秒杀商品 + if ($this->isExistGoodsId($goodsId)) { + $this->error = '很抱歉,该商品已存在,无需重复添加'; + return false; + } + return true; + } + + /** + * 添加商品规格 + * @param $goods + * @param $data + * @param bool $isUpdate + * @return array|false|\think\Model + * @throws \Exception + */ + private function addGoodsSpec($goods, $data, $isUpdate = false) + { + // 更新模式: 先删除所有规格 + $model = new GoodsSkuModel; + $isUpdate && $model->removeAll($this['sharp_goods_id']); + // 添加sku (单规格) + if ($goods['spec_type'] == '10') { + return $this->sku()->save(array_merge($data['sku'], [ + 'wxapp_id' => self::$wxapp_id, + ])); + } + // 添加sku (多规格) + return $model->addSkuList($this['sharp_goods_id'], $data['spec_many']['spec_list']); + } + + /** + * 商品ID是否存在 + * @param $goodsId + * @return bool + */ + public static function isExistGoodsId($goodsId) + { + return !!(new static)->where('goods_id', '=', $goodsId) + ->where('is_delete', '=', 0) + ->value('sharp_goods_id'); + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + // 同步删除活动会场与商品关联记录 + $model = new ActiveGoodsModel; + $model->onDeleteSharpGoods($this['sharp_goods_id']); + return $this->allowField(true)->save(['is_delete' => 1]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/GoodsSku.php b/source/application/store/model/sharp/GoodsSku.php new file mode 100644 index 0000000..e60b997 --- /dev/null +++ b/source/application/store/model/sharp/GoodsSku.php @@ -0,0 +1,44 @@ + $item['spec_sku_id'], + 'sharp_goods_id' => $sharpGoodsId, + 'wxapp_id' => self::$wxapp_id, + ]); + } + return $this->allowField(true)->saveAll($data); + } + + /** + * 移除指定商品的所有sku + * @param $sharpGoodsId + * @return int + */ + public function removeAll($sharpGoodsId) + { + return $this->where('sharp_goods_id', '=', $sharpGoodsId)->delete(); + } + +} \ No newline at end of file diff --git a/source/application/store/model/sharp/Setting.php b/source/application/store/model/sharp/Setting.php new file mode 100644 index 0000000..67b0cb6 --- /dev/null +++ b/source/application/store/model/sharp/Setting.php @@ -0,0 +1,43 @@ + '基础设置', + ]; + + /** + * 更新系统设置 + * @param $key + * @param $values + * @return bool + * @throws \think\exception\DbException + */ + public function edit($key, $values) + { + $model = self::detail($key) ?: $this; + // 删除系统设置缓存 + Cache::rm('sharp_setting_' . self::$wxapp_id); + return $model->save([ + 'key' => $key, + 'describe' => $this->describe[$key], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/Access.php b/source/application/store/model/store/Access.php new file mode 100644 index 0000000..c01bb68 --- /dev/null +++ b/source/application/store/model/store/Access.php @@ -0,0 +1,56 @@ +getAll() as $item) { + $jsTree[] = [ + 'id' => $item['access_id'], + 'parent' => $item['parent_id'] > 0 ? $item['parent_id'] : '#', + 'text' => $item['name'], + 'state' => [ + 'selected' => (in_array($item['access_id'], $accessIds) && !$this->hasChildren($item['access_id'])) + ] + ]; + } + return json_encode($jsTree); + } + + /** + * 是否存在子集 + * @param $access_id + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function hasChildren($access_id) + { + foreach (self::getAll() as $item) { + if ($item['parent_id'] == $access_id) + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/Role.php b/source/application/store/model/store/Role.php new file mode 100644 index 0000000..710f49a --- /dev/null +++ b/source/application/store/model/store/Role.php @@ -0,0 +1,187 @@ +getAll(); + return $this->formatTreeData($all); + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \Exception + */ + public function add($data) + { + $data['wxapp_id'] = self::$wxapp_id; + if (empty($data['access'])) { + $this->error = '请选择权限'; + return false; + } + $this->startTrans(); + try { + // 新增角色记录 + $this->allowField(true)->save($data); + // 新增角色权限关系记录 + (new RoleAccess)->add($this['role_id'], $data['access']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 更新记录 + * @param $data + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \think\exception\PDOException + */ + public function edit($data) + { + if (empty($data['access'])) { + $this->error = '请选择权限'; + return false; + } + // 判断上级角色是否为当前子级 + if ($data['parent_id'] > 0) { + // 获取所有上级id集 + $parentIds = $this->getTopRoleIds($data['parent_id']); + if (in_array($this['role_id'], $parentIds)) { + $this->error = '上级角色不允许设置为当前子角色'; + return false; + } + } + $this->startTrans(); + try { + // 更新角色记录 + $this->allowField(true)->save($data); + // 更新角色权限关系记录 + (new RoleAccess)->edit($this['role_id'], $data['access']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 获取所有上级id集 + * @param $role_id + * @param null $all + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getTopRoleIds($role_id, &$all = null) + { + static $ids = []; + is_null($all) && $all = $this->getAll(); + foreach ($all as $item) { + if ($item['role_id'] == $role_id && $item['parent_id'] > 0) { + $ids[] = $item['parent_id']; + $this->getTopRoleIds($item['parent_id'], $all); + } + } + return $ids; + } + + /** + * 删除记录 + * @return bool|int + * @throws \think\exception\DbException + */ + public function remove() + { + // 判断是否存在下级角色 + if (self::detail(['parent_id' => $this['role_id']])) { + $this->error = '当前角色下存在子角色,不允许删除'; + return false; + } + // 删除对应的权限关系 + RoleAccess::deleteAll(['role_id' => $this['role_id']]); + return $this->delete(); + } + + /** + * 获取所有角色 + * @return array + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getAll() + { + $data = $this->order(['sort' => 'asc', 'create_time' => 'asc'])->select(); + return $data ? $data->toArray() : []; + } + + /** + * 获取权限列表 + * @param $all + * @param int $parent_id + * @param int $deep + * @return array + */ + private function formatTreeData(&$all, $parent_id = 0, $deep = 1) + { + static $tempTreeArr = []; + foreach ($all as $key => $val) { + if ($val['parent_id'] == $parent_id) { + // 记录深度 + $val['deep'] = $deep; + // 根据角色深度处理名称前缀 + $val['role_name_h1'] = $this->htmlPrefix($deep) . $val['role_name']; + $tempTreeArr[] = $val; + $this->formatTreeData($all, $val['role_id'], $deep + 1); + } + } + return $tempTreeArr; + } + + /** + * 角色名称 html格式前缀 + * @param $deep + * @return string + */ + private function htmlPrefix($deep) + { + // 根据角色深度处理名称前缀 + $prefix = ''; + if ($deep > 1) { + for ($i = 1; $i <= $deep - 1; $i++) { + $prefix .= '   ├ '; + } + $prefix .= ' '; + } + return $prefix; + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/RoleAccess.php b/source/application/store/model/store/RoleAccess.php new file mode 100644 index 0000000..8f734f1 --- /dev/null +++ b/source/application/store/model/store/RoleAccess.php @@ -0,0 +1,95 @@ + $role_id, + 'access_id' => $access_id, + 'wxapp_id' => self::$wxapp_id, + ]; + } + return $this->saveAll($data); + } + + /** + * 更新关系记录 + * @param $role_id + * @param array $newAccess 新的权限集 + * @return array|false + * @throws \Exception + */ + public function edit($role_id, $newAccess) + { + // 已分配的权限集 + $assignAccessIds = self::getAccessIds($role_id); + + /** + * 找出删除的权限 + * 假如已有的权限集合是A,界面传递过得权限集合是B + * 权限集合A当中的某个权限不在权限集合B当中,就应该删除 + * 使用 array_diff() 计算补集 + */ + if ($deleteAccessIds = array_diff($assignAccessIds, $newAccess)) { + self::deleteAll(['role_id' => $role_id, 'access_id' => ['in', $deleteAccessIds]]); + } + + /** + * 找出添加的权限 + * 假如已有的权限集合是A,界面传递过得权限集合是B + * 权限集合B当中的某个权限不在权限集合A当中,就应该添加 + * 使用 array_diff() 计算补集 + */ + $newAccessIds = array_diff($newAccess, $assignAccessIds); + $data = []; + foreach ($newAccessIds as $access_id) { + $data[] = [ + 'role_id' => $role_id, + 'access_id' => $access_id, + 'wxapp_id' => self::$wxapp_id, + ]; + } + return $this->saveAll($data); + } + + /** + * 获取指定角色的所有权限id + * @param int|array $role_id 角色id (支持数组) + * @return array + */ + public static function getAccessIds($role_id) + { + $roleIds = is_array($role_id) ? $role_id : [(int)$role_id]; + return (new self)->where('role_id', 'in', $roleIds)->column('access_id'); + } + + /** + * 删除记录 + * @param $where + * @return int + */ + public static function deleteAll($where) + { + return self::destroy($where); + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/Shop.php b/source/application/store/model/store/Shop.php new file mode 100644 index 0000000..a513bc4 --- /dev/null +++ b/source/application/store/model/store/Shop.php @@ -0,0 +1,113 @@ +where('status', '=', (int)$status); + return $this->where('is_delete', '=', '0') + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 获取所有门店列表 + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public static function getAllList() + { + return (new self)->where('is_delete', '=', '0') + ->order(['sort' => 'asc', 'create_time' => 'desc']) + ->select(); + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \Exception + */ + public function add($data) + { + if (!$this->validateForm($data)) { + return false; + } + return $this->allowField(true)->save($this->createData($data)); + } + + /** + * 编辑记录 + * @param $data + * @return false|int + */ + public function edit($data) + { + if (!$this->validateForm($data)) { + return false; + } + return $this->allowField(true)->save($this->createData($data)) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + + /** + * 创建数据 + * @param array $data + * @return array + */ + private function createData($data) + { + $data['wxapp_id'] = self::$wxapp_id; + // 格式化坐标信息 + $coordinate = explode(',', $data['coordinate']); + $data['latitude'] = $coordinate[0]; + $data['longitude'] = $coordinate[1]; + // 生成geohash + $Geohash = new Geohash; + $data['geohash'] = $Geohash->encode($data['longitude'], $data['latitude']); + return $data; + } + + /** + * 表单验证 + * @param $data + * @return bool + */ + private function validateForm($data) + { + if (!isset($data['logo_image_id']) || empty($data['logo_image_id'])) { + $this->error = '请选择门店logo'; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/User.php b/source/application/store/model/store/User.php new file mode 100644 index 0000000..3a15d43 --- /dev/null +++ b/source/application/store/model/store/User.php @@ -0,0 +1,201 @@ +getLoginUser($data['user_name'], $data['password'])) { + $this->error = '登录失败, 用户名或密码错误'; + return false; + } + if (empty($user['wxapp'])) { + $this->error = '登录失败, 未找到小程序信息'; + return false; + } + if ($user['wxapp']['is_recycle']) { + $this->error = '登录失败, 当前小程序商城已删除'; + return false; + } + // 保存登录状态 + $this->loginState($user); + return true; + } + + /** + * 获取登录用户信息 + * @param $user_name + * @param $password + * @return array|false|\PDOStatement|string|\think\Model + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getLoginUser($user_name, $password) + { + return self::useGlobalScope(false)->with(['wxapp'])->where([ + 'user_name' => $user_name, + 'password' => yoshop_hash($password), + 'is_delete' => 0 + ])->find(); + } + + /** + * 获取用户列表 + * @return \think\Paginator + * @throws \think\exception\DbException + */ + public function getList() + { + return $this->where('is_delete', '=', '0') + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 新增记录 + * @param $data + * @return bool|false|int + * @throws \think\exception\DbException + */ + public function add($data) + { + if (self::checkExist($data['user_name'])) { + $this->error = '用户名已存在'; + return false; + } + if ($data['password'] !== $data['password_confirm']) { + $this->error = '确认密码不正确'; + return false; + } + if (empty($data['role_id'])) { + $this->error = '请选择所属角色'; + return false; + } + $this->startTrans(); + try { + // 新增管理员记录 + $data['password'] = yoshop_hash($data['password']); + $data['wxapp_id'] = self::$wxapp_id; + $data['is_super'] = 0; + $this->allowField(true)->save($data); + // 新增角色关系记录 + (new UserRole)->add($this['store_user_id'], $data['role_id']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 更新记录 + * @param array $data + * @return bool + * @throws \think\exception\DbException + */ + public function edit($data) + { + if ($this['user_name'] !== $data['user_name'] + && self::checkExist($data['user_name'])) { + $this->error = '用户名已存在'; + return false; + } + if (!empty($data['password']) && ($data['password'] !== $data['password_confirm'])) { + $this->error = '确认密码不正确'; + return false; + } + if (empty($data['role_id'])) { + $this->error = '请选择所属角色'; + return false; + } + if (!empty($data['password'])) { + $data['password'] = yoshop_hash($data['password']); + } else { + unset($data['password']); + } + $this->startTrans(); + try { + // 更新管理员记录 + $this->allowField(true)->save($data); + // 更新角色关系记录 + (new UserRole)->edit($this['store_user_id'], $data['role_id']); + $this->commit(); + return true; + } catch (\Exception $e) { + $this->error = $e->getMessage(); + $this->rollback(); + return false; + } + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + if ($this['is_super']) { + $this->error = '超级管理员不允许删除'; + return false; + } + // 删除对应的角色关系 + UserRole::deleteAll(['store_user_id' => $this['store_user_id']]); + return $this->save(['is_delete' => 1]); + } + + /** + * 更新当前管理员信息 + * @param $data + * @return bool + */ + public function renew($data) + { + if ($data['password'] !== $data['password_confirm']) { + $this->error = '确认密码不正确'; + return false; + } + if ($this['user_name'] !== $data['user_name'] + && self::checkExist($data['user_name'])) { + $this->error = '用户名已存在'; + return false; + } + // 更新管理员信息 + if ($this->save([ + 'user_name' => $data['user_name'], + 'password' => yoshop_hash($data['password']), + ]) === false) { + return false; + } + // 更新session + Session::set('yoshop_store.user', [ + 'store_user_id' => $this['store_user_id'], + 'user_name' => $data['user_name'], + ]); + return true; + } + +} diff --git a/source/application/store/model/store/UserRole.php b/source/application/store/model/store/UserRole.php new file mode 100644 index 0000000..dec1e53 --- /dev/null +++ b/source/application/store/model/store/UserRole.php @@ -0,0 +1,94 @@ + $store_user_id, + 'role_id' => $role_id, + 'wxapp_id' => self::$wxapp_id, + ]; + } + return $this->saveAll($data); + } + + /** + * 更新关系记录 + * @param $store_user_id + * @param array $newRole 新的角色集 + * @return array|false + * @throws \Exception + */ + public function edit($store_user_id, $newRole) + { + // 已分配的角色集 + $assignRoleIds = self::getRoleIds($store_user_id); + + /** + * 找出删除的角色 + * 假如已有的角色集合是A,界面传递过得角色集合是B + * 角色集合A当中的某个角色不在角色集合B当中,就应该删除 + * 使用 array_diff() 计算补集 + */ + if ($deleteRoleIds = array_diff($assignRoleIds, $newRole)) { + self::deleteAll(['store_user_id' => $store_user_id, 'role_id' => ['in', $deleteRoleIds]]); + } + + /** + * 找出添加的角色 + * 假如已有的角色集合是A,界面传递过得角色集合是B + * 角色集合B当中的某个角色不在角色集合A当中,就应该添加 + * 使用 array_diff() 计算补集 + */ + $newRoleIds = array_diff($newRole, $assignRoleIds); + $data = []; + foreach ($newRoleIds as $role_id) { + $data[] = [ + 'store_user_id' => $store_user_id, + 'role_id' => $role_id, + 'wxapp_id' => self::$wxapp_id, + ]; + } + return $this->saveAll($data); + } + + /** + * 获取指定管理员的所有角色id + * @param $store_user_id + * @return array + */ + public static function getRoleIds($store_user_id) + { + return (new self)->where('store_user_id', '=', $store_user_id)->column('role_id'); + } + + /** + * 删除记录 + * @param $where + * @return int + */ + public static function deleteAll($where) + { + return self::destroy($where); + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/shop/Clerk.php b/source/application/store/model/store/shop/Clerk.php new file mode 100644 index 0000000..011afbb --- /dev/null +++ b/source/application/store/model/store/shop/Clerk.php @@ -0,0 +1,102 @@ + -1 && $this->where('status', '=', (int)$status); + $shop_id > 0 && $this->where('shop_id', '=', (int)$shop_id); + !empty($search) && $this->where('real_name|mobile', 'like', "%{$search}%"); + // 查询列表数据 + return $this->with(['user', 'shop']) + ->where('is_delete', '=', '0') + ->order(['create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \Exception + */ + public function add($data) + { + // 表单验证 + if (!$this->validateForm($data, self::FORM_SCENE_ADD)) { + return false; + } + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return bool|false|int + * @throws \think\exception\DbException + */ + public function edit($data) + { + // 表单验证 + if (!$this->validateForm($data, self::FORM_SCENE_EDIT)) { + return false; + } + return $this->allowField(true)->save($data) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + return $this->save(['is_delete' => 1]); + } + + /** + * 表单验证 + * @param $data + * @param string $scene + * @return bool + * @throws \think\exception\DbException + */ + private function validateForm($data, $scene = self::FORM_SCENE_ADD) + { + if ($scene === self::FORM_SCENE_ADD) { + if (!isset($data['user_id']) || empty($data['user_id'])) { + $this->error = '请选择用户'; + return false; + } + if (self::detail(['user_id' => $data['user_id'], 'is_delete' => 0])) { + $this->error = '该用户已经是店员,无需重复添加'; + return false; + } + } + return true; + } + +} \ No newline at end of file diff --git a/source/application/store/model/store/shop/Order.php b/source/application/store/model/store/shop/Order.php new file mode 100644 index 0000000..d545c5a --- /dev/null +++ b/source/application/store/model/store/shop/Order.php @@ -0,0 +1,43 @@ + 0 && $this->where('clerk.shop_id', '=', (int)$shop_id); + !empty($search) && $this->where('clerk.real_name', 'like', "%{$search}%"); + // 查询列表数据 + $data = $this->with(['shop', 'clerk']) + ->alias('order') + ->field(['order.*']) + ->join('store_shop_clerk clerk', 'clerk.clerk_id = order.clerk_id', 'INNER') + ->order(['order.create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + if ($data->isEmpty()) { + return $data; + } + // 整理订单信息 + return OrderService::getOrderList($data); + } + +} \ No newline at end of file diff --git a/source/application/store/model/user/BalanceLog.php b/source/application/store/model/user/BalanceLog.php new file mode 100644 index 0000000..4d2b626 --- /dev/null +++ b/source/application/store/model/user/BalanceLog.php @@ -0,0 +1,61 @@ +setQueryWhere($query); + // 获取列表数据 + return $this->with(['user']) + ->alias('log') + ->field('log.*') + ->join('user', 'user.user_id = log.user_id') + ->order(['log.create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 设置查询条件 + * @param $query + */ + private function setQueryWhere($query) + { + // 设置默认的检索数据 + $params = $this->setQueryDefaultValue($query, [ + 'user_id' => 0, + 'search' => '', + 'scene' => -1, + 'start_time' => '', + 'end_time' => '', + ]); + // 用户ID + $params['user_id'] > 0 && $this->where('log.user_id', '=', $params['user_id']); + // 用户昵称 + !empty($params['search']) && $this->where('user.nickName', 'like', "%{$params['search']}%"); + // 余额变动场景 + $params['scene'] > -1 && $this->where('log.scene', '=', (int)$params['scene']); + // 起始时间 + !empty($params['start_time']) && $this->where('log.create_time', '>=', strtotime($params['start_time'])); + // 截止时间 + !empty($params['end_time']) && $this->where('log.create_time', '<', strtotime($params['end_time']) + 86400); + } + +} \ No newline at end of file diff --git a/source/application/store/model/user/Grade.php b/source/application/store/model/user/Grade.php new file mode 100644 index 0000000..216dbf7 --- /dev/null +++ b/source/application/store/model/user/Grade.php @@ -0,0 +1,97 @@ +where('is_delete', '=', 0) + ->order(['weight' => 'asc', 'create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + + /** + * 新增记录 + * @param $data + * @return bool + * @throws \Exception + */ + public function add($data) + { + if (!$this->validateForm($data)) { + return false; + } + $data['wxapp_id'] = self::$wxapp_id; + return $this->allowField(true)->save($data); + } + + /** + * 编辑记录 + * @param $data + * @return false|int + */ + public function edit($data) + { + if (!$this->validateForm($data, 'edit')) { + return false; + } + return $this->allowField(true)->save($data) !== false; + } + + /** + * 软删除 + * @return false|int + */ + public function setDelete() + { + // 判断该等级下是否存在会员 + if (UserModel::checkExistByGradeId($this['grade_id'])) { + $this->error = '该会员等级下存在用户,不允许删除'; + return false; + } + return $this->save(['is_delete' => 1]); + } + + /** + * 表单验证 + * @param $data + * @param string $scene + * @return bool + */ + private function validateForm($data, $scene = 'add') + { + if ($scene === 'add') { + // 需要判断等级权重是否已存在 + if (self::checkExistByWeight($data['weight'])) { + $this->error = '等级权重已存在'; + return false; + } + } elseif ($scene === 'edit') { + // 需要判断等级权重是否已存在 + if (self::checkExistByWeight($data['weight'], $this['grade_id'])) { + $this->error = '等级权重已存在'; + return false; + } + } + return true; + } + + +} \ No newline at end of file diff --git a/source/application/store/model/user/GradeLog.php b/source/application/store/model/user/GradeLog.php new file mode 100644 index 0000000..c603caa --- /dev/null +++ b/source/application/store/model/user/GradeLog.php @@ -0,0 +1,26 @@ +records([$data]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/user/PointsLog.php b/source/application/store/model/user/PointsLog.php new file mode 100644 index 0000000..fcb7728 --- /dev/null +++ b/source/application/store/model/user/PointsLog.php @@ -0,0 +1,58 @@ +setQueryWhere($query); + // 获取列表数据 + return $this->with(['user']) + ->alias('log') + ->field('log.*') + ->join('user', 'user.user_id = log.user_id') + ->order(['log.create_time' => 'desc']) + ->paginate(15, false, [ + 'query' => \request()->request() + ]); + } + + /** + * 设置查询条件 + * @param $query + */ + private function setQueryWhere($query) + { + // 设置默认的检索数据 + $params = $this->setQueryDefaultValue($query, [ + 'user_id' => 0, + 'search' => '', + 'start_time' => '', + 'end_time' => '', + ]); + // 用户ID + $params['user_id'] > 0 && $this->where('log.user_id', '=', $params['user_id']); + // 用户昵称 + !empty($params['search']) && $this->where('user.nickName', 'like', "%{$params['search']}%"); + // 起始时间 + !empty($params['start_time']) && $this->where('log.create_time', '>=', strtotime($params['start_time'])); + // 截止时间 + !empty($params['end_time']) && $this->where('log.create_time', '<', strtotime($params['end_time']) + 86400); + } + +} \ No newline at end of file diff --git a/source/application/store/model/wow/Order.php b/source/application/store/model/wow/Order.php new file mode 100644 index 0000000..e44c160 --- /dev/null +++ b/source/application/store/model/wow/Order.php @@ -0,0 +1,43 @@ +setBaseQuery($this->alias, [ + ['order', 'order_id'], + ['user', 'user_id'], + ]); + // 检索查询条件 + if (!empty($search)) { + $this->where(function ($query) use ($search) { + $query->whereOr('order.order_no', 'like', "%{$search}%") + ->whereOr('user.nickName', 'like', "%{$search}%"); + }); + } + // 返回列表数据 + return $this->with(['user']) + ->field(['wow_order.*', 'order.order_no', 'order.pay_price']) + ->where("{$this->alias}.is_delete", '=', 0) + ->order(["{$this->alias}.create_time" => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/wow/Setting.php b/source/application/store/model/wow/Setting.php new file mode 100644 index 0000000..e4193cd --- /dev/null +++ b/source/application/store/model/wow/Setting.php @@ -0,0 +1,43 @@ + '基础设置', + ]; + + /** + * 更新系统设置 + * @param $key + * @param $values + * @return bool + * @throws \think\exception\DbException + */ + public function edit($key, $values) + { + $model = self::detail($key) ?: $this; + // 删除系统设置缓存 + Cache::rm('wow_setting_' . self::$wxapp_id); + return $model->save([ + 'key' => $key, + 'describe' => $this->describe[$key], + 'values' => $values, + 'wxapp_id' => self::$wxapp_id, + ]) !== false; + } + +} \ No newline at end of file diff --git a/source/application/store/model/wow/Shoping.php b/source/application/store/model/wow/Shoping.php new file mode 100644 index 0000000..c9aa239 --- /dev/null +++ b/source/application/store/model/wow/Shoping.php @@ -0,0 +1,43 @@ +setBaseQuery($this->alias, [ + ['goods', 'goods_id'], + ['user', 'user_id'], + ]); + // 检索查询条件 + if (!empty($search)) { + $this->where(function ($query) use ($search) { + $query->whereOr('goods.goods_name', 'like', "%{$search}%") + ->whereOr('user.nickName', 'like', "%{$search}%"); + }); + } + // 返回列表数据 + return $this->with(['goods.image.file', 'user']) + ->where("{$this->alias}.is_delete", '=', 0) + ->order(["{$this->alias}.create_time" => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/model/wxapp/Formid.php b/source/application/store/model/wxapp/Formid.php new file mode 100644 index 0000000..bd69211 --- /dev/null +++ b/source/application/store/model/wxapp/Formid.php @@ -0,0 +1,32 @@ +with(['user']) + ->field(['user_id', 'count(id) AS total_formid']) + ->where('is_used', '=', 0) + ->where('expiry_time', '>', time()) + ->group('user_id') + ->order(['total_formid' => 'desc']) + ->paginate(15, false, [ + 'query' => request()->request() + ]); + } + +} \ No newline at end of file diff --git a/source/application/store/service/Auth.php b/source/application/store/service/Auth.php new file mode 100644 index 0000000..2a2b46e --- /dev/null +++ b/source/application/store/service/Auth.php @@ -0,0 +1,168 @@ +store = Session::get('yoshop_store'); + // 当前用户信息 + $this->user = User::detail($this->store['user']['store_user_id']); + } + + /** + * 私有化克隆方法 + */ + private function __clone() + { + } + + /** + * 验证指定url是否有访问权限 + * @param string|array $url + * @param bool $strict 严格模式(必须全部通过才返回true) + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function checkPrivilege($url, $strict = true) + { + if (!is_array($url)): + return $this->checkAccess($url); + else: + foreach ($url as $val): + if ($strict && !$this->checkAccess($val)) { + return false; + } + if (!$strict && $this->checkAccess($val)) { + return true; + } + endforeach; + endif; + return true; + } + + /** + * @param string $url + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function checkAccess($url) + { + // 超级管理员无需验证 + if ($this->user['is_super']) { + return true; + } + // 验证当前请求是否在白名单 + if (in_array($url, $this->allowAllAction)) { + return true; + } + // 通配符支持 + foreach ($this->allowAllAction as $action) { + if (strpos($action, '*') !== false + && preg_match('/^' . str_replace('/', '\/', $action) . '/', $url) + ) { + return true; + } + } + // 获取当前用户的权限url列表 + if (!in_array($url, $this->getAccessUrls())) { + return false; + } + return true; + } + + /** + * 获取当前用户的权限url列表 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getAccessUrls() + { + if (empty($this->accessUrls)) { + // 获取当前用户的角色集 + $roleIds = UserRole::getRoleIds($this->user['store_user_id']); + // 根据已分配的权限 + $accessIds = RoleAccess::getAccessIds($roleIds); + // 获取当前角色所有权限链接 + $this->accessUrls = Access::getAccessUrls($accessIds); + } + return $this->accessUrls; + } + +} \ No newline at end of file diff --git a/source/application/store/service/Goods.php b/source/application/store/service/Goods.php new file mode 100644 index 0000000..45e73af --- /dev/null +++ b/source/application/store/service/Goods.php @@ -0,0 +1,64 @@ +auth = Auth::getInstance(); + } + + /** + * 私有化克隆方法 + */ + private function __clone() + { + } + + /** + * 后台菜单配置 + * @param $routeUri + * @param $group + * @return array|mixed + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getMenus($routeUri, $group) + { + // 菜单列表数据 + $menus = Config::get('menus'); + $this->first($menus, $routeUri, $group); +// pre($menus); + return $menus; + } + + /** + * 一级菜单 + * @param $menus + * @param $routeUri + * @param $group + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function first(&$menus, $routeUri, $group) + { + foreach ($menus as $key => &$first) : + // 一级菜单索引url + $indexData = $this->getMenusIndexUrls($first, 1); + // 权限验证 + $first['index'] = $this->getAuthUrl($indexData); + if ($first['index'] === false) { + unset($menus[$key]); + continue; + } + // 菜单聚焦 + $first['active'] = $key === $group; + // 遍历:二级菜单 + if (isset($first['submenu'])) { + $this->second($first['submenu'], $routeUri); + } + endforeach; + } + + /** + * 二级菜单 + * @param array $menus + * @param $routeUri + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function second(&$menus, $routeUri) + { + foreach ($menus as $key => &$second) : + // 二级菜单索引url + $indexData = $this->getMenusIndexUrls($second, 2); + // 权限验证 + $second['index'] = $this->getAuthUrl($indexData); + if ($second['index'] === false) { + unset($menus[$key]); + continue; + } + // 二级菜单所有uri + $secondUris = []; + // 遍历:三级菜单 + if (isset($second['submenu'])) { + $this->third($second['submenu'], $routeUri, $secondUris); + } else { + if (isset($second['uris'])) + $secondUris = array_merge($secondUris, $second['uris']); + else + $secondUris[] = $second['index']; + } + // 二级菜单:active + !isset($second['active']) && $second['active'] = in_array($routeUri, $secondUris); + endforeach; + // 删除空数组 + $menus = array_filter($menus); + } + + /** + * 三级菜单 + * @param array $menus + * @param $routeUri + * @param $secondUris + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function third(&$menus, $routeUri, &$secondUris) + { + foreach ($menus as $key => &$third): + // 三级菜单索引url + $indexData = $this->getMenusIndexUrls($third, 3); + // 权限验证 + $third['index'] = $this->getAuthUrl($indexData); + if ($third['index'] === false) { + unset($menus[$key]); + continue; + } + // 三级菜单所有uri + $thirdUris = []; + if (isset($third['uris'])) { + $secondUris = array_merge($secondUris, $third['uris']); + $thirdUris = array_merge($thirdUris, $third['uris']); + } else { + $secondUris[] = $third['index']; + $thirdUris[] = $third['index']; + } + $third['active'] = in_array($routeUri, $thirdUris); + endforeach; + } + + /** + * 获取指定菜单下的所有索引url + * @param array $menus + * @param int $level + * @return array|null + */ + private function getMenusIndexUrls(&$menus, $level = 1) + { +// // 三级 +// if ($level === 3) { +// return isset($menus['index']) ? [$menus['index']] : null; +// } + // 判断是否存在url + if (!isset($menus['index']) && !isset($menus['submenu'])) { + return null; + } + $data = []; + if (isset($menus['index']) && !empty($menus['index'])) { + $data[] = $menus['index']; + } + if (isset($menus['submenu']) && !empty($menus['submenu'])) { + foreach ($menus['submenu'] as $submenu) { + $submenuIndex = $this->getMenusIndexUrls($submenu, ++$level); + !is_null($submenuIndex) && $data = array_merge($data, $submenuIndex); + } + } + return array_unique($data); + } + + /** + * 取出通过权限验证urk作为index + * @param $urls + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function getAuthUrl($urls) + { + // 取出通过权限验证urk作为index + foreach ($urls as $url) { + if ($this->auth->checkPrivilege($url)) return $url; + } + return false; + } + +} \ No newline at end of file diff --git a/source/application/store/service/goods/Apply.php b/source/application/store/service/goods/Apply.php new file mode 100644 index 0000000..7f57d14 --- /dev/null +++ b/source/application/store/service/goods/Apply.php @@ -0,0 +1,55 @@ +checkSharpGoods($goodsId); + } + + /** + * 验证商品是否允许删除 + * @param $goodsId + * @return bool + */ + public static function checkIsAllowDelete($goodsId) + { + $service = new static; + if ($service->checkSharpGoods($goodsId)) return false; + if ($service->checkBargainGoods($goodsId)) return false; + return true; + } + + /** + * 验证商品是否参与了秒杀商品 + * @param $goodsId + * @return bool + */ + private function checkSharpGoods($goodsId) + { + return SharpGoodsModel::isExistGoodsId($goodsId); + } + + /** + * 验证商品是否参与了砍价商品 + * @param $goodsId + * @return bool + */ + private function checkBargainGoods($goodsId) + { + return BargainGoodsModel::isExistGoodsId($goodsId); + } + +} \ No newline at end of file diff --git a/source/application/store/service/order/Export.php b/source/application/store/service/order/Export.php new file mode 100644 index 0000000..2d7f2fc --- /dev/null +++ b/source/application/store/service/order/Export.php @@ -0,0 +1,121 @@ + $this->filterValue($order['order_no']), + '商品信息' => $this->filterGoodsInfo($order), + '订单总额' => $this->filterValue($order['total_price']), + '优惠券抵扣' => $this->filterValue($order['coupon_money']), + '积分抵扣' => $this->filterValue($order['points_money']), + '运费金额' => $this->filterValue($order['express_price']), + '后台改价' => $this->filterValue("{$order['update_price']['symbol']}{$order['update_price']['value']}"), + '实付款金额' => $this->filterValue($order['pay_price']), + '支付方式' => $this->filterValue($order['pay_type']['text']), + '下单时间' => $this->filterValue($order['create_time']), + '买家' => $this->filterValue($order['user']['nickName']), + '买家留言' => $this->filterValue($order['buyer_remark']), + '配送方式' => $this->filterValue($order['delivery_type']['text']), + '自提门店名称' => !empty($order['extract_shop']) ? $this->filterValue($order['extract_shop']['shop_name']) : '', + '自提联系人' => !empty($order['extract']) ? $this->filterValue($order['extract']['linkman']) : '', + '自提联系电话' => !empty($order['extract']) ? $this->filterValue($order['extract']['phone']) : '', + '收货人姓名' => $this->filterValue($order['address']['name']), + '联系电话' => $this->filterValue($order['address']['phone']), + '收货人地址' => $this->filterValue($address ? $address->getFullAddress() : ''), + '物流公司' => $this->filterValue($order['express']['express_name']), + '物流单号' => $this->filterValue($order['express_no']), + '付款状态' => $this->filterValue($order['pay_status']['text']), + '付款时间' => $this->filterTime($order['pay_time']), + '发货状态' => $this->filterValue($order['delivery_status']['text']), + '发货时间' => $this->filterTime($order['delivery_time']), + '收货状态' => $this->filterValue($order['receipt_status']['text']), + '收货时间' => $this->filterTime($order['receipt_time']), + '订单状态' => $this->filterValue($order['order_status']['text']), + '微信支付交易号' => $this->filterValue($order['transaction_id']), + '是否已评价' => $this->filterValue($order['is_comment'] ? '是' : '否'), + ]; + } + // 导出csv文件 + $filename = 'order-' . date('YmdHis'); + return export_excel($filename . '.csv', $this->tileArray, $dataArray); + } + + /** + * 批量发货模板 + */ + public function deliveryTpl() + { + // 导出csv文件 + $filename = 'delivery-' . date('YmdHis'); + return export_excel($filename . '.csv', ['订单号', '物流单号']); + } + + /** + * 格式化商品信息 + * @param $order + * @return string + */ + private function filterGoodsInfo($order) + { + $content = ''; + foreach ($order['goods'] as $key => $goods) { + $content .= ($key + 1) . ".商品名称:{$goods['goods_name']}\n"; + !empty($goods['goods_attr']) && $content .= " 商品规格:{$goods['goods_attr']}\n"; + $content .= " 购买数量:{$goods['total_num']}\n"; + $content .= " 商品总价:{$goods['total_price']}元\n\n"; + } + return $content; + } + + /** + * 表格值过滤 + * @param $value + * @return string + */ + private function filterValue($value) + { + return "\t" . $value . "\t"; + } + + /** + * 日期值过滤 + * @param $value + * @return string + */ + private function filterTime($value) + { + if (!$value) return ''; + return $this->filterValue(date('Y-m-d H:i:s', $value)); + } + +} \ No newline at end of file diff --git a/source/application/store/service/statistics/Data.php b/source/application/store/service/statistics/Data.php new file mode 100644 index 0000000..93c451a --- /dev/null +++ b/source/application/store/service/statistics/Data.php @@ -0,0 +1,64 @@ +getSurveyData($startDate, $endDate); + } + + /** + * 近7日走势 + * @return array + * @throws \think\Exception + */ + public function getTransactionTrend() + { + return (new Trade7days)->getTransactionTrend(); + } + + /** + * 商品销售榜 + * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getGoodsRanking() + { + return (new GoodsRanking)->getGoodsRanking(); + } + + /** + * 用户消费榜 + * @return string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function geUserExpendRanking() + { + return (new UserExpendRanking)->getUserExpendRanking(); + } + +} \ No newline at end of file diff --git a/source/application/store/service/statistics/data/GoodsRanking.php b/source/application/store/service/statistics/data/GoodsRanking.php new file mode 100644 index 0000000..11d4e5b --- /dev/null +++ b/source/application/store/service/statistics/data/GoodsRanking.php @@ -0,0 +1,42 @@ +alias('o_goods') + ->field([ + 'goods_id', + 'goods_name', + 'SUM(total_pay_price) AS sales_volume', + 'SUM(total_num) AS total_sales_num' + ]) + ->join('order', 'order.order_id = o_goods.order_id') + ->where('order.pay_status', '=', OrderPayStatusEnum::SUCCESS) + ->where('order.order_status', '<>', OrderStatusEnum::CANCELLED) + ->group('goods_id, goods_name') + // order:此处按总销售额排序,如需按销量改为total_sales_num + ->order(['sales_volume' => 'DESC']) + ->limit(10) + ->select(); + } + +} \ No newline at end of file diff --git a/source/application/store/service/statistics/data/Survey.php b/source/application/store/service/statistics/data/Survey.php new file mode 100644 index 0000000..4217982 --- /dev/null +++ b/source/application/store/service/statistics/data/Survey.php @@ -0,0 +1,147 @@ + $this->getUserTotal($startDate, $endDate), + // 消费人数 + 'consume_users' => $this->getConsumeUsers($startDate, $endDate), + // 付款订单数 + 'order_total' => $this->getOrderTotal($startDate, $endDate), + // 付款订单总额 + 'order_total_money' => $this->getOrderTotalMoney($startDate, $endDate), + // 商品总量 + 'goods_total' => $this->getGoodsTotal($startDate, $endDate), + // 用户充值总额 + 'recharge_total' => $this->getRechargeTotal($startDate, $endDate), + ]; + } + + /** + * 获取用户总量 + * @param null $startDate + * @param null $endDate + * @return string + * @throws \think\Exception + */ + private function getUserTotal($startDate = null, $endDate = null) + { + $model = new UserModel; + if (!is_null($startDate) && !is_null($endDate)) { + $model->where('create_time', '>=', strtotime($startDate)) + ->where('create_time', '<', strtotime($endDate) + 86400); + } + $value = $model->where('is_delete', '=', '0')->count(); + return number_format($value); + } + + /** + * 消费人数 + * @param null $startDate + * @param null $endDate + * @return string + * @throws \think\Exception + */ + public function getConsumeUsers($startDate = null, $endDate = null) + { + $model = new OrderModel; + if (!is_null($startDate) && !is_null($endDate)) { + $model->where('pay_time', '>=', strtotime($startDate)) + ->where('pay_time', '<', strtotime($endDate) + 86400); + } + $value = $model->field('user_id') + ->where('pay_status', '=', PayStatusEnum::SUCCESS) + ->where('order_status', '<>', OrderStatusEnum::CANCELLED) + ->where('is_delete', '=', '0') + ->group('user_id') + ->count(); + return number_format($value); + } + + /** + * 获取订单总量 + * @param null $startDate + * @param null $endDate + * @return string + * @throws \think\Exception + */ + private function getOrderTotal($startDate = null, $endDate = null) + { + return number_format((new OrderModel)->getPayOrderTotal($startDate, $endDate)); + } + + /** + * 付款订单总额 + * @param null $startDate + * @param null $endDate + * @return string + */ + private function getOrderTotalMoney($startDate = null, $endDate = null) + { + return helper::number2((new OrderModel)->getOrderTotalPrice($startDate, $endDate)); + } + + /** + * 获取商品总量 + * @param null $startDate + * @param null $endDate + * @return int|string + * @throws \think\Exception + */ + private function getGoodsTotal($startDate = null, $endDate = null) + { + $model = new GoodsModel; + if (!is_null($startDate) && !is_null($endDate)) { + $model->where('create_time', '>=', strtotime($startDate)) + ->where('create_time', '<', strtotime($endDate) + 86400); + } + $value = $model->where('is_delete', '=', 0)->count(); + return number_format($value); + } + + /** + * 用户充值总额 + * @param null $startDate + * @param null $endDate + * @return float|int + */ + private function getRechargeTotal($startDate = null, $endDate = null) + { + $model = new RechargeOrderModel; + if (!is_null($startDate) && !is_null($endDate)) { + $model->where('pay_time', '>=', strtotime($startDate)) + ->where('pay_time', '<', strtotime($endDate) + 86400); + } + $value = $model->where('pay_status', '=', RechargePayStatusEnum::SUCCESS) + ->sum('actual_money'); + return helper::number2($value); + } + +} \ No newline at end of file diff --git a/source/application/store/service/statistics/data/Trade7days.php b/source/application/store/service/statistics/data/Trade7days.php new file mode 100644 index 0000000..9bc19b7 --- /dev/null +++ b/source/application/store/service/statistics/data/Trade7days.php @@ -0,0 +1,107 @@ +OrderModel = new OrderModel; + } + + /** + * 近7日走势 + * @return array + * @throws \think\Exception + */ + public function getTransactionTrend() + { + // 最近七天日期 + $lately7days = $this->getLately7days(); + return [ + 'date' => helper::jsonEncode($lately7days), + 'order_total' => helper::jsonEncode($this->getOrderTotalByDate($lately7days)), + 'order_total_price' => helper::jsonEncode($this->getOrderTotalPriceByDate($lately7days)) + ]; + } + + /** + * 最近七天日期 + */ + private function getLately7days() + { + // 获取当前周几 + $date = []; + for ($i = 0; $i < 7; $i++) { + $date[] = date('Y-m-d', strtotime('-' . $i . ' days')); + } + return array_reverse($date); + } + + /** + * 获取订单总量 (指定日期) + * @param $days + * @return array + * @throws \think\Exception + */ + private function getOrderTotalByDate($days) + { + $data = []; + foreach ($days as $day) { + $data[] = $this->getOrderTotal($day); + } + return $data; + } + + /** + * 获取订单总量 + * @param null $day + * @return string + * @throws \think\Exception + */ + private function getOrderTotal($day = null) + { + return number_format($this->OrderModel->getPayOrderTotal($day, $day)); + } + + /** + * 获取某天的总销售额 + * @param null $day + * @return string + */ + private function getOrderTotalPrice($day = null) + { + return helper::number2($this->OrderModel->getOrderTotalPrice($day, $day)); + } + + /** + * 获取订单总量 (指定日期) + * @param $days + * @return array + */ + private function getOrderTotalPriceByDate($days) + { + $data = []; + foreach ($days as $day) { + $data[] = $this->getOrderTotalPrice($day); + } + return $data; + } + +} \ No newline at end of file diff --git a/source/application/store/service/statistics/data/UserExpendRanking.php b/source/application/store/service/statistics/data/UserExpendRanking.php new file mode 100644 index 0000000..58e7521 --- /dev/null +++ b/source/application/store/service/statistics/data/UserExpendRanking.php @@ -0,0 +1,31 @@ +field(['user_id', 'nickName', 'expend_money']) + ->where('is_delete', '=', 0) + ->order(['expend_money' => 'DESC']) + ->limit(10) + ->select(); + } + +} \ No newline at end of file diff --git a/source/application/store/service/wxapp/Message.php b/source/application/store/service/wxapp/Message.php new file mode 100644 index 0000000..10c79f7 --- /dev/null +++ b/source/application/store/service/wxapp/Message.php @@ -0,0 +1,113 @@ +WxTplMsg = new WxTplMsg($config['app_id'], $config['app_secret']); + } + + /** + * @param $data + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + public function send($data) + { + // 用户id集 + $userIdsArr = !strstr($data['user_id'], self::SEPARATOR) ? [$data['user_id']] + : explode(self::SEPARATOR, $data['user_id']); + // 批量发送 + foreach ($userIdsArr as $userId) { + $this->sendTemplateMessage($userId, $data); + } + return true; + } + + /** + * 发送模板消息 + * @param $userId + * @param $data + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\exception\DbException + */ + private function sendTemplateMessage($userId, $data) + { + // 获取formid + if (!$formId = FormIdService::getAvailableFormId($userId)) { + $this->recordState("用户[ID:$userId] 无可用formid,无法发送模板消息!"); + return false; + } + // 获取用户信息 + $user = UserModel::detail($data['user_id']); + // 构建模板消息参数 + $params = [ + 'touser' => $user['open_id'], + 'template_id' => $data['template_id'], + 'page' => $data['page'], + 'form_id' => $formId['form_id'], + 'data' => [] + ]; + // 格式化模板内容 + foreach (array_filter($data['content']) as $key => $item) { + $params['data']['keyword' . ($key + 1)] = $item; + } + // 请求微信api:发送模板消息 + if ($status = $this->WxTplMsg->sendTemplateMessage($params)) { + $this->recordState("用户[ID:$userId] 发送成功!"); + } + // 标记formid已使用 + FormIdService::setIsUsed($formId['id']); + return $status; + } + + /** + * 获取状态集 + * @return array + */ + public function getStateSet() + { + return $this->stateSet; + } + + /** + * 记录状态集 + * @param $content + */ + private function recordState($content) + { + $this->stateSet[] = $content; + } + +} \ No newline at end of file diff --git a/source/application/store/view/apps/bargain/active/add.php b/source/application/store/view/apps/bargain/active/add.php new file mode 100644 index 0000000..2acd76c --- /dev/null +++ b/source/application/store/view/apps/bargain/active/add.php @@ -0,0 +1,226 @@ +
+
+
+
+
+
+
+
+
新增砍价活动
+
+ +
+ +
+
+ +
+
+
+
+ 注:砍价活动仅支持单规格商品 或 同价的多规格商品 +
+
+
+ +
+ +
+ +
+ + + +
+
+ 砍价活动的开始时间与截止时间 +
+
+
+ +
+ +
+ + 自用户发起砍价到砍价截止的时间,单位:小时 +
+
+ +
+ +
+ + 砍价商品的最低价格,单位:元 +
+
+ +
+ +
+ + 每个砍价订单的帮砍人数,达到该人数才可砍至底价 +
+
+ +
+ +
+ + +
+ 砍价发起人自己砍一刀 +
+
+
+ +
+ +
+ + +
+ 只有砍到底价才可以购买 +
+
+
+ +
+ +
+ + 注:前台展示的销量 = 初始销量 + 实际销量 +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + 数字越小越靠前 +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + diff --git a/source/application/store/view/apps/bargain/active/edit.php b/source/application/store/view/apps/bargain/active/edit.php new file mode 100644 index 0000000..7be6670 --- /dev/null +++ b/source/application/store/view/apps/bargain/active/edit.php @@ -0,0 +1,200 @@ +
+
+
+
+
+
+
+
+
编辑砍价活动
+
+ +
+ +
+
+
+ +
+
+

+
+
+
+
+ +
+ +
+ +
+ + + +
+
+ 砍价活动的开始时间与截止时间 +
+
+
+ +
+ +
+ + 自用户发起砍价到砍价截止的时间,单位:小时 +
+
+ +
+ +
+ + 砍价商品的最低价格,单位:元 +
+
+ +
+ +
+ + 每个砍价订单的帮砍人数,达到该人数才可砍至底价 +
+
+ +
+ +
+ + +
+ 砍价发起人自己砍一刀 +
+
+
+ +
+ +
+ + +
+ 只有砍到底价才可以购买 +
+
+
+ +
+ +
+ + 注:前台展示的销量 = 初始销量 + 实际销量 +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + 数字越小越靠前 +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/bargain/active/index.php b/source/application/store/view/apps/bargain/active/index.php new file mode 100644 index 0000000..834a1e1 --- /dev/null +++ b/source/application/store/view/apps/bargain/active/index.php @@ -0,0 +1,132 @@ +
+
+
+
+
+
砍价活动列表
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
活动ID商品信息活动时间砍价底价帮砍人数实际销量排序活动状态创建时间操作
+
+ +
+
+

+
+
+

开始时间:

+

结束时间:

+
+ + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/bargain/setting/index.php b/source/application/store/view/apps/bargain/setting/index.php new file mode 100644 index 0000000..28865f2 --- /dev/null +++ b/source/application/store/view/apps/bargain/setting/index.php @@ -0,0 +1,60 @@ +
+
+
+
+
+
+
+
+
砍价设置
+
+
+ +
+ + +
+ 注:砍价订单是否参与分销 +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/bargain/task/help.php b/source/application/store/view/apps/bargain/task/help.php new file mode 100644 index 0000000..73fa30b --- /dev/null +++ b/source/application/store/view/apps/bargain/task/help.php @@ -0,0 +1,65 @@ +
+
+
+
+
+
砍价助力榜
+
+
+
+ + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + +
ID用户头像用户昵称砍掉的金额操作时间
+ + + + + 发起人 + + -
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/bargain/task/index.php b/source/application/store/view/apps/bargain/task/index.php new file mode 100644 index 0000000..cfd9f2a --- /dev/null +++ b/source/application/store/view/apps/bargain/task/index.php @@ -0,0 +1,130 @@ +
+
+
+
+
+
砍价记录
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
ID商品信息用户信息砍价底价已砍金额截止时间是否购买砍价状态创建时间操作
+
+
+ +
+
+

+
+
+
+
+
+ +
+
+

+

(ID:)

+
+
+
+ + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/dealer/apply/index.php b/source/application/store/view/apps/dealer/apply/index.php new file mode 100644 index 0000000..7d246aa --- /dev/null +++ b/source/application/store/view/apps/dealer/apply/index.php @@ -0,0 +1,209 @@ +
+
+
+
+
+
申请成为分销商
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
用户ID微信头像微信昵称 +

姓名

+

手机号

+
推荐人审核状态审核方式申请时间操作
+ + + + +

+
+ +

+

+ +

--

+ +
+ 0): ?> +

+ + +

平台

+ +
+ + 待审核 + + 已通过 + + 已驳回 + + + + 后台审核 + + 无需审核 + + + +
暂无记录
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/source/application/store/view/apps/dealer/order/index.php b/source/application/store/view/apps/dealer/order/index.php new file mode 100644 index 0000000..87c5aa5 --- /dev/null +++ b/source/application/store/view/apps/dealer/order/index.php @@ -0,0 +1,187 @@ +
+
+
+
+
+
分销订单
+
+
+ +
+
+ + +
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list->toArray()['data'] as $order): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
商品信息单价/数量实付款买家交易状态佣金结算
+ + 订单号: +
+
+ +
+
+

+ +
+
+

+

×

+
+

+ +
+

+ +
+

付款状态: + + +

+

发货状态: + + +

+

收货状态: + + +

+
+ + 已结算 + + 未结算 + +
+
+ 0): ?> +
+

+ 一级分销商: + + (ID: ) +

+

+ 分销佣金: + +

+
+ + 0): ?> +
+

+ 二级分销商: + + (ID: ) +

+

+ 分销佣金: + +

+
+ + 0): ?> +
+

+ 三级分销商: + + (ID: ) +

+

+ 分销佣金: + +

+
+ +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/dealer/setting/index.php b/source/application/store/view/apps/dealer/setting/index.php new file mode 100644 index 0000000..1414dbe --- /dev/null +++ b/source/application/store/view/apps/dealer/setting/index.php @@ -0,0 +1,930 @@ +
+
+
+
+
+
+
+ +
+
+
+ +
+ + +
+
+
+ +
+ + + +
+
+
+ +
+ + +
+ 如开启,分销商自己购买商品,获得一级佣金 +
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+ 购买指定商品付款后自动成为分销商,无需后台审核 +
+
+ +
+ isEmpty()): foreach ($goodsList as $goods): ?> +
+ + + + + +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ + 佣金比例范围 0% - 100% +
+
+
+ +
+ + 佣金比例范围 0% - 100% +
+
+
+ +
+ + 佣金比例范围 0% - 100% +
+
+
+
+
+ +
+ + + +
+ 注:如使用微信支付,则需申请微信支付企业付款到零钱功能 +
+
+
+
+ +
+ +
+
+
+ +
+ +
+ 当订单完成n天后,该订单的分销佣金才会结算到分销商余额,如果设置为0天 则订单完成时就结算 +
+
+ 注:建议佣金结算天数大于允许发起售后申请天数,如果用户申请退款退货 则不结算佣金 +
+
+
+
+
+
+
分销中心页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
申请成为分销商页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
分销订单页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
我的团队页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
提现明细页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
申请提现页面
+
+
+ +
+ + + 默认: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
推广二维码
+
+
+ +
+ + + 默认: +
+
+
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+ + + + + +
+ +
+
+
+ 尺寸:宽750像素 高度不限 +
+
+
+
+
+ +
+
+
+ +
+ +
+ + + + + +
+ +
+
+
+ 尺寸:宽750像素 高度不限 +
+
+
+
+
+ +
+
+
+ +
+ +
+ + + + + +
+ +
+
+
+ 尺寸:宽750像素 高度不限 +
+
+
+
+
+
+
+ +
+ + 模板编号AT0674,关键词 (申请时间、审核状态、审核时间、备注信息) + + 如何获取模板消息ID? + +
+
+
+ +
+ + 模板编号AT0324,关键词 (提现时间、提现方式、提现金额、提现状态、备注) + + 如何获取模板消息ID? + +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/apps/dealer/setting/qrcode.php b/source/application/store/view/apps/dealer/setting/qrcode.php new file mode 100644 index 0000000..eac5056 --- /dev/null +++ b/source/application/store/view/apps/dealer/setting/qrcode.php @@ -0,0 +1,206 @@ +
+
+
+
+
+
+
分销海报设置
+
+
+
+

注:可拖动头像、二维码、昵称调整位置,如修改

+

注:修改后如需生效请前往 设置-清理缓存,清除临时图片 +

+
+
+
+
+
+ +
+ +
+
+ +
+
+ 这里是昵称 +
+
+
+ +
+
+ +
+ +
+
+
+ +
+
+ 尺寸:宽750像素 高大于(等于)1200像素 +
+
+
+
+ +
+ +
+ +
+
+
+ +
+ + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ + +{{include file="layouts/_template/file_library" /}} + + + diff --git a/source/application/store/view/apps/dealer/user/edit.php b/source/application/store/view/apps/dealer/user/edit.php new file mode 100644 index 0000000..67bb4e7 --- /dev/null +++ b/source/application/store/view/apps/dealer/user/edit.php @@ -0,0 +1,66 @@ +
+
+
+
+
+
+
+
+
编辑分销商
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/dealer/user/fans.php b/source/application/store/view/apps/dealer/user/fans.php new file mode 100644 index 0000000..6ca2501 --- /dev/null +++ b/source/application/store/view/apps/dealer/user/fans.php @@ -0,0 +1,64 @@ +
+
+
+
+
+
下级用户列表
+
+
+ +
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + +
用户ID微信头像微信昵称性别累积消费金额注册时间
+ + + + +

+
暂无记录
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/dealer/user/index.php b/source/application/store/view/apps/dealer/user/index.php new file mode 100644 index 0000000..43bd666 --- /dev/null +++ b/source/application/store/view/apps/dealer/user/index.php @@ -0,0 +1,205 @@ +
+
+
+
+
+
分销商列表
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
用户ID微信头像微信昵称 +

姓名

+

手机号

+
+

累计佣金

+

可提现佣金

+
推荐人下级用户成为时间操作
+ + + + +

+
+ +

+

+ +

--

+ +
+

+

+
+ 0): ?> +

+ + +

平台

+ +
+

+ 一级: +

+ = 2): ?> +

+ 二级: +

+ + +

+ 三级: +

+ +
+
+ + + 编辑 + + + +
+ + +
+ +
+
暂无记录
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/dealer/withdraw/index.php b/source/application/store/view/apps/dealer/withdraw/index.php new file mode 100644 index 0000000..d211a4b --- /dev/null +++ b/source/application/store/view/apps/dealer/withdraw/index.php @@ -0,0 +1,301 @@ +
+
+
+
+
+
分销商提现申请
+
+
+ +
+
+ + +
+
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
用户ID微信头像微信昵称 +

姓名

+

手机号

+
提现金额提现方式提现信息审核状态申请时间审核时间操作
+ + + + +

+
+ +

+

+ +

--

+ +
+

+
+

+
+ +

+

+ +

+

+

+ +

--

+ +
+ + 待审核 + + 审核通过 + +

已驳回

+ + + 查看原因 + + + 已打款 + +
+
+ + + + 审核 + + + + + 确认打款 + + + + + + 微信付款 + + + + + + --- + +
+
暂无记录
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/source/application/store/view/apps/sharing/active/index.php b/source/application/store/view/apps/sharing/active/index.php new file mode 100644 index 0000000..fdd0bb6 --- /dev/null +++ b/source/application/store/view/apps/sharing/active/index.php @@ -0,0 +1,111 @@ +
+
+
+
+
+
拼单记录
+
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
拼单ID商品图片商品名称成团人数已拼人员发起人(团长)拼单结束时间拼单状态发起时间操作
+ + 商品图片 + + +

+
+

+
+

+ +
+ +

+ +

+ +

+ + 还差 人 + +

+ + + + + + +
+ +
暂无记录
+
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/active/users.php b/source/application/store/view/apps/sharing/active/users.php new file mode 100644 index 0000000..c24a10e --- /dev/null +++ b/source/application/store/view/apps/sharing/active/users.php @@ -0,0 +1,90 @@ +
+
+
+
+
+
拼单成员列表
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
用户ID微信头像微信昵称拼团角色订单号订单金额收货人联系方式收货地址下单时间操作
+ + + + + + 团长 + + 团员 + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/category/add.php b/source/application/store/view/apps/sharing/category/add.php new file mode 100644 index 0000000..ad4954a --- /dev/null +++ b/source/application/store/view/apps/sharing/category/add.php @@ -0,0 +1,61 @@ +
+
+
+
+
+
+
+
+
添加商品分类
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + diff --git a/source/application/store/view/apps/sharing/category/edit.php b/source/application/store/view/apps/sharing/category/edit.php new file mode 100644 index 0000000..ce5137b --- /dev/null +++ b/source/application/store/view/apps/sharing/category/edit.php @@ -0,0 +1,61 @@ +
+
+
+
+
+
+
+
+
编辑商品分类
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + diff --git a/source/application/store/view/apps/sharing/category/index.php b/source/application/store/view/apps/sharing/category/index.php new file mode 100644 index 0000000..fa3be06 --- /dev/null +++ b/source/application/store/view/apps/sharing/category/index.php @@ -0,0 +1,128 @@ +
+
+
+
+
+
拼团商品分类
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
分类ID分类名称分类排序添加时间操作
+ +
 -- + +
   -- + +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/comment/detail.php b/source/application/store/view/apps/sharing/comment/detail.php new file mode 100644 index 0000000..debfc80 --- /dev/null +++ b/source/application/store/view/apps/sharing/comment/detail.php @@ -0,0 +1,163 @@ +
+
+
+
+
+
+
+
+
商品评价详情
+
+
+ +
+ + (用户id:) +
+
+
+ +
+ + 商品图片 + +
+
+
+ +
+ +
+
+
+ +
+ + + +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ $item): ?> +
+ + + + + +
+ +
+
+
+ 最多允许6张,可拖拽调整显示顺序 +
+
+
+
+ +
+ + 数字越小越靠前 +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + diff --git a/source/application/store/view/apps/sharing/comment/index.php b/source/application/store/view/apps/sharing/comment/index.php new file mode 100644 index 0000000..9f50f7e --- /dev/null +++ b/source/application/store/view/apps/sharing/comment/index.php @@ -0,0 +1,122 @@ +
+
+
+
+
+
评价列表
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
ID用户商品图片商品名称评分评价内容是否有图片显示状态评价排序评价时间操作
+

+ +
+ + 商品图片 + + +

+
+ + 好评 + + 中评 + + 差评 + + +

+
+ + + + + + + + 显示 + + 隐藏 + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/goods/add.php b/source/application/store/view/apps/sharing/goods/add.php new file mode 100644 index 0000000..e8ac2be --- /dev/null +++ b/source/application/store/view/apps/sharing/goods/add.php @@ -0,0 +1,704 @@ + + +
+
+
+
+
+
+
+
+
基本信息
+
+
+ +
+ +
+
+
+ +
+ + + + 去添加 + + +
+
+
+ +
+
+
+ +
+
+
+
+ 尺寸750x750像素以上,大小2M以下 (可拖拽图片调整显示顺序 ) +
+
+
+
+
+ +
+ + 选填,商品卖点简述,例如:此款商品美观大方 性价比较高 不容错过 +
+
+ +
+
拼团设置
+
+
+ +
+ + +
+ 是否允许用户选择不拼团单独购买,如果允许单买,请务必设置好单买价 +
+
+
+
+ +
+ + 拼团成员的总人数,最低2人 +
+
+
+ +
+ + 注:开团后的有效时间,单位:小时,超过时长则拼团失败 +
+
+ +
+
规格/库存
+
+
+ +
+ + +
+
+ + +
+
+ +
+
+
+ {{ item.group_name }} + +
+
+
+ {{ val.spec_value }} + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}规格图片商家编码 + 拼团价 + + 单买价 + 划线价 + 库存 + + 重量(kg) +
+ {{ td.spec_value }} + +
+ + +
+
+ +
+
+ + + + + + + + + + + +
+
+ 注:如不允许单买,单买价设置为0即可 +
+
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + +
+
+ +
+
商品详情
+
+
+ +
+ + +
+
+
+
其他设置
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+ + +
+
积分设置
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+
+ 注:如需使用积分功能必须在 [营销管理 - 积分设置] 中开启 +
+
+
+ + +
+
会员折扣设置
+
+
+ +
+ + +
+ 如果不开启会员折扣,该商品则不享受会员等级折扣价 +
+
+
+
+
+ +
+ + +
+ 默认折扣:默认为用户所属会员等级的折扣率 +
+
+
+
+
+ +
+ + +
+ + : + + + +
+ +
+ 注:折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+
+ + +
+
分销设置
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ +
+
+ 一级佣金: + + % +
+
+ 二级佣金: + + % +
+
+ 三级佣金: + + % +
+
+

+ 注:如需使用分销功能必须在 [分销中心 - 分销设置] 中开启 +

+

+ 注:如不开启单独分销则默认使用全局分销比例 +

+
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/apps/sharing/goods/copy_master.php b/source/application/store/view/apps/sharing/goods/copy_master.php new file mode 100644 index 0000000..f7d2f45 --- /dev/null +++ b/source/application/store/view/apps/sharing/goods/copy_master.php @@ -0,0 +1,744 @@ + + +
+
+
+
+
+
+
+
+
基本信息
+
+
+ +
+ +
+
+
+ +
+ + + + 去添加 + + +
+
+
+ +
+
+ +
+ $item): ?> +
+ + + + + +
+ +
+
+
+ 尺寸750x750像素以上,大小2M以下 (可拖拽图片调整显示顺序 ) +
+
+
+
+ +
+ + 选填,商品卖点简述,例如:此款商品美观大方 性价比较高 不容错过 +
+
+ +
+
拼团设置
+
+
+ +
+ + +
+ 是否允许用户选择不拼团单独购买,如果允许单买,请务必设置好单买价 +
+
+
+
+ +
+ + 拼团成员的总人数,最低2人 +
+
+
+ +
+ + 注:开团后的有效时间,单位:小时,超过时长则拼团失败 +
+
+ +
+
规格/库存
+
+
+ +
+ + +
+
+ + +
+
+ +
+
+
+ {{ item.group_name }} + +
+
+
+ {{ val.spec_value }} + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}规格图片商家编码 + 拼团价 + + 单买价 + 划线价 + 库存 + + 重量(kg) +
+ {{ td.spec_value }} + +
+ + +
+
+ +
+
+ + + + + + + + + + + +
+
+ 注:如不允许单买,单买价设置为0即可 +
+
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + +
+
+ +
+
商品详情
+
+
+ +
+ + +
+
+
+
其他设置
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+ + +
+
积分设置
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+
+ 注:如需使用积分功能必须在 [营销管理 - 积分设置] 中开启 +
+
+
+ + +
+
会员折扣设置
+
+
+ +
+ + +
+ 如果不开启会员折扣,该商品则不享受会员等级折扣价 +
+
+
+
+
+ +
+ + +
+ 默认折扣:默认为用户所属会员等级的折扣率 +
+
+
+
+
+ +
+ + +
+ + : + + + +
+ +
+ 注:折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+
+ + +
+
分销设置
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ + '%', 20 => '元']; + ?> +
+
+ 一级佣金: + + + + +
+
+ 二级佣金: + + + + +
+
+ 三级佣金: + + + + +
+
+

+ 注:如需使用分销功能必须在 [分销中心 - 分销设置] 中开启 +

+

+ 注:如不开启单独分销则默认使用全局分销比例 +

+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/apps/sharing/goods/edit.php b/source/application/store/view/apps/sharing/goods/edit.php new file mode 100644 index 0000000..a8dd3c5 --- /dev/null +++ b/source/application/store/view/apps/sharing/goods/edit.php @@ -0,0 +1,749 @@ + + +
+
+
+
+
+
+
+
+
基本信息
+
+
+ +
+ +
+
+
+ +
+ + + + 去添加 + + +
+
+
+ +
+
+ +
+ $item): ?> +
+ + + + + +
+ +
+
+
+ 尺寸750x750像素以上,大小2M以下 (可拖拽图片调整显示顺序 ) +
+
+
+
+ +
+ + 选填,商品卖点简述,例如:此款商品美观大方 性价比较高 不容错过 +
+
+ +
+
拼团设置
+
+
+ +
+ + +
+ 是否允许用户选择不拼团单独购买,如果允许单买,请务必设置好单买价 +
+
+
+
+ +
+ + 拼团成员的总人数,最低2人 +
+
+
+ +
+ + 注:开团后的有效时间,单位:小时,超过时长则拼团失败 +
+
+ +
+
规格/库存
+
+
+ +
+ + +
+
+ + +
+
+ +
+
+
+ {{ item.group_name }} + +
+
+
+ {{ val.spec_value }} + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}规格图片商家编码 + 拼团价 + + 单买价 + 划线价 + 库存 + + 重量(kg) +
+ {{ td.spec_value }} + +
+ + +
+
+ +
+
+ + + + + + + + + + + +
+
+ 注:如不允许单买,单买价设置为0即可 +
+
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + +
+
+ +
+
商品详情
+
+
+ +
+ + +
+
+
+
其他设置
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+ + +
+
积分设置
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+
+ 注:如需使用积分功能必须在 [营销管理 - 积分设置] 中开启 +
+
+
+ + +
+
会员折扣设置
+
+
+ +
+ + +
+ 如果不开启会员折扣,该商品则不享受会员等级折扣价 +
+
+
+
+
+ +
+ + +
+ 默认折扣:默认为用户所属会员等级的折扣率 +
+
+
+
+
+ +
+ + +
+ + : + + + +
+ +
+ 注:折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+
+ + +
+
分销设置
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ + '%', 20 => '元']; + ?> +
+
+ 一级佣金: + + + + +
+
+ 二级佣金: + + + + +
+
+ 三级佣金: + + + + +
+
+

+ 注:如需使用分销功能必须在 [分销中心 - 分销设置] 中开启 +

+

+ 注:如不开启单独分销则默认使用全局分销比例 +

+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/apps/sharing/goods/index.php b/source/application/store/view/apps/sharing/goods/index.php new file mode 100644 index 0000000..1d8640a --- /dev/null +++ b/source/application/store/view/apps/sharing/goods/index.php @@ -0,0 +1,253 @@ +
+
+
+
+
+
拼团商品列表
+
+
+ +
+
+ + +
+
+
+ get('category_id') ?: null; ?> + +
+
+ get('status') ?: null; ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
商品ID商品图片商品名称商品分类成团人数成团有效时长实际销量商品排序商品状态添加时间操作
+ + 商品图片 + + +

+
小时 + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + + + + diff --git a/source/application/store/view/apps/sharing/order/detail.php b/source/application/store/view/apps/sharing/order/detail.php new file mode 100644 index 0000000..1e29e65 --- /dev/null +++ b/source/application/store/view/apps/sharing/order/detail.php @@ -0,0 +1,700 @@ + +
+
+
+
+
+ + +
+ +
    +
  • + 下单时间 +
    +
  • +
  • + 付款 + +
    + 付款于 +
    + +
  • +
  • + 发货 + +
    + 发货于 +
    + +
  • +
  • + 收货 + +
    + 收货于 +
    + +
  • +
  • + 完成 + +
    + 完成于 +
    + +
  • +
+
+ + +
+
基本信息
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
订单号买家订单类型订单金额支付方式配送方式交易状态操作
+

+ +
+ + + + + + + + + + +
+
    +
  • 订单总额:
  • +
  • +
+ 0) : ?> +
    +
  • 优惠券抵扣:
  • +
  • - ¥
  • +
+ + 0) : ?> +
    +
  • 积分抵扣:
  • +
  • - ¥
  • +
+ +
    +
  • 运费金额:
  • +
  • + ¥
  • +
+ +
    +
  • 后台改价:
  • +
  • + ¥
  • +
+ +
    +
  • 实付款金额:
  • +
  • + ¥
  • +
+
+
+ + + + +

付款状态: + + +

+ +

拼单状态: + + + + + + + +

+ + +

退款状态: + + 待退款 + + 已退款 + +

+ + + + +

发货状态: + + +

+

收货状态: + + +

+ + +

订单状态: + +

+ +
+ +

+ 修改价格 +

+ +
+
+ + +
+
商品信息
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
商品名称商品编码重量(Kg)单价购买数量商品总价
+
+ +
+
+

+ +
+
+

+ +

+ +

+ 会员折扣价: +

+ +
×
+ 买家留言: + 总计金额:¥ +
+
+ + + +
+
收货信息
+
+
+ + + + + + + + + + + + + +
收货人收货电话收货地址
+ + + + +
+
+ + + + + +
+
自提信息
+
+
+

联系人:

+

联系电话:

+
+ +
+
自提门店信息
+
+
+ + + + + + + + + + + + + + + + + + + +
门店ID门店logo门店名称联系人联系电话门店地址
+ + + + + + + + +
+
+ + + + +
+
付款信息
+
+
+ + + + + + + + + + + + + + + + + +
应付款金额支付方式支付流水号付款状态付款时间
+ + + + +
+
+ + + + + +
+
用户取消订单
+
+
+
+

当前买家已付款并申请取消订单,请审核是否同意,如同意则自动退回付款金额(微信支付原路退款)并关闭订单。

+
+
+ +
+
+ +
+
+ + +
+ +
+
+
+
+ + +
+
+
+ + + + + +
+
发货信息
+
+ + + + +
+
+ +
+ +
+ 可在 物流公司列表 + 中设置 + +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + +
物流公司物流单号发货状态发货时间
+ + + + +
+
+ + + + + +
+
门店自提核销
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + +
自提门店名称核销员核销状态核销时间
+

+ +
+

+ +
+ + 已核销 + + +
+
+ + + + + + +
+
拼团失败手动退款
+
+
+
+

当前拼团已失败,可选择手动退款并关闭订单。

+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + +
+
+ +
+
+
+ + + + + diff --git a/source/application/store/view/apps/sharing/order/index.php b/source/application/store/view/apps/sharing/order/index.php new file mode 100644 index 0000000..bcbc343 --- /dev/null +++ b/source/application/store/view/apps/sharing/order/index.php @@ -0,0 +1,359 @@ + +
+
+
+
+
+
拼团订单列表
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $order): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
商品信息订单类型单价/数量实付款买家支付方式配送方式交易状态操作
+ + 订单号: +
+
+ +
+
+

+ +
+
+ + + + + + + + + + +

+

×

+
+

+ +
+

+ +
+ + + + +

付款状态: + + +

+ +

拼单状态: + + + + + + + +

+ + +

退款状态: + + 待退款 + + 已退款 + +

+ + + + +

发货状态: + + +

+

收货状态: + + +

+ + +

订单状态: + +

+ +
+
+ + 0 + ): ?> + + + 拼团信息 + + + + + + 订单详情 + + + + + 去发货 + + + + + + 去审核 + + + + + + 去退款 + + +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/order/operate/batchDelivery.php b/source/application/store/view/apps/sharing/order/operate/batchDelivery.php new file mode 100644 index 0000000..f8ad473 --- /dev/null +++ b/source/application/store/view/apps/sharing/order/operate/batchDelivery.php @@ -0,0 +1,80 @@ +
+
+
+
+
+
+
+
+
批量发货
+
+
+ +
+
+ + +
+
+ +
+
+
+ +
+ +
+ 可在 物流公司列表 + 中设置 + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/sharing/order/refund/detail.php b/source/application/store/view/apps/sharing/order/refund/detail.php new file mode 100644 index 0000000..f351f58 --- /dev/null +++ b/source/application/store/view/apps/sharing/order/refund/detail.php @@ -0,0 +1,358 @@ +
+
+
+
+
+
+
售后单信息
+
+
+ + + + + + + + + + + + + + + + + +
订单号买家售后类型处理状态操作
+

+ +
+ + + + +

+ 商家审核: + + + + + + + +

+ + +

+ 用户发货: + + 待发货 + + 已发货 + +

+ + + +

商家收货: 待收货

+ + + + + + +
+ + 订单详情 + +
+
+ +
+
售后商品信息
+
+
+ + + + + + + + + + + + + + + + + + + +
商品名称商品编码重量(Kg)单价购买数量付款价
+
+ +
+
+

+ +
+
× +
+
+ +
+
用户申请原因
+
+
+
+ +
+
+
+ +
+ + + +
+ +
+
+
+ + + + +
+
商家审核
+
+ +
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + 如审核状态为拒绝,则需要输入拒绝原因 +
+
+
+
+ +
+
+
+ + + + + +
+
退货地址
+
+
+ + + + + + + + + + + + + +
收货人收货电话收货地址
+
+ + + + +
+
商家拒绝原因
+
+
+
+ +
+
+ + + + +
+
用户发货信息
+
+
+ + + + + + + + + + + + + + + + + +
物流公司物流单号用户发货状态发货时间商家收货状态
+ 已发货 + + + 已收货 + + 待收货 + +
+
+ + + + + +
+
确认收货并退款
+
+
+
+

注:该操作将执行订单原路退款 并关闭当前售后单,请确认并填写退款的金额(不能大于订单实付款)

+ +

+ 注:当前订单存在后台改价记录,退款金额请参考订单实付款金额

+ +
+
+
+
+ +
+ +
+
+
+ +
+ + + 请输入退款金额,最多 + 元 + +
+
+
+
+ +
+
+
+ + + +
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/order/refund/index.php b/source/application/store/view/apps/sharing/order/refund/index.php new file mode 100644 index 0000000..6d23bf2 --- /dev/null +++ b/source/application/store/view/apps/sharing/order/refund/index.php @@ -0,0 +1,210 @@ +
+
+
+
+
+
售后列表
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + + + +
商品信息单价/数量付款价买家售后类型处理状态操作
+ 售后申请时间: + 订单号: +
+
+ +
+
+

+ +
+
+

+

×

+
+

+
+

+ +
+ + + + +

+ 商家审核: + + + + + + + +

+ + +

+ 用户发货: + + 待发货 + + 已发货 + +

+ + + +

商家收货: 待收货

+ + + + + + +
+ $item['order_refund_id']]); ?> +
+ + 售后详情 + + + + 去审核 + + + + + 确认收货 + + +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharing/setting/index.php b/source/application/store/view/apps/sharing/setting/index.php new file mode 100644 index 0000000..f02c3fd --- /dev/null +++ b/source/application/store/view/apps/sharing/setting/index.php @@ -0,0 +1,129 @@ +
+
+
+
+
+
+
+
+
拼团设置
+
+
+ +
+ + +
+ 注:如果不开启自动退款,则需要在 [订单管理] 处手动退款 +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+ 注:拼团订单是否参与分销 +
+
+
+ +
+
规则描述
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
模板消息
+
+
+ +
+ + 模板编号AT1814,关键词 (订单编号、商品名称、拼团价格、拼团人数、拼团时间、拼团结果) + + 如何获取模板消息ID? + +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/sharp/active/add.php b/source/application/store/view/apps/sharp/active/add.php new file mode 100644 index 0000000..4108790 --- /dev/null +++ b/source/application/store/view/apps/sharp/active/add.php @@ -0,0 +1,173 @@ +
+
+
+
+
+
+
+
+
新增活动会场
+
+
+ +
+ +
+ 注:活动日期保存后不能更改 +
+
+
+
+ +
+ + + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
商品ID商品图片商品名称操作
+ + {{ item.goods_id }} + + + 商品图片 + + +

{{ item.goods_name }}

+
+ 删除 +
+
+
+
+ 注:每个活动场次中出售的秒杀商品,此处非必填,可在场次管理中单独设置 +
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + diff --git a/source/application/store/view/apps/sharp/active/index.php b/source/application/store/view/apps/sharp/active/index.php new file mode 100644 index 0000000..6cad6c7 --- /dev/null +++ b/source/application/store/view/apps/sharp/active/index.php @@ -0,0 +1,122 @@ +
+
+
+
+
+
活动会场列表
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + +
活动ID活动日期场次数量活动状态创建时间操作
+ + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharp/active_time/add.php b/source/application/store/view/apps/sharp/active_time/add.php new file mode 100644 index 0000000..62e7b9f --- /dev/null +++ b/source/application/store/view/apps/sharp/active_time/add.php @@ -0,0 +1,156 @@ +
+
+
+
+
+
+
+
+
新增活动场次
+
+
+ +
+
+
+
+
+ +
+ + + +
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
商品ID商品图片商品名称操作
+ + {{ item.goods_id }} + + + 商品图片 + + +

{{ item.goods_name }}

+
+ 删除 +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + diff --git a/source/application/store/view/apps/sharp/active_time/edit.php b/source/application/store/view/apps/sharp/active_time/edit.php new file mode 100644 index 0000000..cb94afe --- /dev/null +++ b/source/application/store/view/apps/sharp/active_time/edit.php @@ -0,0 +1,148 @@ +
+
+
+
+
+
+
+
+
编辑活动场次
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
商品ID商品图片商品名称操作
+ + {{ item.goods_id }} + + + 商品图片 + + +

{{ item.goods_name }}

+
+ 删除 +
+
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + diff --git a/source/application/store/view/apps/sharp/active_time/index.php b/source/application/store/view/apps/sharp/active_time/index.php new file mode 100644 index 0000000..3487468 --- /dev/null +++ b/source/application/store/view/apps/sharp/active_time/index.php @@ -0,0 +1,125 @@ +
+
+
+
+
+
活动会场-场次列表
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
场次ID场次时间活动日期商品数量场次状态创建时间操作
+ + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharp/goods/edit.php b/source/application/store/view/apps/sharp/goods/edit.php new file mode 100644 index 0000000..897d296 --- /dev/null +++ b/source/application/store/view/apps/sharp/goods/edit.php @@ -0,0 +1,247 @@ + +
+
+
+
+
+
+
+
+
编辑秒杀商品
+
+
+ +
+
+
+ +
+
+

+

ID:

+
+
+
+
+ + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ 注:秒杀库存为独立库存,与主商品库存不同步 +
+
+
+
+ + + + +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}商家编码商品售价商品库存 + 秒杀价格 + + 秒杀库存 +
+ {{ td.spec_value }} + {{ item.form.goods_no ? item.form.goods_no : '--' }}{{ item.form.goods_price }}{{ item.form.stock_num }} + + + +
+
+ 注:秒杀库存为独立库存,与主商品库存不同步 +
+
+
+
+
+ + +
+ +
+ + + + + + + +
+ 注:秒杀商品默认为下单减库存,可在 基础设置 + 中配置未支付订单自动关闭释放库存 + +
+
+
+ + +
+ +
+ + 注:每人限制购买的数量,如果填写0则不限购 +
+
+
+ +
+ + +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + diff --git a/source/application/store/view/apps/sharp/goods/index.php b/source/application/store/view/apps/sharp/goods/index.php new file mode 100644 index 0000000..ce75f31 --- /dev/null +++ b/source/application/store/view/apps/sharp/goods/index.php @@ -0,0 +1,127 @@ +
+
+
+
+
+
秒杀商品列表
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
秒杀商品ID商品信息限购数量累积销量库存总量排序状态创建时间操作
+
+ +
+
+

+
+
+ + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/sharp/goods/step1.php b/source/application/store/view/apps/sharp/goods/step1.php new file mode 100644 index 0000000..85219bd --- /dev/null +++ b/source/application/store/view/apps/sharp/goods/step1.php @@ -0,0 +1,73 @@ +
+
+
+
+
+ + +
+
+
+
第一步:选择商品
+
+
+ +
+
+ +
+
+
+
+ 注:添加秒杀商品后,将不允许修改主商品的规格属性 +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/source/application/store/view/apps/sharp/goods/step2.php b/source/application/store/view/apps/sharp/goods/step2.php new file mode 100644 index 0000000..294d54c --- /dev/null +++ b/source/application/store/view/apps/sharp/goods/step2.php @@ -0,0 +1,236 @@ + +
+
+
+
+
+
+
+
+
第二步:填写商品信息
+
+
+ +
+
+
+ +
+
+

+

ID:

+
+
+
+
+ + + +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+ 注:秒杀库存为独立库存,与主商品库存不同步 +
+
+
+
+ + + + +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}商家编码商品售价商品库存 + 秒杀价格 + + 秒杀库存 +
+ {{ td.spec_value }} + {{ item.form.goods_no ? item.form.goods_no : '--' }}{{ item.form.goods_price }}{{ item.form.stock_num }} + + + +
+
+ 注:秒杀库存为独立库存,与主商品库存不同步 +
+
+
+
+
+ + +
+ +
+ + +
+
+ +
+ +
+ + 注:每人限制购买的数量,如果填写0则不限购 +
+
+
+ +
+ + +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + + diff --git a/source/application/store/view/apps/sharp/setting/index.php b/source/application/store/view/apps/sharp/setting/index.php new file mode 100644 index 0000000..0c2bc4f --- /dev/null +++ b/source/application/store/view/apps/sharp/setting/index.php @@ -0,0 +1,67 @@ +
+
+
+
+
+
+
+
+
整点秒杀设置
+
+
+ +
+
+ +
+ +
+ 秒杀订单下单未付款,n分钟后自动关闭,设置0则不自动关闭 +
+
+
+
+ +
+ + +
+ 注:整点秒杀订单是否参与分销 +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/wow/order/index.php b/source/application/store/view/apps/wow/order/index.php new file mode 100644 index 0000000..4b78b7e --- /dev/null +++ b/source/application/store/view/apps/wow/order/index.php @@ -0,0 +1,122 @@ + +
+
+
+
+
+
订单同步记录
+
+
+
+
+

注:用户下单(付款)后,自动同步到微信好物圈订单信息。

+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
ID用户头像用户昵称订单编号付款金额订单状态最后同步时间创建时间操作
+ + 用户头像 + + +

+ +
+ + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/apps/wow/setting/index.php b/source/application/store/view/apps/wow/setting/index.php new file mode 100644 index 0000000..c8f9db8 --- /dev/null +++ b/source/application/store/view/apps/wow/setting/index.php @@ -0,0 +1,72 @@ +
+
+
+
+
+
+
+
+
好物圈设置
+
+
+ +
+ + +
+ 注:用户将商品加入购物车时,自动同步到微信好物圈商品收藏 +
+
+
+
+ +
+ + +
+ 注:用户下单(付款)后,自动同步到微信好物圈订单信息。 +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/apps/wow/shoping/index.php b/source/application/store/view/apps/wow/shoping/index.php new file mode 100644 index 0000000..e086441 --- /dev/null +++ b/source/application/store/view/apps/wow/shoping/index.php @@ -0,0 +1,114 @@ +
+
+
+
+
+
商品收藏记录
+
+
+
+
+

注:用户将商品加入购物车时,自动同步到微信好物圈商品收藏。

+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
ID用户头像用户昵称商品图片商品名称收藏时间操作
+ + 用户头像 + + +

+ +
+ + 商品图片 + + +

+
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/article/add.php b/source/application/store/view/content/article/add.php new file mode 100644 index 0000000..239c7d4 --- /dev/null +++ b/source/application/store/view/content/article/add.php @@ -0,0 +1,159 @@ + +
+
+
+
+
+
+
+
+
添加文章
+
+
+ +
+ +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+ 小图模式建议封面图尺寸:300 * 188,大图模式建议封面图尺寸:750 * 455 +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + 显示的阅读量 = 实际阅读量 + 虚拟阅读量 +
+
+
+ +
+ + +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + +{{include file="layouts/_template/file_library" /}} + + + + diff --git a/source/application/store/view/content/article/category/add.php b/source/application/store/view/content/article/category/add.php new file mode 100644 index 0000000..caf8df3 --- /dev/null +++ b/source/application/store/view/content/article/category/add.php @@ -0,0 +1,50 @@ +
+
+
+
+
+
+
+
+
添加文章分类
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/article/category/edit.php b/source/application/store/view/content/article/category/edit.php new file mode 100644 index 0000000..638b91b --- /dev/null +++ b/source/application/store/view/content/article/category/edit.php @@ -0,0 +1,50 @@ +
+
+
+
+
+
+
+
+
编辑文章分类
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/article/category/index.php b/source/application/store/view/content/article/category/index.php new file mode 100644 index 0000000..ab216ad --- /dev/null +++ b/source/application/store/view/content/article/category/index.php @@ -0,0 +1,79 @@ +
+
+
+
+
+
文章分类
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
分类ID分类名称分类排序添加时间操作
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/article/edit.php b/source/application/store/view/content/article/edit.php new file mode 100644 index 0000000..e5db4cc --- /dev/null +++ b/source/application/store/view/content/article/edit.php @@ -0,0 +1,172 @@ + +
+
+
+
+
+
+
+
+
编辑文章
+
+
+ +
+ +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+ 小图模式建议封面图尺寸:300 * 188,大图模式建议封面图尺寸:750 * 455 +
+
+
+
+ +
+
+
+ +
+
+ + + + + +
+
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + 显示的阅读量 = 实际阅读量 + 虚拟阅读量 +
+
+
+ +
+ + +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + +{{include file="layouts/_template/file_library" /}} + + + + diff --git a/source/application/store/view/content/article/index.php b/source/application/store/view/content/article/index.php new file mode 100644 index 0000000..e764770 --- /dev/null +++ b/source/application/store/view/content/article/index.php @@ -0,0 +1,109 @@ +
+
+
+
+
+
文章列表
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
文章ID文章标题封面图文章分类实际阅读量文章排序状态添加时间更新时间操作
+

+
+ + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/files/group/add.php b/source/application/store/view/content/files/group/add.php new file mode 100644 index 0000000..ec7dce0 --- /dev/null +++ b/source/application/store/view/content/files/group/add.php @@ -0,0 +1,71 @@ +
+
+
+
+
+
+
+
+
添加文件分组
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + diff --git a/source/application/store/view/content/files/group/edit.php b/source/application/store/view/content/files/group/edit.php new file mode 100644 index 0000000..ac9174a --- /dev/null +++ b/source/application/store/view/content/files/group/edit.php @@ -0,0 +1,60 @@ +
+
+
+
+
+
+
+
+
添加文件分组
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/files/group/index.php b/source/application/store/view/content/files/group/index.php new file mode 100644 index 0000000..2fd8296 --- /dev/null +++ b/source/application/store/view/content/files/group/index.php @@ -0,0 +1,81 @@ +
+
+
+
+
+
文件分组
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $first): ?> + + + + + + + + + + + + + + +
分组ID分组名称文件类型分组排序添加时间操作
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/files/index.php b/source/application/store/view/content/files/index.php new file mode 100644 index 0000000..b654a70 --- /dev/null +++ b/source/application/store/view/content/files/index.php @@ -0,0 +1,87 @@ +
+
+
+
+
+
文件列表
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
文件ID文件名称所属分组存储方式存储域名文件大小文件类型文件后缀上传时间操作
+ + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/content/files/recycle.php b/source/application/store/view/content/files/recycle.php new file mode 100644 index 0000000..8d87e26 --- /dev/null +++ b/source/application/store/view/content/files/recycle.php @@ -0,0 +1,96 @@ +
+
+
+
+
+
文件列表
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + +
文件ID文件名称所属分组存储方式存储域名文件大小文件类型文件后缀上传时间操作
+ + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/data/bargain/goods/list.php b/source/application/store/view/data/bargain/goods/list.php new file mode 100644 index 0000000..599198f --- /dev/null +++ b/source/application/store/view/data/bargain/goods/list.php @@ -0,0 +1,136 @@ + + + + + + + + + + + 砍价活动列表 + + + +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
+ + 活动ID商品信息活动时间砍价底价帮砍人数创建时间
+ + +
+ +
+
+

+
+
+

开始时间:

+

结束时间:

+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/data/goods/list.php b/source/application/store/view/data/goods/list.php new file mode 100644 index 0000000..ed7e662 --- /dev/null +++ b/source/application/store/view/data/goods/list.php @@ -0,0 +1,165 @@ + + + + + + + + + + + 商品列表 + + + +
+
+ +
+
+ get('category_id') ?: null; ?> + +
+
+ get('status') ?: null; ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + +
+ + 商品ID商品图片商品名称商品分类添加时间
+ + + + 商品图片 + + +

+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/data/sharing/goods/list.php b/source/application/store/view/data/sharing/goods/list.php new file mode 100644 index 0000000..d2c6e0d --- /dev/null +++ b/source/application/store/view/data/sharing/goods/list.php @@ -0,0 +1,168 @@ + + + + + + + + + + + 拼团商品列表 + + + +
+
+ +
+
+ get('category_id') ?: null; ?> + +
+
+ get('status') ?: null; ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
+ + 商品ID商品图片商品名称商品分类成团人数成团有效时长添加时间
+ + + + 商品图片 + + +

+
小时
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/data/sharp/goods/list.php b/source/application/store/view/data/sharp/goods/list.php new file mode 100644 index 0000000..305d319 --- /dev/null +++ b/source/application/store/view/data/sharp/goods/list.php @@ -0,0 +1,133 @@ + + + + + + + + + + + 秒杀商品列表 + + + +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
+ + 商品ID商品信息限购数量累积销量商品库存总量创建时间
+ + +
+ +
+
+

+
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/data/shop/list.php b/source/application/store/view/data/shop/list.php new file mode 100644 index 0000000..ccf2c46 --- /dev/null +++ b/source/application/store/view/data/shop/list.php @@ -0,0 +1,117 @@ + + + + + + + + + + + 门店列表 + + +
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + +
+ + 门店ID门店logo门店名称门店地址自提核销
+ + + + + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/data/user/list.php b/source/application/store/view/data/user/list.php new file mode 100644 index 0000000..7d1424e --- /dev/null +++ b/source/application/store/view/data/user/list.php @@ -0,0 +1,168 @@ + + + + + + + + + + + 用户列表 + + + +
+
+ +
+
+
+ get('grade'); ?> + +
+
+ get('gender'); ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
+ + 微信头像微信昵称用户余额会员等级累积消费金额性别注册时间
+ + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+ + + + + diff --git a/source/application/store/view/goods/_template/spec_many.php b/source/application/store/view/goods/_template/spec_many.php new file mode 100644 index 0000000..f8a4bcc --- /dev/null +++ b/source/application/store/view/goods/_template/spec_many.php @@ -0,0 +1,72 @@ + + + + + diff --git a/source/application/store/view/goods/add.php b/source/application/store/view/goods/add.php new file mode 100644 index 0000000..631be8d --- /dev/null +++ b/source/application/store/view/goods/add.php @@ -0,0 +1,643 @@ + + +
+
+
+
+
+
+
+
+
基本信息
+
+
+ +
+ +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+
+
+ +
+
+
+
+ 尺寸750x750像素以上,大小2M以下 (可拖拽图片调整显示顺序 ) +
+
+
+
+
+ +
+ + 选填,商品卖点简述,例如:此款商品美观大方 性价比较高 不容错过 +
+
+ +
+
规格/库存
+
+
+ +
+ + +
+
+ + +
+
+ +
+
+
+ {{ item.group_name }} + +
+
+
+ {{ val.spec_value }} + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}规格图片商家编码销售价划线价库存重量(kg)
+ {{ td.spec_value }} + +
+ + +
+
+ +
+
+ + + + + + + + + +
+
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + +
+
+ +
+
商品详情
+
+
+ +
+ + +
+
+
+
其他设置
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+ + +
+
积分设置
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+
+ 注:如需使用积分功能必须在 [营销管理 - 积分设置] 中开启 +
+
+
+ + +
+
会员折扣设置
+
+
+ +
+ + +
+ 如果不开启会员折扣,该商品则不享受会员等级折扣价 +
+
+
+
+
+ +
+ + +
+ 默认折扣:默认为用户所属会员等级的折扣率 +
+
+
+
+
+ +
+ + +
+ + : + + + +
+ +
+ 注:折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+
+ + +
+
分销设置
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ +
+
+ 一级佣金: + + % +
+
+ 二级佣金: + + % +
+
+ 三级佣金: + + % +
+
+

+ 注:如需使用分销功能必须在 [分销中心 - 分销设置] 中开启 +

+

+ 注:如不开启单独分销则默认使用全局分销比例 +

+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/goods/category/add.php b/source/application/store/view/goods/category/add.php new file mode 100644 index 0000000..415326a --- /dev/null +++ b/source/application/store/view/goods/category/add.php @@ -0,0 +1,87 @@ +
+
+
+
+
+
+
+
+
添加商品分类
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + diff --git a/source/application/store/view/goods/category/edit.php b/source/application/store/view/goods/category/edit.php new file mode 100644 index 0000000..8ce001b --- /dev/null +++ b/source/application/store/view/goods/category/edit.php @@ -0,0 +1,96 @@ +
+
+
+
+
+
+
+
+
编辑商品分类
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ +
+ + + +
+ +
+
+
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + diff --git a/source/application/store/view/goods/category/index.php b/source/application/store/view/goods/category/index.php new file mode 100644 index 0000000..d279e92 --- /dev/null +++ b/source/application/store/view/goods/category/index.php @@ -0,0 +1,134 @@ +
+
+
+
+
+
商品分类
+
+
+
+
+

注:商品分类最多添加2级,分类图片可参考 + 分类页模板 上传

+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
分类ID分类名称分类排序添加时间操作
+ +
 -- + +
   -- + +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/goods/comment/detail.php b/source/application/store/view/goods/comment/detail.php new file mode 100644 index 0000000..debfc80 --- /dev/null +++ b/source/application/store/view/goods/comment/detail.php @@ -0,0 +1,163 @@ +
+
+
+
+
+
+
+
+
商品评价详情
+
+
+ +
+ + (用户id:) +
+
+
+ +
+ + 商品图片 + +
+
+
+ +
+ +
+
+
+ +
+ + + +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ $item): ?> +
+ + + + + +
+ +
+
+
+ 最多允许6张,可拖拽调整显示顺序 +
+
+
+
+ +
+ + 数字越小越靠前 +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + diff --git a/source/application/store/view/goods/comment/index.php b/source/application/store/view/goods/comment/index.php new file mode 100644 index 0000000..a0a5ebb --- /dev/null +++ b/source/application/store/view/goods/comment/index.php @@ -0,0 +1,123 @@ +
+
+
+
+
+
评价列表
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
ID商品图片商品名称用户评分评价内容是否有图片显示状态评价排序评价时间操作
+ + 商品图片 + + +

+
+

+ +
+ + 好评 + + 中评 + + 差评 + + +

+
+ + + + + + + + 显示 + + 隐藏 + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/goods/edit.php b/source/application/store/view/goods/edit.php new file mode 100644 index 0000000..aa2e504 --- /dev/null +++ b/source/application/store/view/goods/edit.php @@ -0,0 +1,691 @@ + + +
+
+
+
+
+
+
+
+
基本信息
+
+
+ +
+ +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+
+ +
+ $item): ?> +
+ + + + + +
+ +
+
+
+ 尺寸750x750像素以上,大小2M以下 (可拖拽图片调整显示顺序 ) +
+
+
+
+ +
+ + 选填,商品卖点简述,例如:此款商品美观大方 性价比较高 不容错过 +
+
+ +
+
规格/库存
+
+
+ +
+ + + +
+ 注:该商品当前正在参与其他活动,商品规格不允许更改 +
+ +
+
+ + +
+
+ +
+
+
+ {{ item.group_name }} + +
+
+
+ {{ val.spec_value }} + +
+
+ + +
+
+
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{{ item.group_name }}规格图片商家编码销售价划线价库存重量(kg)
+ {{ td.spec_value }} + +
+ + +
+
+ +
+
+ + + + + + + + + +
+
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + +
+
+ +
+
商品详情
+
+
+ +
+ + +
+
+
+
其他设置
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+ + +
+
积分设置
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+
+ 注:如需使用积分功能必须在 [营销管理 - 积分设置] 中开启 +
+
+
+ + +
+
会员折扣设置
+
+
+ +
+ + +
+ 如果不开启会员折扣,该商品则不享受会员等级折扣价 +
+
+
+
+
+ +
+ + +
+ 默认折扣:默认为用户所属会员等级的折扣率 +
+
+
+
+
+ +
+ + +
+ + : + + + +
+ +
+ 注:折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+
+ + +
+
分销设置
+
+
+ +
+ + +
+
+
+
+ +
+ + +
+
+
+ + '%', 20 => '元']; + ?> +
+
+ 一级佣金: + + + + +
+
+ 二级佣金: + + + + +
+
+ 三级佣金: + + + + +
+
+

+ 注:如需使用分销功能必须在 [分销中心 - 分销设置] 中开启 +

+

+ 注:如不开启单独分销则默认使用全局分销比例 +

+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+
+
+ + +{{include file="layouts/_template/tpl_file_item" /}} + + +{{include file="layouts/_template/file_library" /}} + + + + + + + diff --git a/source/application/store/view/goods/index.php b/source/application/store/view/goods/index.php new file mode 100644 index 0000000..c2570b5 --- /dev/null +++ b/source/application/store/view/goods/index.php @@ -0,0 +1,190 @@ +
+
+
+
+
+
出售中的商品
+
+
+ +
+
+ +
+
+ + + +
+
+
+
+
+ get('category_id') ?: null; ?> + +
+
+ get('status') ?: null; ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
商品ID商品图片商品名称商品分类实际销量商品排序商品状态添加时间操作
+ + 商品图片 + + +

+
+ + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/index/index.php b/source/application/store/view/index/index.php new file mode 100644 index 0000000..1696ead --- /dev/null +++ b/source/application/store/view/index/index.php @@ -0,0 +1,179 @@ +
+ + +
+
+
+
+
商城统计
+
+
+
+
+
商品总量
+
+
+
当前商品总数量
+ +
+
+
+ +
+
+
用户总量
+
+
+
当前用户总数量
+ +
+
+
+ +
+
+
订单总量
+
+
+
已付款订单总数量
+ +
+
+
+ +
+
+
评价总量
+
+
+
订单评价总数量
+ +
+
+
+ +
+
+
+
+ + +
+
+
+
+
实时概况
+
+
+
+
+
+ +
+
+
销售额(元)
+
+
+ 昨日:
+
+
+
+
+
+
支付订单数
+
+
+ 昨日:
+
+
+
+
+
+ +
+
+
新增用户数
+
+
+ 昨日:
+
+
+
+
+
+
下单用户数
+
+
+ 昨日:
+
+
+
+
+
+
+ + +
+
+
+
+
近七日交易走势
+
+
+
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/source/application/store/view/layouts/_template/file_library.php b/source/application/store/view/layouts/_template/file_library.php new file mode 100644 index 0000000..ccd7066 --- /dev/null +++ b/source/application/store/view/layouts/_template/file_library.php @@ -0,0 +1,123 @@ + + + + + + + + + + + diff --git a/source/application/store/view/layouts/_template/tpl_file_item.php b/source/application/store/view/layouts/_template/tpl_file_item.php new file mode 100644 index 0000000..e8feb4d --- /dev/null +++ b/source/application/store/view/layouts/_template/tpl_file_item.php @@ -0,0 +1,13 @@ + + + diff --git a/source/application/store/view/layouts/error.php b/source/application/store/view/layouts/error.php new file mode 100644 index 0000000..7b7e4b7 --- /dev/null +++ b/source/application/store/view/layouts/error.php @@ -0,0 +1,57 @@ + +
+
+
+
+
+
+ +
+
提交失败
+
+
+ +
+
+
+
+
+
+ diff --git a/source/application/store/view/layouts/layout.php b/source/application/store/view/layouts/layout.php new file mode 100644 index 0000000..9fd2383 --- /dev/null +++ b/source/application/store/view/layouts/layout.php @@ -0,0 +1,134 @@ + + + + + + + <?= $setting['store']['values']['name'] ?> + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ + + + +
+ {__CONTENT__} +
+ + +
+ + + + + + + + + + diff --git a/source/application/store/view/market/basic/full_free.php b/source/application/store/view/market/basic/full_free.php new file mode 100644 index 0000000..cb0a357 --- /dev/null +++ b/source/application/store/view/market/basic/full_free.php @@ -0,0 +1,400 @@ +
+
+
+
+
+
+
+
+
满额包邮设置
+
+
+ +
+ + +
+
+
+ +
+
+ + +
+ 如果开启满额包邮,设置0为全场包邮 +
+
+
+ +
+
+ +
+
+ + + + + +
+
+
+
+
+
+ +
+ + + 选择地区 + +
+ + 全国 + + +
+
+
+
+
+ +
+
+
+
+
+ + +
+
+
+
+ + 清空 +
+
+
+
+ +
+ +
+

+ +

+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + diff --git a/source/application/store/view/market/coupon/add.php b/source/application/store/view/market/coupon/add.php new file mode 100644 index 0000000..2b8a154 --- /dev/null +++ b/source/application/store/view/market/coupon/add.php @@ -0,0 +1,226 @@ +
+
+
+
+
+
+
+
+
添加优惠券
+
+
+ +
+ + 例如:满100减10 +
+
+
+ +
+ + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ + 限制领取的优惠券数量,-1为不限制 +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + diff --git a/source/application/store/view/market/coupon/edit.php b/source/application/store/view/market/coupon/edit.php new file mode 100644 index 0000000..2302aaa --- /dev/null +++ b/source/application/store/view/market/coupon/edit.php @@ -0,0 +1,243 @@ +
+
+
+
+
+
+
+
+
编辑优惠券
+
+
+ +
+ + 例如:满100减10 +
+
+
+ +
+ + + + +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + 折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ + 限制领取的优惠券数量,-1为不限制 +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + diff --git a/source/application/store/view/market/coupon/index.php b/source/application/store/view/market/coupon/index.php new file mode 100644 index 0000000..4ffea09 --- /dev/null +++ b/source/application/store/view/market/coupon/index.php @@ -0,0 +1,120 @@ +
+
+
+
+
+
优惠券列表
+
+
+
+
+

注:优惠券只能抵扣商品金额,最多优惠到0.01元,不能抵扣运费

+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): ?> + + + + + + + + + + + + + + + + + + + + + + +
优惠券ID优惠券名称优惠券类型最低消费金额优惠方式有效期发放总数量已领取数量排序添加时间操作
+ + 立减 + + + + + + 领取 天内有效 + + + ~ + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/market/coupon/receive.php b/source/application/store/view/market/coupon/receive.php new file mode 100644 index 0000000..a6c3eb4 --- /dev/null +++ b/source/application/store/view/market/coupon/receive.php @@ -0,0 +1,76 @@ +
+
+
+
+
+
优惠券领取记录
+
+
+
+ + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
用户优惠券ID优惠券名称优惠券类型最低消费金额优惠方式有效期领取时间
+

+ +
+ + 立减 + + + + + + 领取 天内有效 + + + ~ + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/market/points/log.php b/source/application/store/view/market/points/log.php new file mode 100644 index 0000000..764736a --- /dev/null +++ b/source/application/store/view/market/points/log.php @@ -0,0 +1,96 @@ +
+
+
+
+
+
积分明细
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
ID微信头像微信昵称变动数量描述/说明管理员备注创建时间
+ + + + +

+ +
+ 0 ? '+' : '' ?> +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/market/points/setting.php b/source/application/store/view/market/points/setting.php new file mode 100644 index 0000000..b2053e8 --- /dev/null +++ b/source/application/store/view/market/points/setting.php @@ -0,0 +1,153 @@ +
+
+
+
+
+
+
+
+
积分设置
+
+
+ +
+ +
+ 注:修改积分名称后,在买家端的所有页面里,看到的都是自定义的名称 +
+
+ 注:商家使用自定义的积分名称来做品牌运营。如京东把积分称为“京豆”,淘宝把积分称为“淘金币” +
+
+
+
+ +
+ +
+
+ +
+
积分赠送
+
+
+ +
+ + +
+ 注:如开启则订单完成后赠送用户积分 +
+
+ 积分赠送规则:1.订单确认收货已完成;2.已完成订单超出后台设置的申请售后期限 +
+
+
+
+ +
+
+ + % +
+
+ 注:赠送比例请填写数字0~100;订单的运费不参与积分赠送 +
+
+ 例:订单付款金额(100.00元) * 积分赠送比例(100%) = 实际赠送的积分(100积分) +
+
+
+ +
+
积分抵扣
+
+
+ +
+ + +
+ 注:如开启则用户下单时可选择使用积分抵扣订单金额 +
+
+
+
+ +
+
+ 1个积分可抵扣 + + +
+
+ 例如:1积分可抵扣0.01元,100积分则可抵扣1元,1000积分则可抵扣10元 +
+
+
+
+ +
+
+ 订单满 + + +
+
+ + 最高可抵扣金额 + + + % +
+
+ 温馨提示:例如订单金额100元,最高可抵扣10%,此时用户可抵扣10元 +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/market/push/send.php b/source/application/store/view/market/push/send.php new file mode 100644 index 0000000..e880028 --- /dev/null +++ b/source/application/store/view/market/push/send.php @@ -0,0 +1,116 @@ +
+
+
+
+
+
+
+
+
发送推送消息
+
+
+
+

+ 注:模板消息只能发送给活跃用户,查看活跃用户列表,建议每次发送不超过10人。 +

+

注:根据腾讯官方规定,滥用模板消息接口有被封号的风险,请谨慎使用!

+
+
+
+ +
+ + 如需发送多个用户,请使用英文逗号 , 隔开;例如:10001,10002 +
+
+
+ + +
+
+ +
+ +
+
+ + param('more') ? 10 : 5; ?> + +
+ +
+ +
+
+ + +
+ +
+ + + 用户点击消息进入的小程序页面,例如:pages/index/index + +
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ + diff --git a/source/application/store/view/market/push/user.php b/source/application/store/view/market/push/user.php new file mode 100644 index 0000000..9271282 --- /dev/null +++ b/source/application/store/view/market/push/user.php @@ -0,0 +1,64 @@ +
+
+
+
+
+
活跃用户列表
+
+
+
+
+

注:仅展示7天内在小程序中活跃(浏览点击)的用户。

+

注:formId有效期是7天,可用于向用户发送模板消息。

+
+
+
+ + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + +
用户ID用户头像用户昵称formid数量注册时间
+ + + +
暂无活跃用户
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/market/recharge/plan/add.php b/source/application/store/view/market/recharge/plan/add.php new file mode 100644 index 0000000..1b500d7 --- /dev/null +++ b/source/application/store/view/market/recharge/plan/add.php @@ -0,0 +1,63 @@ +
+
+
+
+
+
+
+
+
添加充值套餐
+
+
+ +
+ + 例如:套餐A 套餐B +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/market/recharge/plan/edit.php b/source/application/store/view/market/recharge/plan/edit.php new file mode 100644 index 0000000..50ccc90 --- /dev/null +++ b/source/application/store/view/market/recharge/plan/edit.php @@ -0,0 +1,63 @@ +
+
+
+
+
+
+
+
+
编辑充值套餐
+
+
+ +
+ + 例如:套餐A 套餐B +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/market/recharge/plan/index.php b/source/application/store/view/market/recharge/plan/index.php new file mode 100644 index 0000000..90ac168 --- /dev/null +++ b/source/application/store/view/market/recharge/plan/index.php @@ -0,0 +1,93 @@ +
+
+
+
+
+
充值套餐列表
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
套餐ID套餐名称充值金额赠送金额排序添加时间更新时间操作
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/market/recharge/setting.php b/source/application/store/view/market/recharge/setting.php new file mode 100644 index 0000000..35f1544 --- /dev/null +++ b/source/application/store/view/market/recharge/setting.php @@ -0,0 +1,91 @@ +
+
+
+
+
+
+
+
+
充值设置
+
+
+ +
+ + +
+ 如设置不允许则用户端不显示充值按钮 +
+
+
+
+ +
+ + +
+ 是否允许用户填写自定义的充值金额 +
+
+
+
+ +
+ + +
+ 用户充值自定义金额是否自动匹配套餐,如不开启则不参与套餐金额赠送 +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+ + diff --git a/source/application/store/view/order/detail.php b/source/application/store/view/order/detail.php new file mode 100644 index 0000000..a08bd98 --- /dev/null +++ b/source/application/store/view/order/detail.php @@ -0,0 +1,612 @@ + +
+
+
+
+
+ + +
+ +
    +
  • + 下单时间 +
    +
  • +
  • + 付款 + +
    + 付款于 +
    + +
  • +
  • + 发货 + +
    + 发货于 +
    + +
  • +
  • + 收货 + +
    + 收货于 +
    + +
  • +
  • + 完成 + +
    + 完成于 +
    + +
  • +
+
+ + +
+
基本信息
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
订单号买家订单金额支付方式配送方式交易状态操作
+

+ +
+
+
    +
  • 订单总额:
  • +
  • +
+ 0) : ?> +
    +
  • 优惠券抵扣:
  • +
  • - ¥
  • +
+ + 0) : ?> +
    +
  • 积分抵扣:
  • +
  • - ¥
  • +
+ +
    +
  • 运费金额:
  • +
  • +¥
  • +
+ +
    +
  • 后台改价:
  • +
  • + ¥
  • +
+ +
    +
  • 实付款金额:
  • +
  • + ¥
  • +
+
+
+ + + + +

付款状态: + + +

+

发货状态: + + +

+

收货状态: + + +

+ +

订单状态: + +

+ +
+ +

+ 修改价格 +

+ +
+
+ + +
+
商品信息
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
商品名称商品编码重量(Kg)单价购买数量商品总价
+
+ +
+
+

+ +
+
+

+ +

+ +

+ 会员折扣价: +

+ +
×
+ 买家留言: + 总计金额:¥ +
+
+ + + +
+
收货信息
+
+
+ + + + + + + + + + + + + +
收货人收货电话收货地址
+ + + + +
+
+ + + + + +
+
自提信息
+
+
+

联系人:

+

联系电话:

+
+ +
+
自提门店信息
+
+
+ + + + + + + + + + + + + + + + + + + +
门店ID门店logo门店名称联系人联系电话门店地址
+ + + + + + + + +
+
+ + + + +
+
付款信息
+
+
+ + + + + + + + + + + + + + + + + +
应付款金额支付方式支付流水号付款状态付款时间
+ + + + +
+
+ + + + + +
+
用户取消订单
+
+
+
+

当前买家已付款并申请取消订单,请审核是否同意,如同意则自动退回付款金额(微信支付原路退款)并关闭订单。

+
+
+ +
+
+ +
+
+ + +
+ +
+
+
+
+ + +
+
+
+ + + + + +
+
发货信息
+
+ + + +
+
+ +
+ +
+ 可在 物流公司列表 + 中设置 + +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + +
物流公司物流单号发货状态发货时间
+ + + + +
+
+ + + + + +
+
门店自提核销
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ + +
+ + + + + + + + + + + + + + + +
自提门店名称核销员核销状态核销时间
+

+ +
+

+ +
+ + 已核销 + + +
+
+ + + +
+
+ +
+
+
+ + + + + diff --git a/source/application/store/view/order/index.php b/source/application/store/view/order/index.php new file mode 100644 index 0000000..62b8945 --- /dev/null +++ b/source/application/store/view/order/index.php @@ -0,0 +1,251 @@ + +
+
+
+
+
+
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $order): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + +
商品信息单价/数量实付款买家支付方式配送方式交易状态操作
+ + 订单号: +
+
+ +
+
+

+ +
+
+

+

×

+
+

+ +
+

+ +
+ + + + + + + + +

付款状态: + + +

+

发货状态: + + +

+

收货状态: + + +

+ +

订单状态: + +

+ +
+
+ + + 订单详情 + + + + 去发货 + + + + + 去审核 + + +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/order/operate/batchDelivery.php b/source/application/store/view/order/operate/batchDelivery.php new file mode 100644 index 0000000..f8ad473 --- /dev/null +++ b/source/application/store/view/order/operate/batchDelivery.php @@ -0,0 +1,80 @@ +
+
+
+
+
+
+
+
+
批量发货
+
+
+ +
+
+ + +
+
+ +
+
+
+ +
+ +
+ 可在 物流公司列表 + 中设置 + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/order/refund/detail.php b/source/application/store/view/order/refund/detail.php new file mode 100644 index 0000000..a0c9039 --- /dev/null +++ b/source/application/store/view/order/refund/detail.php @@ -0,0 +1,358 @@ +
+
+
+
+
+
+
售后单信息
+
+
+ + + + + + + + + + + + + + + + + +
订单号买家售后类型处理状态操作
+

+ +
+ + + + +

+ 商家审核: + + + + + + + +

+ + +

+ 用户发货: + + 待发货 + + 已发货 + +

+ + + +

商家收货: 待收货

+ + + + + + +
+ + 订单详情 + +
+
+ +
+
售后商品信息
+
+
+ + + + + + + + + + + + + + + + + + + +
商品名称商品编码重量(Kg)单价购买数量付款价
+
+ +
+
+

+ +
+
× +
+
+ +
+
用户申请原因
+
+
+
+ +
+
+
+ +
+ + + +
+ +
+
+
+ + + + +
+
商家审核
+
+ +
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ + + 去添加 + +
+
+
+ +
+ + 如审核状态为拒绝,则需要输入拒绝原因 +
+
+
+
+ +
+
+
+ + + + + +
+
退货地址
+
+
+ + + + + + + + + + + + + +
收货人收货电话收货地址
+
+ + + + +
+
商家拒绝原因
+
+
+
+ +
+
+ + + + +
+
用户发货信息
+
+
+ + + + + + + + + + + + + + + + + +
物流公司物流单号用户发货状态发货时间商家收货状态
+ 已发货 + + + 已收货 + + 待收货 + +
+
+ + + + + +
+
确认收货并退款
+
+
+
+

注:该操作将执行订单原路退款 并关闭当前售后单,请确认并填写退款的金额(不能大于订单实付款)

+ +

+ 注:当前订单存在后台改价记录,退款金额请参考订单实付款金额

+ +
+
+
+
+ +
+ +
+
+
+ +
+ + + 请输入退款金额,最多 + 元 + +
+
+
+
+ +
+
+
+ + + +
+
+
+
+
+ + diff --git a/source/application/store/view/order/refund/index.php b/source/application/store/view/order/refund/index.php new file mode 100644 index 0000000..40d15f0 --- /dev/null +++ b/source/application/store/view/order/refund/index.php @@ -0,0 +1,210 @@ +
+
+
+
+
+
售后列表
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + + + +
商品信息单价/数量付款价买家售后类型处理状态操作
+ 售后申请时间: + 订单号: +
+
+ +
+
+

+ +
+
+

+

×

+
+

+
+

+ +
+ + + + +

+ 商家审核: + + + + + + + +

+ + +

+ 用户发货: + + 待发货 + + 已发货 + +

+ + + +

商家收货: 待收货

+ + + + + + +
+ $item['order_refund_id']]); ?> +
+ + 售后详情 + + + + 去审核 + + + + + 确认收货 + + +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/passport/login.php b/source/application/store/view/passport/login.php new file mode 100644 index 0000000..9e1da4a --- /dev/null +++ b/source/application/store/view/passport/login.php @@ -0,0 +1,67 @@ + + + + + + 商城系统登录 + + + + + + + +
+ +
+ + + + + + diff --git a/source/application/store/view/setting/address/add.php b/source/application/store/view/setting/address/add.php new file mode 100644 index 0000000..e0a2ea9 --- /dev/null +++ b/source/application/store/view/setting/address/add.php @@ -0,0 +1,60 @@ +
+
+
+
+
+
+
+
+
新增退货地址
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/address/edit.php b/source/application/store/view/setting/address/edit.php new file mode 100644 index 0000000..10ff6b6 --- /dev/null +++ b/source/application/store/view/setting/address/edit.php @@ -0,0 +1,60 @@ +
+
+
+
+
+
+
+
+
新增退货地址
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/address/index.php b/source/application/store/view/setting/address/index.php new file mode 100644 index 0000000..67718c8 --- /dev/null +++ b/source/application/store/view/setting/address/index.php @@ -0,0 +1,92 @@ +
+
+
+
+
+
退货地址列表
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + +
地址ID收货人姓名联系电话详细地址排序添加时间操作
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/setting/cache/clear.php b/source/application/store/view/setting/cache/clear.php new file mode 100644 index 0000000..125ece4 --- /dev/null +++ b/source/application/store/view/setting/cache/clear.php @@ -0,0 +1,48 @@ +
+
+
+
+
+
+
+
+
清理缓存
+
+
+ +
+ $item): ?> + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/delivery/add.php b/source/application/store/view/setting/delivery/add.php new file mode 100644 index 0000000..82ce8b8 --- /dev/null +++ b/source/application/store/view/setting/delivery/add.php @@ -0,0 +1,197 @@ +
+
+
+
+
+
+
+
+
运费模版
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
可配送区域 + {{ method == 10 ? '首件 (个)' : '首重 (Kg)' }} + 运费 (元) + {{ method == 10 ? '续件 (个)' : '续重 (Kg)' }} + 续费 (元)
+

+ 全国 + +

+

+ 编辑 + 删除 +

+ +
+ + + + + + + +
+ + + 点击添加可配送区域和运费 + +
+
+
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+ + +
+
+
+
+ + 清空 +
+
+
+
+ +
+ +
+

+ +

+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + diff --git a/source/application/store/view/setting/delivery/index.php b/source/application/store/view/setting/delivery/index.php new file mode 100644 index 0000000..716b69e --- /dev/null +++ b/source/application/store/view/setting/delivery/index.php @@ -0,0 +1,91 @@ +
+
+
+
+
+
运费模板
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + isEmpty()): ?> + + + + + + + + + + + + + + + + +
模板ID模板名称计费方式排序添加时间操作
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/setting/express/add.php b/source/application/store/view/setting/express/add.php new file mode 100644 index 0000000..d73c30f --- /dev/null +++ b/source/application/store/view/setting/express/add.php @@ -0,0 +1,56 @@ +
+
+
+
+
+
+
+
+
新增物流公司
+
+
+ +
+ + 请对照 物流公司编码表 填写 +
+
+
+ +
+ + 用于快递100API查询物流信息,务必填写正确 +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/express/company.php b/source/application/store/view/setting/express/company.php new file mode 100644 index 0000000..eb7444d --- /dev/null +++ b/source/application/store/view/setting/express/company.php @@ -0,0 +1,5433 @@ +
+
+
+
+
+
+
物流公司编码表
+
+
+
+

友情提示:可使用Ctrl+F键 快速检索

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
公司名称公司编码
金岸物流jinan +
海带宝haidaibao +
澳通华人物流cllexpress +
斑马物流banma +
信丰物流xinfengwuliu +
德国(Deutsche Post)deutschepost +
苏宁订单suningorder +
宜送物流yiex +
AOL澳通速递aolau +
TRAKPAKtrakpak +
GTS快递gts +
通达兴物流tongdaxing +
中国香港(HongKong Post)英文hkposten +
骏丰国际速递junfengguoji +
俄罗斯邮政(Russian Post)pochta +
云达通ydglobe +
EU-EXPRESSeuexpress +
广州海关gzcustoms +
杭州海关hzcustoms +
南京海关njcustoms +
北京海关bjcustoms +
美西快递meixi +
一站通快递zgyzt +
易联通达el56 +
驿扬国际速运iyoungspeed +
途鲜物流ibenben +
豌豆物流wandougongzhu +
哥士传奇速递gscq365 +
心怡物流alog +
ME物流macroexpressco +
疯狂快递crazyexpress +
韩国邮政韩文koreapostkr +
全速物流quansu +
新杰物流sunjex +
鲁通快运lutong +
安的快递gda +
八达通bdatong +
美国申通stoexpress +
法国小包(colissimo)colissimo +
泛捷国际速递epanex +
中远e环球cosco +
顺达快递sundarexpress +
捷记方舟ajexpress +
方舟速递arkexpress +
明大快递adaexpress +
长江国际速递changjiang +
PCA Expresspcaexpress +
洋包裹yangbaoguo +
优联吉运uluckex +
德豪驿dehaoyi +
堡昕德速递bosind +
阿根廷(Correo Argentina)correoargentino +
秘鲁(SERPOST)peru +
哈萨克斯坦(Kazpost)kazpost +
广通速递gtongsudi +
东瀚物流donghanwl +
rpxrpx +
黑猫雅玛多yamato +
华通快运htongexpress +
吉尔吉斯斯坦(Kyrgyz Post)kyrgyzpost +
拉脱维亚(Latvijas Pasts)latvia +
黎巴嫩(Liban Post)libanpost +
立陶宛(Lietuvos pa?tas)lithuania +
马尔代夫(Maldives Post)maldives +
马耳他(Malta Post)malta +
马其顿(Macedonian Post)macedonia +
新西兰(New Zealand Post)newzealand +
摩尔多瓦(Posta Moldovei)moldova +
塞尔维亚(PE Post of Serbia)serbia +
塞浦路斯(Cyprus Post)cypruspost +
突尼斯EMS(Rapid-Poste)tunisia +
乌兹别克斯坦(Post of Uzbekistan)uzbekistan +
新喀里多尼亚[法国](New Caledonia)caledonia +
叙利亚(Syrian Post)republic +
亚美尼亚(Haypost-Armenian Postal)haypost +
也门(Yemen Post)yemen +
印度(India Post)india +
英国(大包,EMS)england +
约旦(Jordan Post)jordan +
越南小包(Vietnam Posts)vietnam +
黑山(Po?ta Crne Gore)montenegro +
哥斯达黎加(Correos de Costa Rica)correos +
EFS Post(平安快递)efs +
TNT Posttntpostcn +
立白宝凯物流lbbk +
匈牙利(Magyar Posta)hungary +
中国澳门(Macau Post)macao +
西安喜来快递xilaikd +
韩润hanrun +
格陵兰[丹麦](TELE Greenland A/S)greenland +
菲律宾(Philippine Postal)phlpost +
厄瓜多尔(Correos del Ecuador)ecuador +
冰岛(Iceland Post)iceland +
波兰小包(Poczta Polska)emonitoring +
阿尔巴尼亚(Posta shqipatre)albania +
埃及(Egypt Post)egypt +
爱沙尼亚(Eesti Post)omniva +
云豹国际货运leopard +
中外运空运sinoairinex +
上海昊宏国际货物hyk +
城晓国际快递ckeex +
中铁快运ztky +
出口易chukou1 +
跨畅(直邮易)kuachangwuliu +
WTD海外通wtdex +
CHS中环国际快递chszhonghuanguoji +
汉邦国际速递handboy +
银河物流milkyway +
荷兰速递(Nederland Post)nederlandpost +
澳州顺风快递emms +
环东物流huandonglg +
中邮速递wondersyd +
布谷鸟速递cuckooexpess +
万庚国际速递vangenexpress +
FedRoad 联邦转运fedroad +
Landmark Globallandmarkglobal +
佳成快递jiacheng +
诺尔国际物流nuoer +
加运美速递jym56 +
新时速物流csxss +
中宇天地zytdscm +
翔腾物流xiangteng +
恒瑞物流hengrui56 +
中国翼cnws +
邦工快运bgky100 +
上海无疆for买卖宝shanghaiwujiangmmb +
新加坡小包(Singapore Post)singpost +
中俄速通(淼信)mxe56 +
海派通hipito +
源安达yuananda +
赛澳递for买卖宝saiaodimmb +
ECMS Expressecmsglobal +
英脉物流gml +
佳家通货运jiajiatong56 +
吉日优派jrypex +
西安胜峰xaetc +
logen路坚ilogen +
amazon-国际订单amusorder +
CJ物流doortodoor +
转运四方zhuanyunsifang +
成都东骏物流dongjun +
日本郵便japanpost +
猴急送hjs +
全信通快递quanxintong +
信天捷快递xintianjie +
泰国138国际物流sd138 +
荷兰包裹(PostNL International Parcels)postnlpacle +
乐天速递ltexp +
智通物流ztong +
全速通quansutong +
中技物流zhongjiwuliu +
九曳供应链jiuyescm +
当当dangdang +
美龙快递mjexp +
唯品会(vip)vipshop +
1号店yhdshop +
皇家物流pfcexpress +
百千诚物流bqcwl +
法国(La Poste)csuivi +
DHL-全球件dhlen +
运通中港yuntongkuaidi +
苏宁物流suning +
荷兰Sky Postskypost +
瑞达国际速递ruidaex +
丰程物流sccod +
德中快递decnlh +
全时速运runhengfeng +
云邮跨境快递hkems +
亚风速递yafengsudi +
快淘快递kuaitao +
鑫通宝物流xtb +
USPSusps +
加拿大邮政canpostfr +
汇通天下物流httx56 +
台湾(中华邮政)postserv +
好又快物流haoyoukuai +
永旺达快递yongwangda +
木春货运mchy +
程光快递flyway +
百事亨通bsht +
万家通快递timedg +
全之鑫物流qzx56 +
美快国际物流meiquick +
ILYANGilyang +
先锋快递xianfeng +
亿顺航yishunhang +
尚橙物流shangcheng +
OnTracontrac +
TNT-全球件tnten +
顺丰-美国件shunfengen +
共速达gongsuda +
源伟丰yuanweifeng +
祥龙运通物流xianglongyuntong +
偌亚奥国际快递nuoyaao +
陪行物流peixingwuliu +
天天快递tiantian +
CCES/国通快递cces +
彪记快递biaojikuaidi +
安信达anxindakuaixi +
配思货运peisihuoyunkuaidi +
大田物流datianwuliu +
邮政快递包裹youzhengguonei +
文捷航空wenjiesudi +
BHTbht +
北青小红帽xiaohongmao +
GSMgsm +
汇强快递huiqiangkuaidi +
昊盛物流haoshengwuliu +
联邦快递-英文lianbangkuaidien +
伍圆速递wuyuansudi +
南京100nanjing +
全通快运quantwl +
宅急便zhaijibian +
加拿大(Canada Post)canpost +
COEcoe +
百通物流buytong +
友家速递youjia +
新元快递xingyuankuaidi +
中澳速递cnausu +
联合快递gslhkd +
河南次晨达ccd +
奔腾物流benteng +
今枫国际快运mapleexpress +
中运全速topspeedex +
中欧快运otobv +
宜家行yjxlm +
金马甲jmjss +
一号仓onehcang +
论道国际物流lundao +
顺通快递stkd +
globaltracktraceglobaltracktrace +
德方物流ahdf +
速递中国sendtochina +
NLEnle +
亚欧专线nlebv +
信联通sinatone +
澳德物流auod +
微转运wzhaunyun +
iExpressiexpress +
远成快运ycgky +
高考通知书emsluqu +
安鲜达exfresh +
BCWELTbcwelt +
欧亚专线euasia +
乐递供应链ledii +
万通快递gswtkd +
特急送lntjs +
金大物流jindawuliu +
民航快递minghangkuaidi +
红马甲物流sxhongmajia +
amazon-国内订单amcnorder +
ABFabf +
小米xiaomi +
新元国际xynyc +
小C海淘xiaocex +
航空快递airgtc +
叮咚澳洲转运dindon +
环球通达hqtd +
新西兰中通nzzto +
良藤国际速递lmfex +
速品快递supinexpress +
海龟国际快递turtle +
韩国邮政koreapostcn +
韵丰物流yunfeng56 +
易达通快递qexpress +
一运全成物流yyqc56 +
泛远国际物流farlogistis +
达速物流dasu +
恒通快递lqht +
壹品速递ypsd +
鹰运国际速递vipexpress +
南方传媒物流ndwl +
速呈宅配sucheng +
云南滇驿物流dianyi +
四川星程快递scxingcheng +
运通中港快递ytkd +
Gati-英文gatien +
jcexjcex +
凯信达kxda +
安达信advancing +
亿翔yxexpress +
加运美jiayunmeiwuliu +
赛澳递saiaodi +
康力物流kangliwuliu +
鑫飞鸿xinhongyukuaidi +
全一快递quanyikuaidi +
华企快运huaqikuaiyun +
青岛安捷快递anjiekuaidi +
递四方disifang +
三态速递santaisudi +
成都立即送lijisong +
河北建华hebeijianhua +
风行天下fengxingtianxia +
一统飞鸿yitongfeihong +
海外环球haiwaihuanqiu +
DHL-中国件dhl +
西安城联速递xianchengliansudi +
一柒国际物流yiqiguojiwuliu +
广东通路guangdongtonglu +
中国香港骏辉物流chunfai +
三三国际物流zenzen +
比利时国际(Bpost international)bpostinter +
海红for买卖宝haihongmmb +
FedEx-英国件(FedEx UK)fedexuk +
FedEx-英国件fedexukcn +
叮咚快递dingdong +
MRWmrw +
Chronopost Portugalchronopostport +
西班牙(Correos de Espa?a)correosdees +
丹麦(Post Denmark)postdanmarken +
Purolatorpurolator +
法国大包、EMS-法文(Chronopost France)chronopostfra +
Selektvrachtselektvracht +
蓝弧快递lanhukuaidi +
比利时(Belgium Post)belgiumpost +
晟邦物流nanjingshengbang +
UPS Mail Innovationsupsmailinno +
挪威(Posten Norge)postennorge +
瑞士(Swiss Post)swisspost +
英国邮政小包royalmailcn +
英国小包(Royal Mail)royalmail +
DHL Beneluxdhlbenelux +
DHL-荷兰(DHL Netherlands)dhlnetherlands +
OPEKopek +
Italy SDAitalysad +
Fastway Irelandfastway +
DHL-波兰(DHL Poland)dhlpoland +
DPDdpd +
速通物流sutongwuliu +
荷兰邮政-中文(PostNL international registered mail)postnlcn +
荷兰邮政(PostNL international registered mail)postnl +
乌克兰EMS(EMS Ukraine)emsukraine +
乌克兰邮政包裹ukrpostcn +
英国大包、EMS(Parcel Force)parcelforce +
YODELyodel +
UBI Australiagotoubi +
红马速递nedahm +
云南诚中物流czwlyn +
万博快递wanboex +
腾达速递nntengda +
郑州速捷sujievip +
中睿速递zhongruisudi +
中天万运zhongtianwanyun +
新蛋奥硕neweggozzo +
七天连锁sevendays +
UPS-全球件upsen +
跨越速运kuayue +
全际通quanjitong +
UPSups +
一邦速递yibangwuliu +
上海快通shanghaikuaitong +
品速心达快递pinsuxinda +
PostNord(Posten AB)postenab +
城际速递chengjisudi +
户通物流hutongwuliu +
飞康达feikangda +
星晨急便xingchengjibian +
全日通quanritongkuaidi +
凤凰快递fenghuangkuaidi +
广东邮政guangdongyouzhengwuliu +
长宇物流changyuwuliu +
万家物流wanjiawuliu +
EMS-国际件-英文emsinten +
飞远配送feiyuanvipshop +
国美gome +
能达速递ganzhongnengda +
急先达jixianda +
凡宇快递fanyukuaidi +
希优特xiyoutekuaidi +
中通(带电话)zhongtongphone +
蓝镖快递lanbiaokuaidi +
佳吉快运jiajiwuliu +
宏品物流hongpinwuliu +
GLSgls +
原飞航yuanfeihangwuliu +
海红网送haihongwangsong +
TNTtnt +
元智捷诚yuanzhijiecheng +
国际包裹youzhengguoji +
城市100city100 +
DPEXdpex +
芝麻开门zhimakaimen +
EMS-国际件emsguoji +
晋越快递jinyuekuaidi +
乐捷递lejiedi +
飞力士物流flysman +
百腾物流baitengwuliu +
品骏快递pjbest +
瓦努阿图(Vanuatu Post)vanuatu +
巴巴多斯(Barbados Post)barbados +
萨摩亚(Samoa Post)samoa +
斐济(Fiji Post)fiji +
英超物流yingchao +
TNY物流tny +
美通valueway +
新速航sunspeedy +
速方(Sufast)bphchina +
华航快递hzpl +
Gati-KWEgatikwe +
Red Expressredexpress +
Toll Priority(Toll Online)tollpriority +
Estafetaestafeta +
港快速递gdkd +
墨西哥(Correos de Mexico)mexico +
罗马尼亚(Posta Romanian)romanian +
DPD Polanddpdpoland +
阿联酋(Emirates Post)emirates +
新顺丰(NSF)nsf +
巴基斯坦(Pakistan Post)pakistan +
Asendia USAasendiausa +
法国大包、EMS-英文(Chronopost France)chronopostfren +
意大利(Poste Italiane)italiane +
世运快递shiyunkuaidi +
新干线快递anlexpress +
飞洋快递shipgce +
贝海国际速递xlobo +
黄马甲huangmajia +
Tolldpexen +
如风达rufengda +
EC-Firstclassecfirstclass +
DTDC Indiadtdcindia +
Safexpresssafexpress +
泰国(Thailand Thai Post)thailand +
SkyNet Malaysiaskynetmalaysia +
TNT Australiatntau +
马来西亚小包(Malaysia Post(Registered))malaysiapost +
马来西亚大包、EMS(Malaysia Post(parcel,EMS))malaysiaems +
沙特阿拉伯(Saudi Post)saudipost +
南非(South African Post Office)southafrican +
Mexico Senda Expressmexicodenda +
MyHermesmyhermes +
DPD Germanydpdgermany +
Nova Poshtanovaposhta +
Estesestes +
TNT UKtntuk +
Deltec Courierdeltec +
UPS Freightupsfreight +
TNT Italytntitaly +
Mexico Multipackmultipack +
葡萄牙(Portugal CTT)portugalctt +
Interlink Expressinterlink +
DPD UKdpduk +
乌克兰EMS-中文(EMS Ukraine)emsukrainecn +
乌克兰小包、大包(UkrPost)ukrpost +
TCI XPStcixps +
高铁速递hre +
新加坡EMS、大包(Singapore Speedpost)speedpost +
LaserShiplasership +
英国邮政大包EMSparcelforcecn +
同舟行物流chinatzx +
秦邦快运qbexpress +
skynetskynet +
忠信达zhongxinda +
门对门menduimen +
微特派weitepai +
海盟速递haimengsudi +
圣安物流shenganwuliu +
联邦快递lianbangkuaidi +
飞快达feikuaida +
EMSems +
天地华宇tiandihuayu +
煜嘉物流yujiawuliu +
郑州建华zhengzhoujianhua +
大洋物流dayangwuliu +
递达速运didasuyun +
易通达yitongda +
邮必佳youbijia +
EMS-英文emsen +
闽盛快递minshengkuaidi +
佳惠尔syjiahuier +
KCSkcs +
ADP国际快递adp +
颿达国际快递fardarww +
颿达国际快递-英文fandaguoji +
林道国际快递shlindao +
中外运速递-中文sinoex +
中外运速递zhongwaiyun +
深圳德创物流dechuangwuliu +
林道国际快递-英文ldxpres +
中国香港(HongKong Post)hkpost +
邦送物流bangsongwuliu +
华赫物流nmhuahe +
顺捷丰达shunjiefengda +
天马迅达tianma +
恒宇运通hyytes +
考拉国际速递kaolaexpress +
BlueDartbluedart +
日日顺快线rrskx +
运东西yundx +
黑狗物流higo +
鹏远国际速递pengyuanexpress +
安捷物流anjie88 +
骏达快递jdexpressusa +
C&C国际速递cncexp +
北京EMSbjemstckj +
airpak expresssairpak +
荷兰邮政-中国件postnlchina +
大达物流idada +
益递物流edlogistics +
中外运esinotrans +
速派快递(FastGo)fastgo +
易客满ecmscn +
美国云达yundaexus +
Tolltoll +
深圳DPEXszdpex +
俄顺达eshunda +
广东速腾物流suteng +
新鹏快递gdxp +
平安达腾飞pingandatengfei +
穗佳物流suijiawuliu +
传喜物流chuanxiwuliu +
捷特快递jietekuaidi +
隆浪快递longlangkuaidi +
佳吉快递jiajikuaidi +
快达物流kuaidawuliu +
飞狐快递feihukuaidi +
潇湘晨报xiaoxiangchenbao +
巴伦支balunzhi +
安能物流annengwuliu +
申通快递shentong +
亿领速运yilingsuyun +
店通快递diantongkuaidi +
OCA Argentinaocaargen +
尼日利亚(Nigerian Postal)nigerianpost +
智利(Correos Chile)chile +
以色列(Israel Post)israelpost +
京东物流jd +
奥地利(Austrian Post)austria +
乌克兰小包、大包(UkrPoshta)ukraine +
乌干达(Posta Uganda)uganda +
阿塞拜疆EMS(EMS AzerExpressPost)azerbaijan +
芬兰(Itella Posti Oy)finland +
斯洛伐克(Slovenská Posta)slovak +
阿鲁巴[荷兰](Post Aruba)aruba +
爱尔兰(An Post)ireland +
印度尼西亚EMS(Pos Indonesia-EMS)indonesia +
易优包裹eupackage +
威时沛运货运wtdchina +
行必达speeda +
中通国际zhongtongguoji +
千顺快递qskdyxgs +
西邮寄xipost +
顺捷达shunjieda +
CE易欧通国际速递cloudexpress +
和丰同城hfwuxi +
天联快运tlky +
优速物流youshuwuliu +
埃塞俄比亚(Ethiopian postal)ethiopia +
卢森堡(Luxembourg Post)luxembourg +
毛里求斯(Mauritius Post)mauritius +
文莱(Brunei Postal)brunei +
Quantiumquantium +
中铁物流zhongtiewuliu +
宇鑫物流yuxinwuliu +
巴林(Bahrain Post)bahrain +
纳米比亚(NamPost)namibia +
卢旺达(Rwanda i-posita)rwanda +
莱索托(Lesotho Post)lesotho +
肯尼亚(POSTA KENYA)kenya +
喀麦隆(CAMPOST)cameroon +
伯利兹(Belize Postal)belize +
巴拉圭(Correo Paraguayo)paraguay +
波黑(JP BH Posta)bohei +
玻利维亚bolivia +
柬埔寨(Cambodia Post)cambodia +
兰州伙伴物流huoban +
天纵物流tianzong +
坦桑尼亚(Tanzania Posts)tanzania +
阿曼(Oman Post)oman +
直布罗陀[英国]( Royal Gibraltar Post)gibraltar +
展勤快递byht +
越南EMS(VNPost Express)vnpost +
安迅物流anxl +
达方物流dfpost +
十方通物流sfift +
飞鹰物流hnfy +
UPS i-parceliparcel +
鑫锐达bjxsrd +
孟加拉国(EMS)bangladesh +
快捷速递kuaijiesudi +
日本(Japan Post)japanposten +
众辉达物流zhdwl +
秦远物流qinyuan +
澳邮中国快运auexpress +
日益通速递rytsd +
航宇快递hangyu +
急顺通pzhjst +
优速通达yousutongda +
飞邦快递fbkd +
华达快运huada +
FOX国际快递fox +
佳怡物流jiayiwuliu +
鹏程快递pengcheng +
冠庭国际物流guanting +
美国快递meiguokuaidi +
通和天下tonghetianxia +
音素快运yinsu +
创一快递chuangyi +
重庆星程快递cqxingcheng +
贵州星程快递gzxingcheng +
河南全速通hnqst +
快速递ksudi +
北极星快运polarisexpress +
6LS EXPRESSlsexpress +
ANTS EXPRESSqdants +
S2Cs2c +
Hi淘易快递hitaoe +
CNAIRcnair +
易欧洲国际物流yiouzhou +
阳光快递shiningexpress +
北京丰越供应链beijingfengyue +
华中快递cpsair +
青旅物流zqlwl +
易航物流yihangmall +
城铁速递cex +
千里速递qianli +
急递jdpplus +
佳捷翔物流jjx888 +
洋口岸ykouan +
考拉速递koalaexp +
天越物流surpassgo +
邮政标准快递youzhengbk +
运通快运ytky168 +
卢森堡航空cargolux +
优优速递youyou +
全川物流quanchuan56 +
SYNSHIP快递synship +
仓鼠快递cangspeed +
递五方云仓di5pll +
卓志速运chinaicip +
闪电兔shandiantu +
新宁物流xinning +
春风物流spring56 +
首达速运sdsy888 +
丽狮物流lishi +
雅澳物流yourscm +
直德邮zdepost +
日昱物流riyuwuliu +
Gati-中文gaticn +
派尔快递peex +
汇文huiwen +
东红物流donghong +
增益速递zengyisudi +
好运来hlyex +
顺丰速运shunfeng +
城际快递chengji +
程光快递chengguangkuaidi +
天翼快递tykd +
京东订单jdorder +
蓝天快递lantiankuaidi +
永昌物流yongchangwuliu +
笨鸟海淘birdex +
一正达速运yizhengdasuyun +
德意思dabei +
佐川急便sagawa +
优配速运sdyoupei +
速必达subida +
景光物流jgwl +
御风速运yufeng +
至诚通达快递zhichengtongda +
特急便物流sucmj +
亚马逊中国yamaxunwuliu +
货运皇kingfreight +
锦程物流jinchengwuliu +
澳货通auex +
澳速物流aosu +
澳世速递aus +
环球速运huanqiu +
麦力快递mailikuaidi +
瑞丰速递rfsd +
美联快递letseml +
CNPEX中邮快递cnpex +
鑫世锐达xsrd +
顺丰优选sfbest +
全峰快递quanfengkuaidi +
克罗地亚(Hrvatska Posta)hrvatska +
保加利亚(Bulgarian Posts)bulgarian +
Portugal Seurportugalseur +
International Seurseur +
久易快递jiuyicn +
Direct Linkdirectlink +
希腊EMS(ELTA Courier)eltahell +
捷克(?eská po?ta)ceskaposta +
Siodemkasiodemka +
爱尔兰(An Post)anposten +
渥途国际速运wotu +
一号线lineone +
四海快递sihaiet +
德坤物流dekuncn +
准实快运zsky123 +
宏捷国际物流hongjie +
鸿讯物流hongxun +
卡邦配送ahkbps +
凡客配送(作废)vancl +
瑞士邮政swisspostcn +
辉联物流huilian +
A2U速递a2u +
UEQ快递ueq +
中加国际快递scic +
易达通yidatong +
宜送yisong +
全球快运abcglobal +
芒果速递mangguo +
金海淘goldhaitao +
极光转运jiguang +
富腾达国际货运ftd +
DCSdcs +
捷网俄全通ruexp +
华通务达物流htwd +
申必达speedoex +
联运快递lianyun +
捷安达jieanda +
SHL畅灵国际物流shlexp +
EWE全球快递ewe +
顺邦国际物流shunbang +
成达国际速递chengda +
启辰国际速递qichen +
合众速递(UCS)ucs +
阿富汗(Afghan Post)afghan +
白俄罗斯(Belpochta)belpost +
冠捷物流gjwl +
钏博物流cbo56 +
西翼物流westwing +
优邦速运ubonex +
首通快运staky +
马珂博逻cnmcpl +
小熊物流littlebearbear +
玥玛速运yue777 +
上海航瑞货运hangrui +
星云速递nebuex +
环创物流ghl +
林安物流lasy56 +
笨鸟国际benniao +
全速快递fsexp +
法翔速运ftlexpress +
易转运ezhuanyuan +
Superb Gracesuperb +
蓝天国际快递ltx +
圣飞捷快递sfjhd +
淘韩国际快递krtao +
容智快运gdrz58 +
锦程快递hrex +
顺时达物流hnssd56 +
骏绅物流jsexpress +
德国雄鹰速递adlerlogi +
远为快递ywexpress +
嗖一下同城快递sofast56 +
开心快递happylink +
五六快运wuliuky +
卓烨快递hrbzykd +
ZTE中兴物流zteexpress +
尼尔快递nell +
高铁快运gaotieex +
万家康物流wjkwl +
国晶物流xdshipping +
德国云快递yunexpress +
宏递快运hd +
一起送yiqisong +
迈隆递运mailongdy +
新亚物流nalexpress +
艾瑞斯远ariesfar +
澳多多国际速递adodoxm +
CNUP 中联邮cnup +
UEX国际物流uex +
Hermeshermes +
PostElbepostelbe +
维普恩物流vps +
明辉物流zsmhwl +
联运通物流szuem +
龙象国际物流edragon +
永邦国际物流yongbangwuliu +
51跨境通wykjt +
速配欧翼superoz +
嘉里大荣物流kerrytj +
中国香港环球快运huanqiuabc +
CL日中速运clsp +
SQK国际速递chinasqk +
家家通快递newsway +
邮客全球速递yyox +
华瀚快递hhair56 +
顺士达速运shunshid +
天翔东捷运djy56 +
卓实快运zhuoshikuaiyun +
吉祥邮(澳洲)jixiangyouau +
蓝天快递blueskyexpress +
天天快物流guoeryue +
纵通速运ynztsy +
中通快运zhongtongkuaiyun +
CNEcnexps +
希腊包裹(ELTA Hellenic Post)elta +
星速递starex +
土耳其ptt +
哥伦比亚(4-72 La Red Postal de Colombia)colombia +
加州猫速递jiazhoumao +
捷邦物流jieborne +
邮政国内yzguonei +
Canparcanpar +
海硕高铁速递hsgtsd +
日日通国际rrthk +
天翼物流tywl99 +
啪啪供应链papascm +
万达美wdm +
安得物流annto +
广东诚通物流gdct56 +
安达速递adapost +
易达国际速递eta100 +
西游寄xiyoug +
光线速递gxwl +
易邮国际euguoji +
深圳邮政szyouzheng +
粤中国际货运代理(上海)有限公司yuezhongsh +
城通物流chengtong +
GE2D跨境物流ge2d +
败欧洲europe8 +
飛斯特bester +
蒙古国(Mongol Post)mongolpost +
乌拉圭(Correo Uruguayo)correo +
牙买加(Jamaica Post)jamaicapost +
格鲁吉亚(Georgian Pos)georgianpost +
美达快递meidaexpress +
驭丰速运yfsuyun +
无忧物流aliexpress +
邮鸽速运ugoexpress +
澳洲新干线快递expressplus +
标杆物流bmlchina +
长风物流longvast +
邮来速递youlai +
魔速达mosuda +
商桥物流shangqiao56 +
AUV国际快递auvexpress +
Newgisticsnewgistics +
FQ狂派速递freakyquick +
泽西岛jerseypost +
威盛快递wherexpess +
运通速运yuntong +
老挝(Lao Express)lao +
巴布亚新几内亚(PNG Post)postpng +
EASY EXPRESSeasyexpress +
壹米滴答yimidida +
飞云快递系统fyex +
跨跃国际kyue +
EMS包裹emsbg +
珠峰速运zf365 +
甘肃安的快递gansuandi +
一辉物流yatfai +
e直运edtexpress +
wish邮shpostwish +
顶世国际物流topshey +
龙枫国际快递lfexpress +
安能快递ane66 +
圆通快运yuantongkuaiyun +
宝通快递baotongkd +
美国汉邦快递aplus100 +
易普递sixroad +
速呈sczpds +
海淘物流ht22 +
海米派物流haimibuy +
天翔快递tianxiang +
易境达国际物流uscbexpress +
大韩通运cjkoreaexpress +
澳世速递ausexpress +
未来明天快递weilaimingtian +
科捷物流kejie +
大道物流dadaoex +
全联速运guexp +
可可树美中速运excocotree +
邮邦国际youban +
西安运逸快递yyexp +
Aplus物流aplusex +
锋鸟物流beebird +
青云物流bjqywl +
万邑通winit +
中翼国际物流chnexp +
亚洲顺物流yzswuliu +
E跨通ecallturn +
递四方美国disifangus +
星空国际wlwex +
极地快递polarexpress +
到了港camekong +
斯里兰卡(Sri Lanka Post)slpost +
斯洛文尼亚(Slovenia Post)slovenia +
多米尼加(INPOSDOM – Instituto Postal Dominicano)inposdom +
星运快递staryvr +
狮爱高铁物流sycawl +
爱拜物流ibuy8 +
商海德物流shd56 +
九宫物流jiugong +
缔惠盛合twkd56 +
快服务kfwnet +
dhl小包dhlecommerce +
宇佳物流yujiawl +
湘达物流xiangdawuliu +
远盾物流yuandun +
黑猫宅急便tcat +
韵达快运yundakuaiyun +
速派快递fastgoexpress +
中集冷云cccc58 +
久久物流jiujiuwl +
德国八易转运deguo8elog +
UTAO优到utaoscm +
乾坤物流yatexpress +
摩洛哥 ( Morocco Post )morocco +
尼泊尔(Nepal Postal Services)nepalpost +
伊朗(Iran Post)iran +
坦桑尼亚(Tanzania Posts Corporation)posta +
莫桑比克(Correios de Moçambique)correios +
聚中大juzhongda +
中邮电商chinapostcb +
鸿泰物流hnht56 +
南非EMSemssouthafrica +
申通国际stosolution +
皮牙子快递bazirim +
联众国际epspost +
丰通快运ftky365 +
BorderGuruborderguru +
艾姆勒imlb2c +
中欧国际物流eucnrail +
递四方澳洲disifangau +
艺凡快递yifankd +
宏观国际快递gvpexpress +
博茨瓦纳botspost +
塞内加尔laposte +
卡塔尔(Qatar Post)qpost +
苏丹(Sudapost)sudapost +
Sureline冠泰sureline +
海沧无忧hivewms +
安世通快递astexpress +
集先锋快递jxfex +
丰客物流fecobv +
同城快寄shpost +
海联快递hltop +
中联速递auvanda +
三象速递sxexpress +
神马快递shenma +
互联快运hlkytj +
温通物流wto56kj +
四海捷运sihiexpress +
苏通快运zjstky +
邦通国际comexpress +
劲通快递jintongkd +
凡仕特物流wlfast +
红背心hongbeixin +
居家通homexpress +
上大物流shangda +
中邮物流zhongyouwuliu +
Fedex-国际件-中文fedexcn +
韩国(Korea Post)koreapost +
中通快递zhongtong +
京广速递jinguangsudikuaijian +
FedEx-国际件fedex +
日日顺物流rrs +
微店weidianorder +
当当dangdangorder +
国送快运guosong +
考拉订单kaolaorder +
AAE-中国件aae +
四川快优达速递kuaiyouda +
百福东方baifudongfang +
TST速运通tstexp +
YUN TRACKyuntrack +
招金精炼zhaojin +
全程快递agopost +
CDEKcdek +
签收快递signedexpress +
佰麒快递beckygo +
增速跨境zyzoom +
Aramexaramex +
越中国际物流vctrans +
德国优拜物流ubuy +
德尚国际速递gslexpress +
德国 EUC POSTeucpost +
泰国中通ZTOthaizto +
泰国中通CTOctoexp +
顺丰-繁体shunfenghk +
嘉诚速达jcsuda +
AFLafl +
众派速递zhpex +
海星桥快递haixingqiao +
蘑菇街mogujieorder +
嘉里大通jialidatong +
万象物流wanxiangwuliu +
澳大利亚(Australia Post)auspost +
国通快递guotongkuaidi +
全晨快递quanchenkuaidi +
飞豹快递feibaokuaidi +
中速快递zhongsukuaidi +
优能物流mantoo +
国美gomeorder +
亚马逊中国订单amazoncnorder +
蜜芽订单miaorder +
顺丰订单sfexpressorder +
申通快运stoe56 +
City-Linkcitylink +
德邦物流debangwuliu +
银捷速递yinjiesudi +
D速快递dsukuaidi +
民邦速递minbangsudi +
百世快运baishiwuliu +
DHL-德国件(DHL Deutschland)dhlde +
能装能送canhold +
聚美优品jumeiyoupinorder +
诚一物流parcelchina +
网易严选wangyiyxorder +
龙邦速递longbanwuliu +
明亮物流mingliangwuliu +
速尔快递suer +
盛辉物流shenghuiwuliu +
越丰物流yuefengwuliu +
比利时(Bpost)bpost +
韵达快递yunda +
唯品会vipshoporder +
美丽说meilishuoorder +
顺丰优选sfbestorder +
驼峰国际humpline +
小米订单xiaomiorder +
一智通1ziton +
TransRushtransrush +
百世快递huitongkuaidi +
联昊通lianhaowuliu +
远成物流yuanchengwuliu +
FedEx-美国件fedexus +
OCSocs +
巴西(Brazil Post/Correios)brazilposten +
孔夫子kongfzorder +
一号店yhdshoporder +
卷皮juanpiorder +
淘宝订单taobaoorder +
盛丰物流shengfengwuliu +
瑞典(Sweden Post)ruidianyouzheng +
圆通速递yuantong +
宅急送zhaijisong +
新邦物流xinbangwuliu +
恒路物流hengluwuliu +
华夏龙huaxialongwuliu +
龙飞祥快递longfx +
城市映急city56 +
五六快运56kuaiyun +
速通物流sut56 +
迅达速递xdexpress +
JDIEXjdiex +
泰捷达国际物流ztjieda +
捎客物流shaoke +
全球速递pdstow +
安达易国际速递adiexpress +
番薯国际货运koali +
贝贝beibeiorder +
德邦快递debangkuaidi +
联合速运unitedex +
龙邦物流lbex +
GHT物流ghtexpress +
香港伟豪国际物流whgjkd +
澳洲迈速快递maxeedexpress +
TCXB国际物流tcxbthai +
波音速递overseaex +
鑫远东速运xyd666 +
中环快递zhonghuan +
沃埃家wowvip +
OBOR Expressoborexpress +
盛丰物流sfwl +
速达通sdto +
苏豪快递shipsoho +
三盛快递sanshengco +
迅速快递xunsuexpress +
众川国际zhongchuan +
陆本速递 LUBEN EXPRESSluben +
西濃運輸seino +
加拿大联通快运fastontime +
花瓣转运flowerkd +
合心速递hexinexpress +
Highsincehighsince +
蜜蜂速递bee001 +
天使物流云tswlcloud +
王牌快递shipbyace +
华美快递hmus +
折800zhe800order +
小红书xiaohongshuorder +
铁中快运tzky +
景顺物流jingshun +
中环转运zhonghuanus +
YCG物流ycgglobal +
驿递汇速递yidihui
+
+
+
+
+
+
\ No newline at end of file diff --git a/source/application/store/view/setting/express/edit.php b/source/application/store/view/setting/express/edit.php new file mode 100644 index 0000000..1c9a7fa --- /dev/null +++ b/source/application/store/view/setting/express/edit.php @@ -0,0 +1,58 @@ +
+
+
+
+
+
+
+
+
编辑物流公司
+
+
+ +
+ + 请对照 物流公司编码表 填写 +
+
+
+ +
+ + 用于快递100API查询物流信息,务必填写正确 +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/express/index.php b/source/application/store/view/setting/express/index.php new file mode 100644 index 0000000..f4130db --- /dev/null +++ b/source/application/store/view/setting/express/index.php @@ -0,0 +1,91 @@ +
+
+
+
+
+
物流公司列表
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + isEmpty()): ?> + + + + + + + + + + + + + + + + +
物流公司ID物流公司名称物流公司代码排序添加时间操作
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/setting/help/tplMsg.php b/source/application/store/view/setting/help/tplMsg.php new file mode 100644 index 0000000..47407b5 --- /dev/null +++ b/source/application/store/view/setting/help/tplMsg.php @@ -0,0 +1,29 @@ +
+
+
+
+
+
+
如何获取模板消息ID
+
+
+

1. 进入微信小程序官方后台,找到模板库

+

+
+
+

2. 根据模板编号,选用指定的模板

+

+
+
+

3. 选择指定的关键词,并调整好顺序,点击提交

+

+
+
+

4. 复制模板ID

+

+
+
+
+
+
+
\ No newline at end of file diff --git a/source/application/store/view/setting/printer.php b/source/application/store/view/setting/printer.php new file mode 100644 index 0000000..039a052 --- /dev/null +++ b/source/application/store/view/setting/printer.php @@ -0,0 +1,71 @@ +
+
+
+
+
+
+
+
+
小票打印设置
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/printer/add.php b/source/application/store/view/setting/printer/add.php new file mode 100644 index 0000000..ad5e047 --- /dev/null +++ b/source/application/store/view/setting/printer/add.php @@ -0,0 +1,130 @@ + +
+
+
+
+
+
+
+
+
新增小票打印机
+
+
+ +
+ +
+
+
+ +
+ +
+ 目前支持 飞鹅打印机、365云打印 +
+
+
+ + +
+
+ +
+ + 飞鹅云后台注册用户名 +
+
+
+ +
+ + 飞鹅云后台登录生成的UKEY +
+
+
+ +
+ + 打印机编号为9位数字,查看飞鹅打印机底部贴纸上面的编号 +
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + 同一订单,打印的次数 +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/printer/edit.php b/source/application/store/view/setting/printer/edit.php new file mode 100644 index 0000000..2c1ce35 --- /dev/null +++ b/source/application/store/view/setting/printer/edit.php @@ -0,0 +1,141 @@ + +
+
+
+
+
+
+
+
+
编辑小票打印机
+
+
+ +
+ +
+
+
+ +
+ +
+ 目前支持 飞鹅打印机、365云打印 +
+
+
+ + +
+
+ +
+ + 飞鹅云后台注册用户名 +
+
+
+ +
+ + 飞鹅云后台登录生成的UKEY +
+
+
+ +
+ + 打印机编号为9位数字,查看飞鹅打印机底部贴纸上面的编号 +
+
+
+ + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ + 同一订单,打印的次数 +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/printer/index.php b/source/application/store/view/setting/printer/index.php new file mode 100644 index 0000000..87ceab7 --- /dev/null +++ b/source/application/store/view/setting/printer/index.php @@ -0,0 +1,91 @@ +
+
+
+
+
+
小票打印机列表
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + isEmpty()): ?> + + + + + + + + + + + + + + + + +
打印机ID打印机名称打印机类型排序添加时间操作
+ +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/setting/sms.php b/source/application/store/view/setting/sms.php new file mode 100644 index 0000000..d9803f5 --- /dev/null +++ b/source/application/store/view/setting/sms.php @@ -0,0 +1,156 @@ +
+
+
+
+
+
+
+
+
短信通知(阿里云短信)
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
新付款订单提醒
+
+
+ +
+ + +
+
+
+ +
+ + 例如:SMS_139800030 +
+
+
+
+ 模板内容:您有一条新订单,订单号为:${order_no},请注意查看。 +
+
+
+ +
+ +
+ 注:如需填写多个手机号,可用英文逗号 , 隔开 +
+
+ 接收测试: 点击发送 + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/storage.php b/source/application/store/view/setting/storage.php new file mode 100644 index 0000000..be79f44 --- /dev/null +++ b/source/application/store/view/setting/storage.php @@ -0,0 +1,197 @@ +
+
+
+
+
+
+
+
+
文件上传设置
+
+
+ +
+ + + + +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 请补全http:// 或 https://,例如:http://static.cloud.com +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 请补全http:// 或 https://,例如:http://static.cloud.com +
+
+
+
+
+ +
+ +
+
+
+ +
+ + 请填写地域简称,例如:ap-beijing、ap-hongkong、eu-frankfurt +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 请补全http:// 或 https://,例如:http://static.cloud.com +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/store.php b/source/application/store/view/setting/store.php new file mode 100644 index 0000000..a32ceff --- /dev/null +++ b/source/application/store/view/setting/store.php @@ -0,0 +1,81 @@ + +
+
+
+
+
+
+
+
+
商城设置
+
+
+ +
+ +
+
+
+ +
+ + + +
+ 注:配送方式至少选择一个 +
+
+
+
+
物流查询API
+
+
+ +
+ + 用于查询物流信息,快递100申请 +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/tplMsg.php b/source/application/store/view/setting/tplMsg.php new file mode 100644 index 0000000..f9b6b5d --- /dev/null +++ b/source/application/store/view/setting/tplMsg.php @@ -0,0 +1,145 @@ +
+
+
+
+
+
+
+
+
+

+ 模板消息仅用于微信小程序向用户发送服务通知,因微信限制,每笔支付订单可允许向用户在7天内推送最多3条模板消息。 + 如何获取模板消息ID? +

+
+
+
+
支付成功通知
+
+
+ +
+ + +
+
+
+ +
+ +
+ 模板编号AT0009,关键词 (订单编号、支付时间、订单金额、商品名称) +
+
+
+ +
+
订单发货通知
+
+
+ +
+ + +
+
+
+ +
+ + 模板编号AT0007,关键词 (订单编号、商品信息、收货人、收货地址、物流公司、物流单号) +
+
+ +
+
售后状态通知
+
+
+ +
+ + +
+
+
+ +
+ + 模板编号AT0553,关键词 (售后类型、状态、订单号、商品名称、申请时间、申请原因) +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/setting/trade.php b/source/application/store/view/setting/trade.php new file mode 100644 index 0000000..d86281d --- /dev/null +++ b/source/application/store/view/setting/trade.php @@ -0,0 +1,112 @@ +
+
+
+
+
+
+
+
+
订单流程设置
+
+
+ +
+
+ +
+ +
+ 订单下单未付款,n天后自动关闭,设置0天不自动关闭 +
+
+
+
+ +
+
+ +
+ +
+ 如果在期间未确认收货,系统自动完成收货,设置0天不自动收货 +
+
+
+
+ +
+
+ +
+ +
+ 订单完成后 ,用户在n天内可以发起售后申请,设置0天不允许申请售后 +
+
+
+ +
+
运费设置
+
+
+ +
+
+ +
+ 订单中的商品有多个运费模板时,将每个商品的运费之和订为订单总运费 +
+
+
+ +
+ 订单中的商品有多个运费模板时,取订单中运费最少的商品的运费计为订单总运费 +
+
+
+ +
+ 订单中的商品有多个运费模板时,取订单中运费最多的商品的运费计为订单总运费 +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/shop/add.php b/source/application/store/view/shop/add.php new file mode 100644 index 0000000..24a7112 --- /dev/null +++ b/source/application/store/view/shop/add.php @@ -0,0 +1,195 @@ +
+
+
+
+
+
+
+
+
添加门店
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 例如:8:30-17:30 +
+
+
+ +
+
+ + + +
+
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + +{{include file="layouts/_template/file_library" /}} + + + + diff --git a/source/application/store/view/shop/clerk/add.php b/source/application/store/view/shop/clerk/add.php new file mode 100644 index 0000000..6a14314 --- /dev/null +++ b/source/application/store/view/shop/clerk/add.php @@ -0,0 +1,123 @@ +
+
+
+
+
+
+
+
+
添加店员
+
+
+ +
+
+ +
+
+
+ 选择后不可更改 +
+
+
+
+
+ +
+ +
+ 请选择店员所属的门店,用于核销订单 +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/source/application/store/view/shop/clerk/edit.php b/source/application/store/view/shop/clerk/edit.php new file mode 100644 index 0000000..2a0f1d3 --- /dev/null +++ b/source/application/store/view/shop/clerk/edit.php @@ -0,0 +1,110 @@ +
+
+
+
+
+
+
+
+
编辑店员
+
+
+ +
+ +
+ 请选择店员所属的门店,用于核销订单 +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/source/application/store/view/shop/clerk/index.php b/source/application/store/view/shop/clerk/index.php new file mode 100644 index 0000000..3a2def1 --- /dev/null +++ b/source/application/store/view/shop/clerk/index.php @@ -0,0 +1,134 @@ +
+
+
+
+
+
店员列表
+
+
+ +
+
+ +
+
+ + + +
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + +
店员ID微信头像微信昵称所属门店店员姓名店员手机号状态添加时间操作
+ + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/shop/edit.php b/source/application/store/view/shop/edit.php new file mode 100644 index 0000000..2d68bfd --- /dev/null +++ b/source/application/store/view/shop/edit.php @@ -0,0 +1,215 @@ +
+
+
+
+
+
+
+
+
编辑门店
+
+
+ +
+ +
+
+
+ +
+
+
+ +
+
+ + + + + +
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+ + 例如:8:30-17:30 +
+
+
+ +
+ +
+
+
+ +
+
+ + + +
+
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ + 数字越小越靠前 +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + + + +{{include file="layouts/_template/file_library" /}} + + + + diff --git a/source/application/store/view/shop/getpoint.php b/source/application/store/view/shop/getpoint.php new file mode 100644 index 0000000..3d531ab --- /dev/null +++ b/source/application/store/view/shop/getpoint.php @@ -0,0 +1,1179 @@ + + + + + 腾讯地图开放API - 轻快小巧,简单易用! + + + + + + + + + + + +
+
+ +
+
当前坐标:
+ +
当前地址:
+ +
+
+
+
+
+ 北京市[更换城市]当前缩放等级:10 +
+

热门城市X

+
+ 北京 + 深圳 + 上海 + 香港 + 澳门 + 广州 + 天津 + 重庆 + 杭州 + 成都 + 武汉 + 青岛 +
+

全国城市

+
+
直辖市
+
+ 北京 + 上海 + 天津 + 重庆 +
+
+
+
+
内蒙古
+
+ 呼和浩特 + 包头 + 乌海 + 赤峰 + 通辽 + 鄂尔多斯 + 呼伦贝尔 + 巴彦淖尔 + 乌兰察布 + 兴安盟 + 锡林郭勒盟 + 阿拉善盟 +
+
+
+
+
山西
+
+ 太原 + 大同 + 阳泉 + 长治 + 晋城 + 朔州 + 晋中 + 运城 + 忻州 + 临汾 + 吕梁 + +
+
+
+
+
陕西
+
+ 西安 + 铜川 + 宝鸡 + 咸阳 + 渭南 + 延安 + 汉中 + 榆林 + 安康 + 商洛 +
+
+
+
+
河北
+
+ 石家庄 + 唐山 + 秦皇岛 + 邯郸 + 邢台 + 保定 + 张家口 + 承德 + 沧州 + 廊坊 + 衡水 +
+
+
+
+
辽宁
+
+ 沈阳 + 大连 + 鞍山 + 抚顺 + 本溪 + 丹东 + 锦州 + 营口 + 阜新 + 辽阳 + 盘锦 + 铁岭 + 朝阳 + 葫芦岛 +
+
+
+
+
吉林
+
+ 长春 + 吉林市 + 四平 + 辽源 + 通化 + 白山 + 松原 + 白城 + 延边 +
+
+
+
+
黑龙江
+
+ 哈尔滨 + 齐齐哈尔 + 鸡西 + 鹤岗 + 双鸭山 + 大庆 + 伊春 + 牡丹江 + 佳木斯 + 七台河 + 黑河 + 绥化 + 大兴安岭 +
+
+
+
+
江苏
+
+ 南京 + 无锡 + 徐州 + 常州 + 苏州 + 南通 + 连云港 + 淮安 + 盐城 + 扬州 + 镇江 + 泰州 + 宿迁 +
+
+
+
+
安徽
+
+ 合肥 + 蚌埠 + 芜湖 + 淮南 + 马鞍山 + 淮北 + 铜陵 + 安庆 + 黄山 + 阜阳 + 宿州 + 滁州 + 六安 + 宣城 + 池州 + 亳州 +
+
+
+
+
山东
+
+ 济南 + 青岛 + 淄博 + 枣庄 + 东营 + 潍坊 + 烟台 + 威海 + 济宁 + 泰安 + 日照 + 临沂 + 德州 + 聊城 + 滨州 + 菏泽 +
+
+
+
+
浙江
+
+ 杭州 + 宁波 + 温州 + 嘉兴 + 绍兴 + 金华 + 衢州 + 舟山 + 台州 + 丽水 + 湖州 +
+
+
+
+
江西
+
+ 南昌 + 景德镇 + 萍乡 + 九江 + 新余 + 鹰潭 + 赣州 + 吉安 + 宜春 + 抚州 + 上饶 +
+
+
+
+
福建
+
+ 福州 + 厦门 + 莆田 + 三明 + 泉州 + 漳州 + 南平 + 龙岩 + 宁德 +
+
+
+
+
湖南
+
+ 长沙 + 株洲 + 湘潭 + 衡阳 + 邵阳 + 岳阳 + 常德 + 张家界 + 益阳 + 郴州 + 永州 + 怀化 + 娄底 + 湘西 +
+
+
+
+
湖北
+
+ 武汉 + 黄石 + 襄阳 + 十堰 + 宜昌 + 荆门 + 鄂州 + 孝感 + 荆州 + 黄冈 + 咸宁 + 随州 + 恩施 + 潜江 + 仙桃 + 天门 + 神农架 +
+
+
+
+
河南
+
+ 郑州 + 开封 + 洛阳 + 平顶山 + 焦作 + 鹤壁 + 新乡 + 安阳 + 濮阳 + 许昌 + 漯河 + 三门峡 + 南阳 + 商丘 + 信阳 + 周口 + 驻马店 + 济源 +
+
+
+
+
海南
+
+ 海口 + 三亚 + 三沙 + 儋州 + 五指山 + 文昌 + 琼海 + 万宁 + 东方 + 定安 + 屯昌 + 澄迈 + 临高 + 白沙 + 昌江 + 乐东 + 陵水 + 保亭 + 琼中 +
+
+
+
+
广东
+
+ 广州 + 深圳 + 珠海 + 汕头 + 韶关 + 佛山 + 江门 + 湛江 + 茂名 + 肇庆 + 惠州 + 梅州 + 汕尾 + 河源 + 阳江 + 清远 + 东莞 + 中山 + 潮州 + 揭阳 + 云浮 +
+
+
+
+
广西
+
+ 南宁 + 柳州 + 桂林 + 梧州 + 北海 + 防城港 + 钦州 + 贵港 + 玉林 + 百色 + 贺州 + 河池 + 来宾 + 崇左 +
+
+
+
+
贵州
+
+ 贵阳 + 遵义 + 安顺 + 铜仁 + 毕节 + 六盘水 + 黔西南 + 黔东南 + 黔南 +
+
+
+
+
四川
+
+ 成都 + 自贡 + 攀枝花 + 泸州 + 德阳 + 绵阳 + 广元 + 遂宁 + 内江 + 乐山 + 南充 + 宜宾 + 广安 + 达州 + 眉山 + 雅安 + 巴中 + 资阳 + 阿坝 + 甘孜 + 凉山 +
+
+
+
+
云南
+
+ 昆明 + 保山 + 昭通 + 丽江 + 普洱 + 临沧 + 曲靖 + 玉溪 + 文山 + 西双版纳 + 楚雄 + 红河 + 德宏 + 大理 + 怒江 + 迪庆 +
+
+
+
+
甘肃
+
+ 兰州 + 嘉峪关 + 金昌 + 白银 + 天水 + 酒泉 + 张掖 + 武威 + 定西 + 陇南 + 平凉 + 庆阳 + 临夏 + 甘南 +
+
+
+
+
宁夏
+
+ 银川 + 石嘴山 + 吴忠 + 固原 + 中卫 +
+
+
+
+
青海
+
+ 西宁 + 玉树 + 果洛 + 海东 + 海西 + 黄南 + 海北 + 海南 +
+
+
+
+
西藏
+
+ 拉萨 + 那曲 + 昌都 + 山南 + 日喀则 + 阿里 + 林芝 +
+
+
+
+
新疆
+
+ 乌鲁木齐 + 克拉玛依 + 吐鲁番 + 哈密 + 博尔塔拉 + 巴音郭楞 + 克孜勒苏 + 和田 + 阿克苏 + 喀什 + 塔城 + 伊犁 + 昌吉 + 阿勒泰 + 石河子 + 阿拉尔 + 图木舒克 + 五家渠 + 北屯 + 铁门关 + 双河 + 可克达拉 + 昆玉 +
+
+
+
+
+ +
+
+
+

功能简介:

+ +

1、支持地址 精确/模糊 查询;

+ +

2、支持POI点坐标显示;

+ +

3、坐标鼠标跟随显示;

+ +

使用说明:

+ +

在搜索框搜索关键词后,地图上会显示相应poi点,同时左侧显示对应该点的信息,点击某点或某信息,右上角会显示相应该点的坐标和地址。

+
+ +
+
+
+
+
+
+ + + + diff --git a/source/application/store/view/shop/index.php b/source/application/store/view/shop/index.php new file mode 100644 index 0000000..027cb68 --- /dev/null +++ b/source/application/store/view/shop/index.php @@ -0,0 +1,113 @@ +
+
+
+
+
+
门店列表
+
+
+ +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
门店ID门店名称门店logo营业时间联系人联系电话门店地址自提核销门店状态创建时间操作
+ + + + + + + + + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/shop/order/index.php b/source/application/store/view/shop/order/index.php new file mode 100644 index 0000000..2e9c0fc --- /dev/null +++ b/source/application/store/view/shop/order/index.php @@ -0,0 +1,102 @@ + +
+
+
+
+
+
核销记录列表
+
+
+ +
+
+ +
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + +
ID核销门店核销员订单号订单类型核销时间
+ + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/statistics/data/index.php b/source/application/store/view/statistics/data/index.php new file mode 100644 index 0000000..0b2a69b --- /dev/null +++ b/source/application/store/view/statistics/data/index.php @@ -0,0 +1,314 @@ + +
+ +
+
+
+
+
数据概况
+
+ +
+ + +
+ +
+
+
+ 7天 +
+
+ 30天 +
+
+ 清空 +
+
+
+
+
+
+
+
+
+
+ +
+
+
用户数量
+
{{ survey.values.user_total }}
+
+
+
+
+
+
+ +
+
+
付款订单数
+
{{ survey.values.order_total }}
+
+
+
+
+
+
+ +
+
+
商品数量
+
{{ survey.values.goods_total }}
+
+
+
+
+
+
+ +
+
+
消费人数
+
{{ survey.values.consume_users }}
+
+
+
+
+
+
+ +
+
+
付款订单总额
+
{{ survey.values.order_total_money }}
+
+
+
+
+
+
+ +
+
+
用户充值总额
+
{{ survey.values.recharge_total }}
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
近七日交易走势
+
+
+
+
+
+
+
+ +
+
+
+
+
商品销售榜
+
+
+ + + + + + + + + + + + + + + + + +
排名商品销量销售额
+
+ +
+ {{ index + 1 }} +
+

{{ item.goods_name }}

+
{{ item.total_sales_num }}{{ item.sales_volume }}
+
+
+
+
+
+
+
用户消费榜
+
+
+ + + + + + + + + + + + + + + +
排名用户昵称实际消费金额
+
+ +
+ {{ index + 1 }} +
+

{{ item.nickName }}

+
{{ item.expend_money }}
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/source/application/store/view/store/role/add.php b/source/application/store/view/store/role/add.php new file mode 100644 index 0000000..bfed640 --- /dev/null +++ b/source/application/store/view/store/role/add.php @@ -0,0 +1,105 @@ + +
+
+
+
+
+
+
+
+
添加角色
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/store/role/edit.php b/source/application/store/view/store/role/edit.php new file mode 100644 index 0000000..d400ae9 --- /dev/null +++ b/source/application/store/view/store/role/edit.php @@ -0,0 +1,108 @@ + +
+
+
+
+
+
+
+
+
编辑角色
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/store/role/index.php b/source/application/store/view/store/role/index.php new file mode 100644 index 0000000..319f147 --- /dev/null +++ b/source/application/store/view/store/role/index.php @@ -0,0 +1,80 @@ +
+
+
+
+
+
角色列表
+
+
+ +
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
角色ID角色名称排序添加时间操作
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/store/user/add.php b/source/application/store/view/store/user/add.php new file mode 100644 index 0000000..afeb859 --- /dev/null +++ b/source/application/store/view/store/user/add.php @@ -0,0 +1,75 @@ +
+
+
+
+
+
+
+
+
添加管理员
+
+
+ +
+ +
+
+
+ +
+ +
+ 注:支持多选 +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/store/user/edit.php b/source/application/store/view/store/user/edit.php new file mode 100644 index 0000000..84fed05 --- /dev/null +++ b/source/application/store/view/store/user/edit.php @@ -0,0 +1,77 @@ +
+
+
+
+
+
+
+
+
编辑管理员
+
+
+ +
+ +
+
+
+ +
+ +
+ 注:支持多选 +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/store/user/index.php b/source/application/store/view/store/user/index.php new file mode 100644 index 0000000..296ac10 --- /dev/null +++ b/source/application/store/view/store/user/index.php @@ -0,0 +1,89 @@ +
+
+
+
+
+
管理员列表
+
+
+ +
+
+ + + +
+
+
+ + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + +
管理员ID用户名姓名添加时间操作
+
+ + + + 编辑 + + + + + 删除 + + + +
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/store/user/renew.php b/source/application/store/view/store/user/renew.php new file mode 100644 index 0000000..30f51cf --- /dev/null +++ b/source/application/store/view/store/user/renew.php @@ -0,0 +1,55 @@ +
+
+
+
+
+
+
+
+
管理员设置
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/user/balance/log.php b/source/application/store/view/user/balance/log.php new file mode 100644 index 0000000..8b9b486 --- /dev/null +++ b/source/application/store/view/user/balance/log.php @@ -0,0 +1,116 @@ +
+
+
+
+
+
余额明细
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
ID微信头像微信昵称余额变动场景变动金额描述/说明管理员备注创建时间
+ + + + +

+ +
+ + + 0 ? '+' : '' ?> +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/user/grade/add.php b/source/application/store/view/user/grade/add.php new file mode 100644 index 0000000..7688c50 --- /dev/null +++ b/source/application/store/view/user/grade/add.php @@ -0,0 +1,100 @@ +
+
+
+
+
+
+
+
+
添加会员等级
+
+
+ +
+ + 例如:大众会员、黄金会员、铂金会员、钻石会员 +
+
+
+ +
+
+ +
+
+ 会员等级的权重,数字越大 等级越高 +
+
+
+
+ +
+
+ 实际消费金额满 + + +
+
+ 用户的实际消费金额满n元后,自动升级 +
+
+
+
+ +
+
+ 折扣率 + + +
+
+ 折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/user/grade/edit.php b/source/application/store/view/user/grade/edit.php new file mode 100644 index 0000000..9887665 --- /dev/null +++ b/source/application/store/view/user/grade/edit.php @@ -0,0 +1,104 @@ +
+
+
+
+
+
+
+
+
编辑会员等级
+
+
+ +
+ + 例如:大众会员、黄金会员、铂金会员、钻石会员 +
+
+
+ +
+
+ +
+
+ 会员等级的权重,数字越大 等级越高 +
+
+
+
+ +
+
+ 实际消费金额满 + + +
+
+ 用户的实际消费金额满n元后,自动升级 +
+
+
+
+ +
+
+ 折扣率 + + +
+
+ 折扣率范围0-10,9.5代表9.5折,0代表不折扣 +
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/user/grade/index.php b/source/application/store/view/user/grade/index.php new file mode 100644 index 0000000..44b7b10 --- /dev/null +++ b/source/application/store/view/user/grade/index.php @@ -0,0 +1,100 @@ +
+
+
+
+
+
会员等级列表
+
+
+ +
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + +
等级ID等级名称等级权重升级条件等级权益状态创建时间操作
+ 消费满 + + + + + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/user/index.php b/source/application/store/view/user/index.php new file mode 100644 index 0000000..7319937 --- /dev/null +++ b/source/application/store/view/user/index.php @@ -0,0 +1,412 @@ +
+
+
+
+
+
用户列表
+
+
+ +
+
+ +
+
+
+ get('grade'); ?> + +
+
+ get('gender'); ?> + +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + + + +
用户ID微信头像微信昵称用户余额可用积分会员等级实际消费金额性别国家省份城市注册时间操作
+ + + + + + +
+ + + + 充值 + + + + + + 会员等级 + + + + + 删除 + + +
+ + +
+
+
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + + + + + + + + diff --git a/source/application/store/view/user/recharge/order.php b/source/application/store/view/user/recharge/order.php new file mode 100644 index 0000000..08cad4c --- /dev/null +++ b/source/application/store/view/user/recharge/order.php @@ -0,0 +1,146 @@ +
+
+
+
+
+
余额充值记录
+
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + + + + + + + +
订单ID微信头像微信昵称订单号充值方式套餐名称支付金额赠送金额支付状态付款时间创建时间
+ + + + +

+ +
+ + + + + +
暂无记录
+
+
+
render() ?>
+
+
总记录:total() ?>
+
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/wxapp/custom/index.php b/source/application/store/view/wxapp/custom/index.php new file mode 100644 index 0000000..5817c5f --- /dev/null +++ b/source/application/store/view/wxapp/custom/index.php @@ -0,0 +1,78 @@ +
+
+
+
+
+
帮助中心
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + +
标题内容排序添加时间操作
+

+
+

+
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/wxapp/help/add.php b/source/application/store/view/wxapp/help/add.php new file mode 100644 index 0000000..e01f967 --- /dev/null +++ b/source/application/store/view/wxapp/help/add.php @@ -0,0 +1,55 @@ +
+
+
+
+
+
+
+
+
添加帮助
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/wxapp/help/edit.php b/source/application/store/view/wxapp/help/edit.php new file mode 100644 index 0000000..eb5c4cf --- /dev/null +++ b/source/application/store/view/wxapp/help/edit.php @@ -0,0 +1,56 @@ +
+
+
+
+
+
+
+
+
编辑帮助
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/wxapp/help/index.php b/source/application/store/view/wxapp/help/index.php new file mode 100644 index 0000000..1b3ea75 --- /dev/null +++ b/source/application/store/view/wxapp/help/index.php @@ -0,0 +1,83 @@ +
+
+
+
+
+
帮助中心
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + isEmpty()): foreach ($list as $item): ?> + + + + + + + + + + + + + +
标题内容排序添加时间操作
+

+
+ +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/wxapp/page/category.php b/source/application/store/view/wxapp/page/category.php new file mode 100644 index 0000000..bcb302a --- /dev/null +++ b/source/application/store/view/wxapp/page/category.php @@ -0,0 +1,102 @@ + +
+
+
+
+
+
+
+
+
分类页模板
+
+
+
+ +
+
+
+ +
+ + + +
+ 分类图尺寸:宽750像素 高度不限 + + 分类图尺寸:宽188像素 高度不限 + + 分类图尺寸:宽150像素 高150像素 + +
+
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ diff --git a/source/application/store/view/wxapp/page/edit.php b/source/application/store/view/wxapp/page/edit.php new file mode 100644 index 0000000..b10d7ed --- /dev/null +++ b/source/application/store/view/wxapp/page/edit.php @@ -0,0 +1,2247 @@ + + +
+
+
+ +
+ +
+ + +
+ +
+
+ + +
+ +
+

{{ diyData.page.params.title }}

+
+ +
+ + + +
+
+ + +
+ + +
+
{{ diyData.page.name }}
+
+
+ +
+ +
+ 页面名称仅用于后台查找 +
+
+
+
+ +
+ +
+ 小程序端顶部显示的标题 +
+
+
+
+ +
+ +
+ 小程序端转发时显示的标题 +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+ +
+
+

1. 设计完成后点击"保存页面",在小程序端页面下拉刷新即可看到效果。

+

2. 如需填写链接地址请参考页面链接

+
+
+
+
+
+ + +{{include file="layouts/_template/file_library" /}} + + + + + + + + + \ No newline at end of file diff --git a/source/application/store/view/wxapp/page/index.php b/source/application/store/view/wxapp/page/index.php new file mode 100644 index 0000000..ad4b48e --- /dev/null +++ b/source/application/store/view/wxapp/page/index.php @@ -0,0 +1,113 @@ +
+
+
+
+
+
页面设计
+
+
+
+
+
+ + + +
+
+
+
+ + + + + + + + + + + + + isEmpty()): foreach ($list + + as $item): ?> + + + + + + + + + + + + + + +
页面ID页面名称页面类型添加时间更新时间操作
+ + + + 商城首页 + + 自定义页 + + + +
暂无记录
+
+
+
+
+
+
+ + diff --git a/source/application/store/view/wxapp/page/links.php b/source/application/store/view/wxapp/page/links.php new file mode 100644 index 0000000..260c2e5 --- /dev/null +++ b/source/application/store/view/wxapp/page/links.php @@ -0,0 +1,439 @@ +
+
+
+
+
+
+
页面链接
+
+ +
+
+
+
+
+ diff --git a/source/application/store/view/wxapp/setting.php b/source/application/store/view/wxapp/setting.php new file mode 100644 index 0000000..7426e4a --- /dev/null +++ b/source/application/store/view/wxapp/setting.php @@ -0,0 +1,93 @@ +
+
+
+
+
+
+
+
+
小程序设置
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
微信支付设置
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 使用文本编辑器打开apiclient_cert.pem文件,将文件的全部内容复制进来 +
+
+
+ +
+ + 使用文本编辑器打开apiclient_key.pem文件,将文件的全部内容复制进来 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ diff --git a/source/application/tags.php b/source/application/tags.php new file mode 100644 index 0000000..8594ab7 --- /dev/null +++ b/source/application/tags.php @@ -0,0 +1,69 @@ + +// +---------------------------------------------------------------------- + +// 应用行为扩展定义文件 +return [ + // 应用初始化 + 'app_init' => [], + // 应用开始 + 'app_begin' => [ + 'app\\common\\behavior\\App' + ], + // 模块初始化 + 'module_init' => [], + // 操作开始执行 + 'action_begin' => [], + // 视图内容过滤 + 'view_filter' => [], + // 日志写入 + 'log_write' => [], + // 应用结束 + 'app_end' => [], + + // 订单行为管理 + 'order' => [ + // 普通订单 + 'app\\task\\behavior\\Order', + // 秒杀订单 + 'app\\task\\behavior\\sharp\\Order', + ], + + // 优惠券行为管理 + 'UserCoupon' => [ + 'app\\task\\behavior\\UserCoupon' + ], + + // 分销商订单行为管理 + 'DealerOrder' => [ + 'app\\task\\behavior\\DealerOrder' + ], + + // 拼团订单行为管理 + 'sharing_order' => [ + 'app\\task\\behavior\\sharing\\Order' + ], + + // 拼团拼单行为管理 + 'sharing_active' => [ + 'app\\task\\behavior\\sharing\\Active' + ], + + // 会员等级行为管理 + 'user_grade' => [ + 'app\\task\\behavior\\user\\Grade' + ], + + // 砍价任务行为管理 + 'bargain_task' => [ + 'app\\task\\behavior\\bargain\\Task' + ], + +]; diff --git a/source/application/task/behavior/DealerOrder.php b/source/application/task/behavior/DealerOrder.php new file mode 100644 index 0000000..c56db78 --- /dev/null +++ b/source/application/task/behavior/DealerOrder.php @@ -0,0 +1,96 @@ +model = $model; + if (!Cache::has('__task_space__DealerOrder')) { + $this->model->startTrans(); + try { + // 发放分销订单佣金 + $this->grantMoney(); + $this->model->commit(); + } catch (\Exception $e) { + $this->model->rollback(); + } + Cache::set('__task_space__DealerOrder', time(), 3600); + } + return true; + } + + /** + * 发放分销订单佣金 + * @return bool + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function grantMoney() + { + // 获取未结算佣金的订单列表 + $list = $this->model->getUnSettledList(); + if ($list->isEmpty()) return false; + // 整理id集 + $invalidIds = []; + $grantIds = []; + // 发放分销订单佣金 + foreach ($list->toArray() as $item) { + // 已失效的订单 + if ($item['order_master']['order_status']['value'] == 20) { + $invalidIds[] = $item['id']; + } + // 已完成的订单 + if ($item['order_master']['order_status']['value'] == 30) { + $grantIds[] = $item['id']; + DealerOrderModel::grantMoney($item['order_master'], $item['order_type']['value']); + } + } + // 标记已失效的订单 + $this->model->setInvalid($invalidIds); + // 记录日志 + $this->dologs('invalidIds', ['Ids' => $invalidIds]); + $this->dologs('grantMoney', ['Ids' => $grantIds]); + return true; + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior DealerOrder --' . $method; + foreach ($params as $key => $val) { + $value .= ' --' . $key . ' ' . (is_array($val) ? json_encode($val) : $val); + } + return log_write($value); + } + +} \ No newline at end of file diff --git a/source/application/task/behavior/Order.php b/source/application/task/behavior/Order.php new file mode 100644 index 0000000..46aacac --- /dev/null +++ b/source/application/task/behavior/Order.php @@ -0,0 +1,212 @@ +model = $model; + $this->wxappId = $model::$wxapp_id; + // 普通订单行为管理 + $this->master(); + return true; + } + + /** + * 普通订单行为管理 + * @return bool + */ + private function master() + { + $key = "__task_space__order__{$this->wxappId}"; + if (Cache::has($key)) return true; + // 获取商城交易设置 + $this->service = new OrderService; + $config = Setting::getItem('trade'); + $this->model->transaction(function () use ($config) { + // 未支付订单自动关闭 + $this->close($config['order']['close_days']); + // 已发货订单自动确认收货 + $this->receive($config['order']['receive_days']); + // 已完成订单结算 + $this->settled($config['order']['refund_days']); + }); + Cache::set($key, time(), 3600); + return true; + } + + /** + * 未支付订单自动关闭 + * @param $closeDays + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function close($closeDays) + { + // 取消n天以前的的未付款订单 + if ($closeDays < 1) return false; + // 截止时间 + $deadlineTime = time() - ((int)$closeDays * 86400); + // 执行自动关闭 + $this->service->close($deadlineTime); + // 记录日志 + $this->dologs('close', [ + 'close_days' => (int)$closeDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($this->service->getCloseOrderIds()), + ]); + return true; + } + + /** + * 已发货订单自动确认收货 + * @param $receiveDays + * @return bool|false|int + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function receive($receiveDays) + { + // 截止时间 + if ($receiveDays < 1) return false; + $deadlineTime = time() - ((int)$receiveDays * 86400); + // 条件 + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 20, + 'receipt_status' => 10, + 'delivery_time' => ['<=', $deadlineTime] + ]; + // 订单id集 + $orderIds = $this->model->where($filter)->column('order_id'); + if (!empty($orderIds)) { + // 更新订单收货状态 + $this->model->onBatchUpdate($orderIds, [ + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); + // 批量处理已完成的订单 + $this->onReceiveCompleted($orderIds); + } + // 记录日志 + $this->dologs('receive', [ + 'receive_days' => (int)$receiveDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($orderIds), + ]); + return true; + } + + /** + * 已完成订单结算 + * @param $refundDays + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function settled($refundDays) + { + // 获取已完成的订单(未累积用户实际消费金额) + // 条件1:订单状态:已完成 + // 条件2:超出售后期限 + // 条件3:is_settled 为 0 + // 截止时间 + $deadlineTime = time() - ((int)$refundDays * 86400); + // 查询条件 + $filter = [ + 'order_status' => 30, + 'receipt_time' => ['<=', $deadlineTime], // 此处使用<=,用于兼容自动确认收货后 + 'is_settled' => 0 + ]; + // 查询订单列表 + $orderList = $this->model->getList($filter, [ + 'goods' => ['refund'], // 用于计算售后退款金额 + ]); + // 订单id集 + $orderIds = helper::getArrayColumn($orderList, 'order_id'); + // 订单结算服务 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::MASTER); + !empty($orderIds) && $OrderCompleteService->settled($orderList); + // 记录日志 + $this->dologs('settled', [ + 'refund_days' => (int)$refundDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($orderIds), + ]); + } + + /** + * 批量处理已完成的订单 + * @param $orderIds + * @return bool + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function onReceiveCompleted($orderIds) + { + // 获取已完成的订单列表 + $list = $this->model->getList(['order_id' => ['in', $orderIds]], [ + 'goods' => ['refund'], // 用于发放分销佣金 + 'user', 'address', 'goods', 'express', // 用于同步微信好物圈 + ]); + if ($list->isEmpty()) return false; + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::MASTER); + $OrderCompleteService->complete($list, $this->wxappId); + return true; + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior Order --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} diff --git a/source/application/task/behavior/UserCoupon.php b/source/application/task/behavior/UserCoupon.php new file mode 100644 index 0000000..9037bc5 --- /dev/null +++ b/source/application/task/behavior/UserCoupon.php @@ -0,0 +1,67 @@ +model = $model; + if (!Cache::has('__task_space__UserCoupon')) { + // 设置优惠券过期状态 + $this->setExpired(); + Cache::set('__task_space__UserCoupon', time(), 3600); + } + return true; + } + + /** + * 设置优惠券过期状态 + * @return false|int + */ + private function setExpired() + { + // 获取已过期的优惠券ID集 + $couponIds = $this->model->getExpiredCouponIds(); + // 记录日志 + $this->dologs('setExpired', [ + 'couponIds' => json_encode($couponIds), + ]); + // 更新已过期状态 + return $this->model->setIsExpire($couponIds); + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'UserCoupon --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} diff --git a/source/application/task/behavior/bargain/Task.php b/source/application/task/behavior/bargain/Task.php new file mode 100644 index 0000000..914f0e3 --- /dev/null +++ b/source/application/task/behavior/bargain/Task.php @@ -0,0 +1,79 @@ +model = $model; + if (!$model::$wxapp_id) { + return false; + } + if (!Cache::has('__task_space__bargain_task__')) { + // 将已过期的砍价任务标记为已结束 + $this->onSetIsEnd(); + Cache::set('__task_space__bargain_task__', time(), 10); + } + return true; + } + + /** + * 将已过期的砍价任务标记为已结束 + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function onSetIsEnd() + { + // 获取已过期但未结束的砍价任务 + $list = $this->model->getEndList(); + $taskIds = helper::getArrayColumn($list, 'task_id'); + // 将砍价任务标记为已结束(批量) + !empty($taskIds) && $this->model->setIsEnd($taskIds); + // 记录日志 + $this->dologs('close', [ + 'orderIds' => json_encode($taskIds), + ]); + return true; + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior bargain Task --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} \ No newline at end of file diff --git a/source/application/task/behavior/sharing/Active.php b/source/application/task/behavior/sharing/Active.php new file mode 100644 index 0000000..d1bca21 --- /dev/null +++ b/source/application/task/behavior/sharing/Active.php @@ -0,0 +1,136 @@ +model = $model; + if (!$model::$wxapp_id) { + return false; + } + if (!Cache::has('__task_space__sharing_active__' . $model::$wxapp_id)) { + try { + // 拼团设置 + $config = Setting::getItem('basic'); + // 已过期的拼单更新状态(拼单失败) + $this->onUpdateActiveEnd(); + // 更新拼团失败的订单并退款 + if ($config['auto_refund'] == true) { + $this->onOrderRefund(); + } + } catch (\Exception $e) { + } + Cache::set('__task_space__sharing_active__' . $model::$wxapp_id, time(), 10); + } + return true; + } + + /** + * 已过期的拼单更新状态 + * @return false|int + * @throws \app\common\exception\BaseException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function onUpdateActiveEnd() + { + // 获取已过期的拼单列表 + $list = $this->model->getEndedList(); + // 拼单ID集 + $activeIds = []; + foreach ($list as $item) { + $activeIds[] = $item['active_id']; + } + // 记录日志 + $this->dologs('onSetActiveEnd', [ + 'activeIds' => json_encode($activeIds), + ]); + // 发送拼团失败模板消息 + $Message = new Message; + foreach ($list as $item) { + $Message->sharingActive($item, '拼团失败'); + } + // 更新已过期状态 + return $this->model->updateEndedStatus($activeIds); + } + + /** + * 更新拼团失败的订单并退款 + * @return bool + * @throws \app\common\exception\BaseException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function onOrderRefund() + { + // 实例化拼单订单模型 + $model = new OrderModel; + // 每次最多处理的个数,防止运行太久 + // 及微信申请退款API请求频率限制:150qps + $maxLimit = 100; + // 获取拼团失败的订单集 + $orderList = $model->getFailedOrderList($maxLimit); + // 整理拼团订单id + $orderIds = []; + foreach ($orderList as $order) { + $orderIds[] = $order['order_id']; + } + // 记录日志 + $this->dologs('onOrderRefund', [ + 'orderIds' => json_encode($orderIds), + ]); + if (empty($orderIds)) { + return false; + } + // 更新拼团失败的订单并退款 + if ($model->updateFailedStatus($orderList)) { + return true; + } + // 存在退款出错的订单记录日志 + $this->dologs('onOrderRefund', [ + 'error: ' => $model->getError() + ]); + return false; + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior sharing Active --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} diff --git a/source/application/task/behavior/sharing/Order.php b/source/application/task/behavior/sharing/Order.php new file mode 100644 index 0000000..585996a --- /dev/null +++ b/source/application/task/behavior/sharing/Order.php @@ -0,0 +1,223 @@ +model = $model; + $this->wxappId = $model::$wxapp_id; + if (!Cache::has("__task_space__sharing_order__{$this->wxappId}")) { + // 获取商城交易设置 + $config = SettingModel::getItem('trade'); + $this->model->transaction(function () use ($config) { + // 未支付订单自动关闭 + $this->close($config['order']['close_days']); + // 已发货订单自动确认收货 + $this->receive($config['order']['receive_days']); + // 已完成订单结算 + $this->settled($config['order']['refund_days']); + }); + Cache::set("__task_space__sharing_order__{$this->wxappId}", time(), 3600); + } + return true; + } + + /** + * 未支付订单自动关闭 + * @param $closeDays + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function close($closeDays) + { + // 取消n天以前的的未付款订单 + if ($closeDays < 1) { + return false; + } + // 截止时间 + $deadlineTime = time() - ((int)$closeDays * 86400); + // 条件 + $filter = [ + 'pay_status' => 10, + 'order_status' => 10, + 'create_time' => ['<=', $deadlineTime] + ]; + // 查询截止时间未支付的订单 + $list = $this->model->getList($filter, ['goods', 'user']); + $orderIds = helper::getArrayColumn($list, 'order_id'); + // 取消订单事件 + if (!empty($orderIds)) { + $OrderGoodsModel = new OrderGoodsModel; + foreach ($list as &$order) { + // 回退商品库存 + $OrderGoodsModel->backGoodsStock($order['goods'], false); + // 回退用户优惠券 + $order['coupon_id'] > 0 && UserCouponModel::setIsUse($order['coupon_id'], false); + // 回退用户积分 + $describe = "订单取消:{$order['order_no']}"; + $order['points_num'] > 0 && $order->user->setIncPoints($order['points_num'], $describe); + } + // 批量更新订单状态为已取消 + $this->model->onBatchUpdate($orderIds, ['order_status' => 20]); + } + // 记录日志 + $this->dologs('close', [ + 'close_days' => (int)$closeDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($orderIds), + ]); + return true; + } + + /** + * 已发货订单自动确认收货 + * @param $receiveDays + * @return bool|false|int + * @throws \think\Exception + * @throws \think\exception\DbException + */ + private function receive($receiveDays) + { + if ($receiveDays < 1) { + return false; + } + // 截止时间 + $deadlineTime = time() - ((int)$receiveDays * 86400); + // 条件 + $filter = [ + 'pay_status' => 20, + 'delivery_status' => 20, + 'receipt_status' => 10, + 'delivery_time' => ['<=', $deadlineTime] + ]; + // 订单id集 + $orderIds = $this->model->where($filter)->column('order_id'); + if (!empty($orderIds)) { + // 更新订单收货状态 + $this->model->onBatchUpdate($orderIds, [ + 'receipt_status' => 20, + 'receipt_time' => time(), + 'order_status' => 30 + ]); + // 批量处理已完成的订单 + $this->onReceiveCompleted($orderIds); + } + // 记录日志 + $this->dologs('receive', [ + 'receive_days' => (int)$receiveDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($orderIds), + ]); + return true; + } + + /** + * 批量处理已完成的订单 + * @param $orderIds + * @return bool + * @throws \think\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function onReceiveCompleted($orderIds) + { + // 获取已完成的订单列表 + $list = $this->model->getList(['order_id' => ['in', $orderIds]], [ + 'goods' => ['refund'], // 用于发放分销佣金 + ]); + if ($list->isEmpty()) return false; + // 执行订单完成后的操作 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::SHARING); + $OrderCompleteService->complete($list, $this->wxappId); + return true; + } + + /** + * 已完成订单结算 + * @param $refundDays + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function settled($refundDays) + { + // 获取已完成的订单(未累积用户实际消费金额) + // 条件1:订单状态:已完成 + // 条件2:超出售后期限 + // 条件3:is_settled 为 0 + // 截止时间 + $deadlineTime = time() - ((int)$refundDays * 86400); + // 查询条件 + $filter = [ + 'order_status' => 30, + 'receipt_time' => ['<=', $deadlineTime], // 此处使用<=,用于兼容自动确认收货后 + 'is_settled' => 0 + ]; + // 查询订单列表 + $orderList = $this->model->getList($filter, [ + 'goods' => ['refund'], // 用于计算售后退款金额 + ]); + // 订单id集 + $orderIds = helper::getArrayColumn($orderList, 'order_id'); + // 订单结算服务 + $OrderCompleteService = new OrderCompleteService(OrderTypeEnum::SHARING); + !empty($orderIds) && $OrderCompleteService->settled($orderList); + // 记录日志 + $this->dologs('settled', [ + 'refund_days' => (int)$refundDays, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($orderIds), + ]); + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior sharing Order --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} diff --git a/source/application/task/behavior/sharp/Order.php b/source/application/task/behavior/sharp/Order.php new file mode 100644 index 0000000..05add38 --- /dev/null +++ b/source/application/task/behavior/sharp/Order.php @@ -0,0 +1,100 @@ +model = $model; + $this->wxappId = $model::$wxapp_id; + // 秒杀订单行为管理 + $this->sharp(); + return true; + } + + /** + * 秒杀订单行为管理 + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + private function sharp() + { + // 未支付订单自动关闭 + $this->close(); + } + + /** + * 未支付订单自动关闭 + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + private function close() + { + $key = "__task_space__sharp_order__{$this->wxappId}"; + if (Cache::has($key)) return true; + // 取消n分钟以前的的未付款订单 + $minute = SettingModel::getItem('basic')['order']['order_close']; + if ($minute < 1) return false; + // 截止时间 + $deadlineTime = time() - ((int)$minute * 60); + // 执行自动关闭 + $service = new OrderService; + $service->close($deadlineTime, ['order_source' => OrderSourceEnum::SHARP]); + // 记录日志 + $this->dologs('close', [ + 'close_minute' => (int)$minute, + 'deadline_time' => $deadlineTime, + 'orderIds' => json_encode($service->getCloseOrderIds()), + ]); + return true; + } + + /** + * 记录日志 + * @param $method + * @param array $params + * @return bool|int + */ + private function dologs($method, $params = []) + { + $value = 'behavior sharp Order --' . $method; + foreach ($params as $key => $val) + $value .= ' --' . $key . ' ' . $val; + return log_write($value); + } + +} diff --git a/source/application/task/behavior/user/Grade.php b/source/application/task/behavior/user/Grade.php new file mode 100644 index 0000000..0cd124b --- /dev/null +++ b/source/application/task/behavior/user/Grade.php @@ -0,0 +1,70 @@ +model = $model; + if (!$model::$wxapp_id) { + return false; + } + $cacheKey = "__task_space__[user/Grade]__{$model::$wxapp_id}"; + if (!Cache::has($cacheKey)) { + // 设置用户的会员等级 + $this->setUserGrade(); + Cache::set($cacheKey, time(), 60 * 10); + } + return true; + } + + /** + * 设置用户的会员等级 + * @return array|bool|false + * @throws \Exception + */ + private function setUserGrade() + { + // 用户模型 + $UserModel = new UserModel; + // 获取所有等级 + $list = GradeModel::getUsableList(null, ['weight' => 'desc']); + if ($list->isEmpty()) { + return false; + } + // 遍历等级,根据升级条件 查询满足消费金额的用户列表,并且他的等级小于该等级 + $data = []; + foreach ($list as $grade) { + $userList = $UserModel->getUpgradeUserList($grade, array_keys($data)); + foreach ($userList as $user) { + if (!isset($data[$user['user_id']])) { + $data[$user['user_id']] = [ + 'user_id' => $user['user_id'], + 'old_grade_id' => $user['grade_id'], + 'new_grade_id' => $grade['grade_id'], + ]; + } + } + } + // 批量修改会员的等级 + return $UserModel->setBatchGrade($data); + } + +} \ No newline at end of file diff --git a/source/application/task/common.php b/source/application/task/common.php new file mode 100644 index 0000000..d5e10c0 --- /dev/null +++ b/source/application/task/common.php @@ -0,0 +1,20 @@ + $val) { + $value .= " --{$key} {$val}"; + } + log_write($value); + return true; +} diff --git a/source/application/task/model/Express.php b/source/application/task/model/Express.php new file mode 100644 index 0000000..c9e68cc --- /dev/null +++ b/source/application/task/model/Express.php @@ -0,0 +1,14 @@ +with($with) + ->where($filter) + ->where('is_delete', '=', 0) + ->select(); + } + +} diff --git a/source/application/task/model/OrderAddress.php b/source/application/task/model/OrderAddress.php new file mode 100644 index 0000000..2d50743 --- /dev/null +++ b/source/application/task/model/OrderAddress.php @@ -0,0 +1,15 @@ +where('user.user_id', 'not in', $excludedUserIds); + } + return $this->alias('user') + ->field(['user.user_id', 'user.grade_id']) + ->join('user_grade grade', 'grade.grade_id = user.grade_id', 'LEFT') + ->where(function ($query) use ($upgradeGrade) { + $query->where('user.grade_id', '=', 0); + $query->whereOr('grade.weight', '<', $upgradeGrade['weight']); + }) + ->where('user.expend_money', '>=', $upgradeGrade['upgrade']['expend_money']) + ->where('user.is_delete', '=', 0) + ->select(); + } + + /** + * 批量设置会员等级 + * @param $data + * @return array|false + * @throws \Exception + */ + public function setBatchGrade($data) + { + // 批量更新会员等级的数据 + $userData = []; + // 批量更新会员等级变更记录的数据 + $logData = []; + foreach ($data as $item) { + $userData[] = [ + 'user_id' => $item['user_id'], + 'grade_id' => $item['new_grade_id'], + ]; + $logData[] = [ + 'user_id' => $item['user_id'], + 'old_grade_id' => $item['old_grade_id'], + 'new_grade_id' => $item['new_grade_id'], + 'change_type' => ChangeTypeEnum::AUTO_UPGRADE, + ]; + } + // 批量更新会员等级 + $status = $this->isUpdate()->saveAll($userData); + // 批量更新会员等级变更记录 + (new GradeLogModel)->records($logData); + return $status; + } + +} diff --git a/source/application/task/model/UserCoupon.php b/source/application/task/model/UserCoupon.php new file mode 100644 index 0000000..8d49f42 --- /dev/null +++ b/source/application/task/model/UserCoupon.php @@ -0,0 +1,43 @@ +where('is_expire', '=', 0) + ->where('is_use', '=', 0) + ->where( + "IF ( `expire_type` = 20, + (`end_time` + 86400) < {$time}, + ( `create_time` + (`expire_day` * 86400)) < {$time} )" + )->column('user_coupon_id'); + } + + /** + * 设置优惠券过期状态 + * @param $couponIds + * @return false|int + */ + public function setIsExpire($couponIds) + { + if (empty($couponIds)) { + return false; + } + return $this->save(['is_expire' => 1], ['user_coupon_id' => ['in', $couponIds]]); + } + +} diff --git a/source/application/task/model/Wxapp.php b/source/application/task/model/Wxapp.php new file mode 100644 index 0000000..ab51ada --- /dev/null +++ b/source/application/task/model/Wxapp.php @@ -0,0 +1,15 @@ +where('end_time', '<=', time()) + ->where('status', '=', 1) + ->where('is_delete', '=', 0) + ->select(); + } + + /** + * 将砍价任务标记为已结束(批量) + * @param $taskIds + * @return false|int + */ + public function setIsEnd($taskIds) + { + return $this->isUpdate(true)->save([ + 'status' => 0 + ], ['task_id' => ['in', $taskIds]]); + } + +} \ No newline at end of file diff --git a/source/application/task/model/dealer/Apply.php b/source/application/task/model/dealer/Apply.php new file mode 100644 index 0000000..2499dda --- /dev/null +++ b/source/application/task/model/dealer/Apply.php @@ -0,0 +1,14 @@ +where('is_invalid', '=', 0) + ->where('is_settled', '=', 0) + ->select(); + if ($list->isEmpty()) { + return $list; + } + // 整理订单信息 + $with = ['goods' => ['refund']]; + return OrderService::getOrderList($list, 'order_master', $with); + } + + /** + * 标记订单已失效(批量) + * @param $ids + * @return false|int + */ + public function setInvalid($ids) + { + return $this->isUpdate(true) + ->save(['is_invalid' => 1], ['id' => ['in', $ids]]); + } + +} \ No newline at end of file diff --git a/source/application/task/model/dealer/Referee.php b/source/application/task/model/dealer/Referee.php new file mode 100644 index 0000000..41ab5f9 --- /dev/null +++ b/source/application/task/model/dealer/Referee.php @@ -0,0 +1,15 @@ +with(['goods', 'users' => ['user', 'sharingOrder']]) + ->where('end_time', '<=', time()) + ->where('status', '=', 10) + ->select(); + } + + /** + * 设置拼单失败状态 + * @param $activeIds + * @return false|int + */ + public function updateEndedStatus($activeIds) + { + if (empty($activeIds)) { + return false; + } + return $this->save(['status' => 30], ['active_id' => ['in', $activeIds]]); + } + +} diff --git a/source/application/task/model/sharing/ActiveUsers.php b/source/application/task/model/sharing/ActiveUsers.php new file mode 100644 index 0000000..6cd2ecd --- /dev/null +++ b/source/application/task/model/sharing/ActiveUsers.php @@ -0,0 +1,15 @@ +with($with) + ->where($filter) + ->where('is_delete', '=', 0) + ->select(); + } + + /** + * 获取拼团失败的订单 + * @param int $limit + * @return false|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getFailedOrderList($limit = 100) + { + return $this->alias('order') + ->join('sharing_active active', 'order.active_id = active.active_id', 'INNER') + ->where('order_type', '=', 20) + ->where('pay_status', '=', 20) + ->where('order_status', '=', 10) + ->where('active.status', '=', 30) + ->where('is_refund', '=', 0) + ->where('order.is_delete', '=', 0) + ->limit($limit) + ->select(); + } + + /** + * 更新拼团失败的订单并退款 + * @param $orderList + * @return bool + */ + public function updateFailedStatus($orderList) + { + // 批量更新订单状态 + foreach ($orderList as $order) { + /* @var static $order */ + try { + // 执行退款操作 + (new RefundService)->execute($order); + // 更新订单状态 + $order->save([ + 'is_refund' => 1, + 'order_status' => '20' + ]); + } catch (\Exception $e) { + $this->error = '订单ID:' . $order['order_id'] . ' 退款失败,错误信息:' . $e->getMessage(); + return false; + } + } + return true; + } + +} diff --git a/source/application/task/model/sharing/OrderAddress.php b/source/application/task/model/sharing/OrderAddress.php new file mode 100644 index 0000000..4e7fe53 --- /dev/null +++ b/source/application/task/model/sharing/OrderAddress.php @@ -0,0 +1,15 @@ +model = new OrderModel; + } + + /** + * 未支付订单自动关闭 + * @param int $deadlineTime + * @param array $where + * @return bool + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + * @throws \Exception + */ + public function close($deadlineTime, $where = []) + { + // 条件 + $filter = array_merge($where, [ + 'pay_status' => 10, + 'order_status' => 10, + 'create_time' => ['<=', $deadlineTime] + ]); + // 查询截止时间未支付的订单 + $list = $this->model->getList($filter, ['goods', 'user']); + $this->closeOrderIds = helper::getArrayColumn($list, 'order_id'); + // 取消订单事件 + if (!empty($this->closeOrderIds)) { + foreach ($list as &$order) { + // 回退商品库存 + FactoryStock::getFactory($order['order_source'])->backGoodsStock($order['goods'], false); + // 回退用户优惠券 + $order['coupon_id'] > 0 && UserCouponModel::setIsUse($order['coupon_id'], false); + // 回退用户积分 + $describe = "订单取消:{$order['order_no']}"; + $order['points_num'] > 0 && $order->user->setIncPoints($order['points_num'], $describe); + } + // 批量更新订单状态为已取消 + return $this->model->onBatchUpdate($this->closeOrderIds, ['order_status' => 20]); + } + return true; + } + + /** + * 获取自动关闭的订单id集 + * @return array + */ + public function getCloseOrderIds() + { + return $this->closeOrderIds; + } + +} \ No newline at end of file diff --git a/source/composer.json b/source/composer.json new file mode 100644 index 0000000..e9a0c3a --- /dev/null +++ b/source/composer.json @@ -0,0 +1,47 @@ +{ + "name": "topthink/think", + "description": "the new thinkphp framework", + "type": "project", + "keywords": [ + "framework", + "thinkphp", + "ORM" + ], + "license": "Apache-2.0", + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "require": { + "php": ">=5.4.0", + "ext-pdo": "*", + "ext-curl": "*", + "ext-json": "*", + "ext-bcmath": "*", + "ext-gd": "*", + "ext-openssl": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "ext-zlib": "*", + "topthink/framework": "5.0.*", + "qiniu/php-sdk": "^7.2", + "aliyuncs/oss-sdk-php": "^2.3", + "qcloud/cos-sdk-v5": "^1.2", + "kosinix/grafika": "dev-master", + "myclabs/php-enum": "^1.6", + "lvht/geohash": "^1.1" + }, + "autoload": { + "psr-4": { + "app\\": "application" + } + }, + "extra": { + "think-path": "thinkphp" + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/source/runtime/.gitignore b/source/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/source/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/source/thinkphp/.gitignore b/source/thinkphp/.gitignore new file mode 100644 index 0000000..7e31ef5 --- /dev/null +++ b/source/thinkphp/.gitignore @@ -0,0 +1,4 @@ +/composer.lock +/vendor +.idea +.DS_Store diff --git a/source/thinkphp/.htaccess b/source/thinkphp/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/source/thinkphp/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/source/thinkphp/.travis.yml b/source/thinkphp/.travis.yml new file mode 100644 index 0000000..f74ffca --- /dev/null +++ b/source/thinkphp/.travis.yml @@ -0,0 +1,47 @@ +sudo: false + +language: php + +services: + - memcached + - mongodb + - mysql + - postgresql + - redis-server + +matrix: + fast_finish: true + include: + - php: 5.4 + - php: 5.5 + - php: 5.6 + - php: 7.0 + - php: hhvm + allow_failures: + - php: hhvm + +cache: + directories: + - $HOME/.composer/cache + +before_install: + - composer self-update + - mysql -e "create database IF NOT EXISTS test;" -uroot + - psql -c 'DROP DATABASE IF EXISTS test;' -U postgres + - psql -c 'create database test;' -U postgres + +install: + - ./tests/script/install.sh + +script: + ## LINT + - find . -path ./vendor -prune -o -type f -name \*.php -exec php -l {} \; + ## PHP Copy/Paste Detector + - vendor/bin/phpcpd --verbose --exclude vendor ./ || true + ## PHPLOC + - vendor/bin/phploc --exclude vendor ./ + ## PHPUNIT + - vendor/bin/phpunit --coverage-clover=coverage.xml --configuration=phpunit.xml + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/source/thinkphp/CONTRIBUTING.md b/source/thinkphp/CONTRIBUTING.md new file mode 100644 index 0000000..dc8e91c --- /dev/null +++ b/source/thinkphp/CONTRIBUTING.md @@ -0,0 +1,119 @@ +如何贡献我的源代码 +=== + +此文档介绍了 ThinkPHP 团队的组成以及运转机制,您提交的代码将给 ThinkPHP 项目带来什么好处,以及如何才能加入我们的行列。 + +## 通过 Github 贡献代码 + +ThinkPHP 目前使用 Git 来控制程序版本,如果你想为 ThinkPHP 贡献源代码,请先大致了解 Git 的使用方法。我们目前把项目托管在 GitHub 上,任何 GitHub 用户都可以向我们贡献代码。 + +参与的方式很简单,`fork`一份 ThinkPHP 的代码到你的仓库中,修改后提交,并向我们发起`pull request`申请,我们会及时对代码进行审查并处理你的申请。审查通过后,你的代码将被`merge`进我们的仓库中,这样你就会自动出现在贡献者名单里了,非常方便。 + +我们希望你贡献的代码符合: + +* ThinkPHP 的编码规范 +* 适当的注释,能让其他人读懂 +* 遵循 Apache2 开源协议 + +**如果想要了解更多细节或有任何疑问,请继续阅读下面的内容** + +### 注意事项 + +* 本项目代码格式化标准选用 [**PSR-2**](http://www.kancloud.cn/thinkphp/php-fig-psr/3141); +* 类名和类文件名遵循 [**PSR-4**](http://www.kancloud.cn/thinkphp/php-fig-psr/3144); +* 对于 Issues 的处理,请使用诸如 `fix #xxx(Issue ID)` 的 commit title 直接关闭 issue。 +* 系统会自动在 PHP 5.4 5.5 5.6 7.0 和 HHVM 上测试修改,其中 HHVM 下的测试容许报错,请确保你的修改符合 PHP 5.4 ~ 5.6 和 PHP 7.0 的语法规范; +* 管理员不会合并造成 CI faild 的修改,若出现 CI faild 请检查自己的源代码或修改相应的[单元测试文件](tests); + +## GitHub Issue + +GitHub 提供了 Issue 功能,该功能可以用于: + +* 提出 bug +* 提出功能改进 +* 反馈使用体验 + +该功能不应该用于: + + * 提出修改意见(涉及代码署名和修订追溯问题) + * 不友善的言论 + +## 快速修改 + +**GitHub 提供了快速编辑文件的功能** + +1. 登录 GitHub 帐号; +2. 浏览项目文件,找到要进行修改的文件; +3. 点击右上角铅笔图标进行修改; +4. 填写 `Commit changes` 相关内容(Title 必填); +5. 提交修改,等待 CI 验证和管理员合并。 + +**若您需要一次提交大量修改,请继续阅读下面的内容** + +## 完整流程 + +1. `fork`本项目; +2. 克隆(`clone`)你 `fork` 的项目到本地; +3. 新建分支(`branch`)并检出(`checkout`)新分支; +4. 添加本项目到你的本地 git 仓库作为上游(`upstream`); +5. 进行修改,若你的修改包含方法或函数的增减,请记得修改[单元测试文件](tests); +6. 变基(衍合 `rebase`)你的分支到上游 master 分支; +7. `push` 你的本地仓库到 GitHub; +8. 提交 `pull request`; +9. 等待 CI 验证(若不通过则重复 5~7,不需要重新提交 `pull request`,GitHub 会自动更新你的 `pull request`); +10. 等待管理员处理,并及时 `rebase` 你的分支到上游 master 分支(若上游 master 分支有修改)。 + +*若有必要,可以 `git push -f` 强行推送 rebase 后的分支到自己的 `fork`* + +*绝对不可以使用 `git push -f` 强行推送修改到上游* + +### 注意事项 + +* 若对上述流程有任何不清楚的地方,请查阅 GIT 教程,如 [这个](http://backlogtool.com/git-guide/cn/); +* 对于代码**不同方面**的修改,请在自己 `fork` 的项目中**创建不同的分支**(原因参见`完整流程`第9条备注部分); +* 变基及交互式变基操作参见 [Git 交互式变基](http://pakchoi.me/2015/03/17/git-interactive-rebase/) + +## 推荐资源 + +### 开发环境 + +* XAMPP for Windows 5.5.x +* WampServer (for Windows) +* upupw Apache PHP5.4 ( for Windows) + +或自行安装 + +- Apache / Nginx +- PHP 5.4 ~ 5.6 +- MySQL / MariaDB + +*Windows 用户推荐添加 PHP bin 目录到 PATH,方便使用 composer* + +*Linux 用户自行配置环境, Mac 用户推荐使用内置 Apache 配合 Homebrew 安装 PHP 和 MariaDB* + +### 编辑器 + +Sublime Text 3 + phpfmt 插件 + +phpfmt 插件参数 + +```json +{ + "autocomplete": true, + "enable_auto_align": true, + "format_on_save": true, + "indent_with_space": true, + "psr1_naming": false, + "psr2": true, + "version": 4 +} +``` + +或其他 编辑器 / IDE 配合 PSR2 自动格式化工具 + +### Git GUI + +* SourceTree +* GitHub Desktop + +或其他 Git 图形界面客户端 diff --git a/source/thinkphp/LICENSE.txt b/source/thinkphp/LICENSE.txt new file mode 100644 index 0000000..2cb9a8a --- /dev/null +++ b/source/thinkphp/LICENSE.txt @@ -0,0 +1,32 @@ + +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 +版权所有Copyright © 2006-2017 by ThinkPHP (http://thinkphp.cn) +All rights reserved。 +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 + +Apache Licence是著名的非盈利开源组织Apache采用的协议。 +该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, +允许代码修改,再作为开源或商业软件发布。需要满足 +的条件: +1. 需要给代码的用户一份Apache Licence ; +2. 如果你修改了代码,需要在被修改的文件中说明; +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要 +带有原来代码中的协议,商标,专利声明和其他原来作者规 +定需要包含的说明; +4. 如果再发布的产品中包含一个Notice文件,则在Notice文 +件中需要带有本协议内容。你可以在Notice中增加自己的 +许可,但不可以表现为对Apache Licence构成更改。 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/source/thinkphp/README.md b/source/thinkphp/README.md new file mode 100644 index 0000000..f01fd2b --- /dev/null +++ b/source/thinkphp/README.md @@ -0,0 +1,114 @@ +ThinkPHP 5.0 +=============== + +[![StyleCI](https://styleci.io/repos/48530411/shield?style=flat&branch=master)](https://styleci.io/repos/48530411) +[![Build Status](https://travis-ci.org/top-think/framework.svg?branch=master)](https://travis-ci.org/top-think/framework) +[![codecov.io](http://codecov.io/github/top-think/framework/coverage.svg?branch=master)](http://codecov.io/github/github/top-think/framework?branch=master) +[![Total Downloads](https://poser.pugx.org/topthink/framework/downloads)](https://packagist.org/packages/topthink/framework) +[![Latest Stable Version](https://poser.pugx.org/topthink/framework/v/stable)](https://packagist.org/packages/topthink/framework) +[![Latest Unstable Version](https://poser.pugx.org/topthink/framework/v/unstable)](https://packagist.org/packages/topthink/framework) +[![License](https://poser.pugx.org/topthink/framework/license)](https://packagist.org/packages/topthink/framework) + +ThinkPHP5在保持快速开发和大道至简的核心理念不变的同时,PHP版本要求提升到5.4,优化核心,减少依赖,基于全新的架构思想和命名空间实现,是ThinkPHP突破原有框架思路的颠覆之作,其主要特性包括: + + + 基于命名空间和众多PHP新特性 + + 核心功能组件化 + + 强化路由功能 + + 更灵活的控制器 + + 重构的模型和数据库类 + + 配置文件可分离 + + 重写的自动验证和完成 + + 简化扩展机制 + + API支持完善 + + 改进的Log类 + + 命令行访问支持 + + REST支持 + + 引导文件支持 + + 方便的自动生成定义 + + 真正惰性加载 + + 分布式环境支持 + + 支持Composer + + 支持MongoDb + +> ThinkPHP5的运行环境要求PHP5.4以上。 + +详细开发文档参考 [ThinkPHP5完全开发手册](http://www.kancloud.cn/manual/thinkphp5) 以及[ThinkPHP5入门系列教程](http://www.kancloud.cn/special/thinkphp5_quickstart) + +## 目录结构 + +初始的目录结构如下: + +~~~ +www WEB部署目录(或者子目录) +├─application 应用目录 +│ ├─common 公共模块目录(可以更改) +│ ├─module_name 模块目录 +│ │ ├─config.php 模块配置文件 +│ │ ├─common.php 模块函数文件 +│ │ ├─controller 控制器目录 +│ │ ├─model 模型目录 +│ │ ├─view 视图目录 +│ │ └─ ... 更多类库目录 +│ │ +│ ├─command.php 命令行工具配置文件 +│ ├─common.php 公共函数文件 +│ ├─config.php 公共配置文件 +│ ├─route.php 路由配置文件 +│ ├─tags.php 应用行为扩展定义文件 +│ └─database.php 数据库配置文件 +│ +├─public WEB目录(对外访问目录) +│ ├─index.php 入口文件 +│ ├─router.php 快速测试文件 +│ └─.htaccess 用于apache的重写 +│ +├─thinkphp 框架系统目录 +│ ├─lang 语言文件目录 +│ ├─library 框架类库目录 +│ │ ├─think Think类库包目录 +│ │ └─traits 系统Trait目录 +│ │ +│ ├─tpl 系统模板目录 +│ ├─base.php 基础定义文件 +│ ├─console.php 控制台入口文件 +│ ├─convention.php 框架惯例配置文件 +│ ├─helper.php 助手函数文件 +│ ├─phpunit.xml phpunit配置文件 +│ └─start.php 框架入口文件 +│ +├─extend 扩展类库目录 +├─runtime 应用的运行时目录(可写,可定制) +├─vendor 第三方类库目录(Composer依赖库) +├─build.php 自动生成定义文件(参考) +├─composer.json composer 定义文件 +├─LICENSE.txt 授权说明文件 +├─README.md README 文件 +├─think 命令行入口文件 +~~~ + +> router.php用于php自带webserver支持,可用于快速测试 +> 切换到public目录后,启动命令:php -S localhost:8888 router.php +> 上面的目录结构和名称是可以改变的,这取决于你的入口文件和配置参数。 + +## 命名规范 + +ThinkPHP5的命名规范遵循`PSR-2`规范以及`PSR-4`自动加载规范。 + +## 参与开发 +注册并登录 Github 帐号, fork 本项目并进行改动。 + +更多细节参阅 [CONTRIBUTING.md](CONTRIBUTING.md) + +## 版权信息 + +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 + +本项目包含的第三方源码和二进制文件之版权信息另行标注。 + +版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn) + +All rights reserved。 + +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 + +更多细节参阅 [LICENSE.txt](LICENSE.txt) diff --git a/source/thinkphp/base.php b/source/thinkphp/base.php new file mode 100644 index 0000000..92c4fa5 --- /dev/null +++ b/source/thinkphp/base.php @@ -0,0 +1,65 @@ + +// +---------------------------------------------------------------------- + +define('THINK_VERSION', '5.0.24'); +define('THINK_START_TIME', microtime(true)); +define('THINK_START_MEM', memory_get_usage()); +define('EXT', '.php'); +define('DS', DIRECTORY_SEPARATOR); +defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS); +define('LIB_PATH', THINK_PATH . 'library' . DS); +define('CORE_PATH', LIB_PATH . 'think' . DS); +define('TRAIT_PATH', LIB_PATH . 'traits' . DS); +defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS); +defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS); +defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS); +defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS); +defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS); +defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS); +defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS); +defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS); +defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录 +defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀 +defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀 + +// 环境常量 +define('IS_CLI', PHP_SAPI == 'cli' ? true : false); +define('IS_WIN', strpos(PHP_OS, 'WIN') !== false); + +// 载入Loader类 +require CORE_PATH . 'Loader.php'; + +// 加载环境变量配置文件 +if (is_file(ROOT_PATH . '.env')) { + $env = parse_ini_file(ROOT_PATH . '.env', true); + + foreach ($env as $key => $val) { + $name = ENV_PREFIX . strtoupper($key); + + if (is_array($val)) { + foreach ($val as $k => $v) { + $item = $name . '_' . strtoupper($k); + putenv("$item=$v"); + } + } else { + putenv("$name=$val"); + } + } +} + +// 注册自动加载 +\think\Loader::register(); + +// 注册错误和异常处理机制 +\think\Error::register(); + +// 加载惯例配置文件 +\think\Config::set(include THINK_PATH . 'convention' . EXT); diff --git a/source/thinkphp/codecov.yml b/source/thinkphp/codecov.yml new file mode 100644 index 0000000..bef9d64 --- /dev/null +++ b/source/thinkphp/codecov.yml @@ -0,0 +1,12 @@ +comment: + layout: header, changes, diff +coverage: + ignore: + - base.php + - helper.php + - convention.php + - lang/zh-cn.php + - start.php + - console.php + status: + patch: false diff --git a/source/thinkphp/composer.json b/source/thinkphp/composer.json new file mode 100644 index 0000000..c546e11 --- /dev/null +++ b/source/thinkphp/composer.json @@ -0,0 +1,35 @@ +{ + "name": "topthink/framework", + "description": "the new thinkphp framework", + "type": "think-framework", + "keywords": [ + "framework", + "thinkphp", + "ORM" + ], + "homepage": "http://thinkphp.cn/", + "license": "Apache-2.0", + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "require": { + "php": ">=5.4.0", + "topthink/think-installer": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*", + "johnkary/phpunit-speedtrap": "^1.0", + "mikey179/vfsStream": "~1.6", + "phploc/phploc": "2.*", + "sebastian/phpcpd": "2.*", + "phpdocumentor/reflection-docblock": "^2.0" + }, + "autoload": { + "psr-4": { + "think\\": "library/think" + } + } +} diff --git a/source/thinkphp/console.php b/source/thinkphp/console.php new file mode 100644 index 0000000..578e4a7 --- /dev/null +++ b/source/thinkphp/console.php @@ -0,0 +1,20 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +// ThinkPHP 引导文件 +// 加载基础文件 +require __DIR__ . '/base.php'; + +// 执行应用 +App::initCommon(); +Console::init(); diff --git a/source/thinkphp/convention.php b/source/thinkphp/convention.php new file mode 100644 index 0000000..31a0a0c --- /dev/null +++ b/source/thinkphp/convention.php @@ -0,0 +1,298 @@ + '', + // 应用调试模式 + 'app_debug' => false, + // 应用Trace + 'app_trace' => false, + // 应用模式状态 + 'app_status' => '', + // 是否支持多模块 + 'app_multi_module' => true, + // 入口自动绑定模块 + 'auto_bind_module' => false, + // 注册的根命名空间 + 'root_namespace' => [], + // 扩展函数文件 + 'extra_file_list' => [THINK_PATH . 'helper' . EXT], + // 默认输出类型 + 'default_return_type' => 'html', + // 默认AJAX 数据返回格式,可选json xml ... + 'default_ajax_return' => 'json', + // 默认JSONP格式返回的处理方法 + 'default_jsonp_handler' => 'jsonpReturn', + // 默认JSONP处理方法 + 'var_jsonp_handler' => 'callback', + // 默认时区 + 'default_timezone' => 'PRC', + // 是否开启多语言 + 'lang_switch_on' => false, + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => '', + // 默认语言 + 'default_lang' => 'zh-cn', + // 应用类库后缀 + 'class_suffix' => false, + // 控制器类后缀 + 'controller_suffix' => false, + + // +---------------------------------------------------------------------- + // | 模块设置 + // +---------------------------------------------------------------------- + + // 默认模块名 + 'default_module' => 'index', + // 禁止访问模块 + 'deny_module_list' => ['common'], + // 默认控制器名 + 'default_controller' => 'Index', + // 默认操作名 + 'default_action' => 'index', + // 默认验证器 + 'default_validate' => '', + // 默认的空控制器名 + 'empty_controller' => 'Error', + // 操作方法前缀 + 'use_action_prefix' => false, + // 操作方法后缀 + 'action_suffix' => '', + // 自动搜索控制器 + 'controller_auto_search' => false, + + // +---------------------------------------------------------------------- + // | URL设置 + // +---------------------------------------------------------------------- + + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // pathinfo分隔符 + 'pathinfo_depr' => '/', + // HTTPS代理标识 + 'https_agent_name' => '', + // URL伪静态后缀 + 'url_html_suffix' => 'html', + // URL普通方式参数 用于自动生成 + 'url_common_param' => false, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 0, + // 是否开启路由 + 'url_route_on' => true, + // 路由配置文件(支持配置多个) + 'route_config_file' => ['route'], + // 路由使用完整匹配 + 'route_complete_match' => false, + // 是否强制使用路由 + 'url_route_must' => false, + // 域名部署 + 'url_domain_deploy' => false, + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // 是否自动转换URL中的控制器和操作名 + 'url_convert' => true, + // 默认的访问控制器层 + 'url_controller_layer' => 'controller', + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, + // 全局请求缓存排除规则 + 'request_cache_except' => [], + + // +---------------------------------------------------------------------- + // | 模板设置 + // +---------------------------------------------------------------------- + + 'template' => [ + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'Think', + // 视图基础目录,配置目录为所有模块的视图起始目录 + 'view_base' => '', + // 当前模板的视图目录 留空为自动获取 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'html', + // 模板文件名分隔符 + 'view_depr' => DS, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}', + // 标签库标签开始标记 + 'taglib_begin' => '{', + // 标签库标签结束标记 + 'taglib_end' => '}', + ], + + // 视图输出字符串内容替换 + 'view_replace_str' => [], + // 默认跳转页面对应的模板文件 + 'dispatch_success_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + 'dispatch_error_tmpl' => THINK_PATH . 'tpl' . DS . 'dispatch_jump.tpl', + + // +---------------------------------------------------------------------- + // | 异常及错误设置 + // +---------------------------------------------------------------------- + + // 异常页面的模板文件 + 'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl', + + // 错误显示信息,非调试模式有效 + 'error_message' => '页面错误!请稍后再试~', + // 显示错误信息 + 'show_error_msg' => false, + // 异常处理handle类 留空使用 \think\exception\Handle + 'exception_handle' => '', + // 是否记录trace信息到日志 + 'record_trace' => false, + + // +---------------------------------------------------------------------- + // | 日志设置 + // +---------------------------------------------------------------------- + + 'log' => [ + // 日志记录方式,内置 file socket 支持扩展 + 'type' => 'File', + // 日志保存目录 + 'path' => LOG_PATH, + // 日志记录级别 + 'level' => [], + ], + + // +---------------------------------------------------------------------- + // | Trace设置 开启 app_trace 后 有效 + // +---------------------------------------------------------------------- + 'trace' => [ + // 内置Html Console 支持扩展 + 'type' => 'Html', + ], + + // +---------------------------------------------------------------------- + // | 缓存设置 + // +---------------------------------------------------------------------- + + 'cache' => [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => CACHE_PATH, + // 缓存前缀 + 'prefix' => '', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + ], + + // +---------------------------------------------------------------------- + // | 会话设置 + // +---------------------------------------------------------------------- + + 'session' => [ + 'id' => '', + // SESSION_ID的提交变量,解决flash上传跨域 + 'var_session_id' => '', + // SESSION 前缀 + 'prefix' => 'think', + // 驱动方式 支持redis memcache memcached + 'type' => '', + // 是否自动开启 SESSION + 'auto_start' => true, + 'httponly' => true, + 'secure' => false, + ], + + // +---------------------------------------------------------------------- + // | Cookie设置 + // +---------------------------------------------------------------------- + 'cookie' => [ + // cookie 名称前缀 + 'prefix' => '', + // cookie 保存时间 + 'expire' => 0, + // cookie 保存路径 + 'path' => '/', + // cookie 有效域名 + 'domain' => '', + // cookie 启用安全传输 + 'secure' => false, + // httponly设置 + 'httponly' => '', + // 是否使用 setcookie + 'setcookie' => true, + ], + + // +---------------------------------------------------------------------- + // | 数据库设置 + // +---------------------------------------------------------------------- + + 'database' => [ + // 数据库类型 + 'type' => 'mysql', + // 数据库连接DSN配置 + 'dsn' => '', + // 服务器地址 + 'hostname' => '127.0.0.1', + // 数据库名 + 'database' => '', + // 数据库用户名 + 'username' => 'root', + // 数据库密码 + 'password' => '', + // 数据库连接端口 + 'hostport' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => '', + // 数据库调试模式 + 'debug' => false, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 数据集返回类型 + 'resultset_type' => 'array', + // 自动写入时间戳字段 + 'auto_timestamp' => false, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + ], + + //分页配置 + 'paginate' => [ + 'type' => 'bootstrap', + 'var_page' => 'page', + 'list_rows' => 15, + ], + + //控制台配置 + 'console' => [ + 'name' => 'Think Console', + 'version' => '0.1', + 'user' => null, + ], + +]; diff --git a/source/thinkphp/helper.php b/source/thinkphp/helper.php new file mode 100644 index 0000000..12683cf --- /dev/null +++ b/source/thinkphp/helper.php @@ -0,0 +1,589 @@ + +// +---------------------------------------------------------------------- + +//------------------------ +// ThinkPHP 助手函数 +//------------------------- + +use think\Cache; +use think\Config; +use think\Cookie; +use think\Db; +use think\Debug; +use think\exception\HttpException; +use think\exception\HttpResponseException; +use think\Lang; +use think\Loader; +use think\Log; +use think\Model; +use think\Request; +use think\Response; +use think\Session; +use think\Url; +use think\View; + +if (!function_exists('load_trait')) { + /** + * 快速导入Traits PHP5.5以上无需调用 + * @param string $class trait库 + * @param string $ext 类库后缀 + * @return boolean + */ + function load_trait($class, $ext = EXT) + { + return Loader::import($class, TRAIT_PATH, $ext); + } +} + +if (!function_exists('exception')) { + /** + * 抛出异常处理 + * + * @param string $msg 异常消息 + * @param integer $code 异常代码 默认为0 + * @param string $exception 异常类 + * + * @throws Exception + */ + function exception($msg, $code = 0, $exception = '') + { + $e = $exception ?: '\think\Exception'; + throw new $e($msg, $code); + } +} + +if (!function_exists('debug')) { + /** + * 记录时间(微秒)和内存使用情况 + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer|string $dec 小数位 如果是m 表示统计内存占用 + * @return mixed + */ + function debug($start, $end = '', $dec = 6) + { + if ('' == $end) { + Debug::remark($start); + } else { + return 'm' == $dec ? Debug::getRangeMem($start, $end) : Debug::getRangeTime($start, $end, $dec); + } + } +} + +if (!function_exists('lang')) { + /** + * 获取语言变量值 + * @param string $name 语言变量名 + * @param array $vars 动态变量值 + * @param string $lang 语言 + * @return mixed + */ + function lang($name, $vars = [], $lang = '') + { + return Lang::get($name, $vars, $lang); + } +} + +if (!function_exists('config')) { + /** + * 获取和设置配置参数 + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @param string $range 作用域 + * @return mixed + */ + function config($name = '', $value = null, $range = '') + { + if (is_null($value) && is_string($name)) { + return 0 === strpos($name, '?') ? Config::has(substr($name, 1), $range) : Config::get($name, $range); + } else { + return Config::set($name, $value, $range); + } + } +} + +if (!function_exists('input')) { + /** + * 获取输入数据 支持默认值和过滤 + * @param string $key 获取的变量名 + * @param mixed $default 默认值 + * @param string $filter 过滤方法 + * @return mixed + */ + function input($key = '', $default = null, $filter = '') + { + if (0 === strpos($key, '?')) { + $key = substr($key, 1); + $has = true; + } + if ($pos = strpos($key, '.')) { + // 指定参数来源 + list($method, $key) = explode('.', $key, 2); + if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'route', 'param', 'request', 'session', 'cookie', 'server', 'env', 'path', 'file'])) { + $key = $method . '.' . $key; + $method = 'param'; + } + } else { + // 默认为自动判断 + $method = 'param'; + } + if (isset($has)) { + return request()->has($key, $method, $default); + } else { + return request()->$method($key, $default, $filter); + } + } +} + +if (!function_exists('widget')) { + /** + * 渲染输出Widget + * @param string $name Widget名称 + * @param array $data 传入的参数 + * @return mixed + */ + function widget($name, $data = []) + { + return Loader::action($name, $data, 'widget'); + } +} + +if (!function_exists('model')) { + /** + * 实例化Model + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Model + */ + function model($name = '', $layer = 'model', $appendSuffix = false) + { + return Loader::model($name, $layer, $appendSuffix); + } +} + +if (!function_exists('validate')) { + /** + * 实例化验证器 + * @param string $name 验证器名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Validate + */ + function validate($name = '', $layer = 'validate', $appendSuffix = false) + { + return Loader::validate($name, $layer, $appendSuffix); + } +} + +if (!function_exists('db')) { + /** + * 实例化数据库类 + * @param string $name 操作的数据表名称(不含前缀) + * @param array|string $config 数据库配置参数 + * @param bool $force 是否强制重新连接 + * @return \think\db\Query + */ + function db($name = '', $config = [], $force = false) + { + return Db::connect($config, $force)->name($name); + } +} + +if (!function_exists('controller')) { + /** + * 实例化控制器 格式:[模块/]控制器 + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return \think\Controller + */ + function controller($name, $layer = 'controller', $appendSuffix = false) + { + return Loader::controller($name, $layer, $appendSuffix); + } +} + +if (!function_exists('action')) { + /** + * 调用模块的操作方法 参数格式 [模块/控制器/]操作 + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return mixed + */ + function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) + { + return Loader::action($url, $vars, $layer, $appendSuffix); + } +} + +if (!function_exists('import')) { + /** + * 导入所需的类库 同java的Import 本函数有缓存功能 + * @param string $class 类库命名空间字符串 + * @param string $baseUrl 起始路径 + * @param string $ext 导入的文件扩展名 + * @return boolean + */ + function import($class, $baseUrl = '', $ext = EXT) + { + return Loader::import($class, $baseUrl, $ext); + } +} + +if (!function_exists('vendor')) { + /** + * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面 + * @param string $class 类库 + * @param string $ext 类库后缀 + * @return boolean + */ + function vendor($class, $ext = EXT) + { + return Loader::import($class, VENDOR_PATH, $ext); + } +} + +if (!function_exists('dump')) { + /** + * 浏览器友好的变量输出 + * @param mixed $var 变量 + * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串 + * @param string $label 标签 默认为空 + * @return void|string + */ + function dump($var, $echo = true, $label = null) + { + return Debug::dump($var, $echo, $label); + } +} + +if (!function_exists('url')) { + /** + * Url生成 + * @param string $url 路由地址 + * @param string|array $vars 变量 + * @param bool|string $suffix 生成的URL后缀 + * @param bool|string $domain 域名 + * @return string + */ + function url($url = '', $vars = '', $suffix = true, $domain = false) + { + return Url::build($url, $vars, $suffix, $domain); + } +} + +if (!function_exists('session')) { + /** + * Session管理 + * @param string|array $name session名称,如果为数组表示进行session设置 + * @param mixed $value session值 + * @param string $prefix 前缀 + * @return mixed + */ + function session($name, $value = '', $prefix = null) + { + if (is_array($name)) { + // 初始化 + Session::init($name); + } elseif (is_null($name)) { + // 清除 + Session::clear('' === $value ? null : $value); + } elseif ('' === $value) { + // 判断或获取 + return 0 === strpos($name, '?') ? Session::has(substr($name, 1), $prefix) : Session::get($name, $prefix); + } elseif (is_null($value)) { + // 删除 + return Session::delete($name, $prefix); + } else { + // 设置 + return Session::set($name, $value, $prefix); + } + } +} + +if (!function_exists('cookie')) { + /** + * Cookie管理 + * @param string|array $name cookie名称,如果为数组表示进行cookie设置 + * @param mixed $value cookie值 + * @param mixed $option 参数 + * @return mixed + */ + function cookie($name, $value = '', $option = null) + { + if (is_array($name)) { + // 初始化 + Cookie::init($name); + } elseif (is_null($name)) { + // 清除 + Cookie::clear($value); + } elseif ('' === $value) { + // 获取 + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name, $option); + } elseif (is_null($value)) { + // 删除 + return Cookie::delete($name); + } else { + // 设置 + return Cookie::set($name, $value, $option); + } + } +} + +if (!function_exists('cache')) { + /** + * 缓存管理 + * @param mixed $name 缓存名称,如果为数组表示进行缓存设置 + * @param mixed $value 缓存值 + * @param mixed $options 缓存参数 + * @param string $tag 缓存标签 + * @return mixed + */ + function cache($name, $value = '', $options = null, $tag = null) + { + if (is_array($options)) { + // 缓存操作的同时初始化 + $cache = Cache::connect($options); + } elseif (is_array($name)) { + // 缓存初始化 + return Cache::connect($name); + } else { + $cache = Cache::init(); + } + + if (is_null($name)) { + return $cache->clear($value); + } elseif ('' === $value) { + // 获取缓存 + return 0 === strpos($name, '?') ? $cache->has(substr($name, 1)) : $cache->get($name); + } elseif (is_null($value)) { + // 删除缓存 + return $cache->rm($name); + } elseif (0 === strpos($name, '?') && '' !== $value) { + $expire = is_numeric($options) ? $options : null; + return $cache->remember(substr($name, 1), $value, $expire); + } else { + // 缓存数据 + if (is_array($options)) { + $expire = isset($options['expire']) ? $options['expire'] : null; //修复查询缓存无法设置过期时间 + } else { + $expire = is_numeric($options) ? $options : null; //默认快捷缓存设置过期时间 + } + if (is_null($tag)) { + return $cache->set($name, $value, $expire); + } else { + return $cache->tag($tag)->set($name, $value, $expire); + } + } + } +} + +if (!function_exists('trace')) { + /** + * 记录日志信息 + * @param mixed $log log信息 支持字符串和数组 + * @param string $level 日志级别 + * @return void|array + */ + function trace($log = '[think]', $level = 'log') + { + if ('[think]' === $log) { + return Log::getLog(); + } else { + Log::record($log, $level); + } + } +} + +if (!function_exists('request')) { + /** + * 获取当前Request对象实例 + * @return Request + */ + function request() + { + return Request::instance(); + } +} + +if (!function_exists('response')) { + /** + * 创建普通 Response 对象实例 + * @param mixed $data 输出数据 + * @param int|string $code 状态码 + * @param array $header 头信息 + * @param string $type + * @return Response + */ + function response($data = [], $code = 200, $header = [], $type = 'html') + { + return Response::create($data, $type, $code, $header); + } +} + +if (!function_exists('view')) { + /** + * 渲染模板输出 + * @param string $template 模板文件 + * @param array $vars 模板变量 + * @param array $replace 模板替换 + * @param integer $code 状态码 + * @return \think\response\View + */ + function view($template = '', $vars = [], $replace = [], $code = 200) + { + return Response::create($template, 'view', $code)->replace($replace)->assign($vars); + } +} + +if (!function_exists('json')) { + /** + * 获取\think\response\Json对象实例 + * @param mixed $data 返回的数据 + * @param integer $code 状态码 + * @param array $header 头部 + * @param array $options 参数 + * @return \think\response\Json + */ + function json($data = [], $code = 200, $header = [], $options = []) + { + return Response::create($data, 'json', $code, $header, $options); + } +} + +if (!function_exists('jsonp')) { + /** + * 获取\think\response\Jsonp对象实例 + * @param mixed $data 返回的数据 + * @param integer $code 状态码 + * @param array $header 头部 + * @param array $options 参数 + * @return \think\response\Jsonp + */ + function jsonp($data = [], $code = 200, $header = [], $options = []) + { + return Response::create($data, 'jsonp', $code, $header, $options); + } +} + +if (!function_exists('xml')) { + /** + * 获取\think\response\Xml对象实例 + * @param mixed $data 返回的数据 + * @param integer $code 状态码 + * @param array $header 头部 + * @param array $options 参数 + * @return \think\response\Xml + */ + function xml($data = [], $code = 200, $header = [], $options = []) + { + return Response::create($data, 'xml', $code, $header, $options); + } +} + +if (!function_exists('redirect')) { + /** + * 获取\think\response\Redirect对象实例 + * @param mixed $url 重定向地址 支持Url::build方法的地址 + * @param array|integer $params 额外参数 + * @param integer $code 状态码 + * @param array $with 隐式传参 + * @return \think\response\Redirect + */ + function redirect($url = [], $params = [], $code = 302, $with = []) + { + if (is_integer($params)) { + $code = $params; + $params = []; + } + return Response::create($url, 'redirect', $code)->params($params)->with($with); + } +} + +if (!function_exists('abort')) { + /** + * 抛出HTTP异常 + * @param integer|Response $code 状态码 或者 Response对象实例 + * @param string $message 错误信息 + * @param array $header 参数 + */ + function abort($code, $message = null, $header = []) + { + if ($code instanceof Response) { + throw new HttpResponseException($code); + } else { + throw new HttpException($code, $message, null, $header); + } + } +} + +if (!function_exists('halt')) { + /** + * 调试变量并且中断输出 + * @param mixed $var 调试变量或者信息 + */ + function halt($var) + { + dump($var); + throw new HttpResponseException(new Response); + } +} + +if (!function_exists('token')) { + /** + * 生成表单令牌 + * @param string $name 令牌名称 + * @param mixed $type 令牌生成方法 + * @return string + */ + function token($name = '__token__', $type = 'md5') + { + $token = Request::instance()->token($name, $type); + return ''; + } +} + +if (!function_exists('load_relation')) { + /** + * 延迟预载入关联查询 + * @param mixed $resultSet 数据集 + * @param mixed $relation 关联 + * @return array + */ + function load_relation($resultSet, $relation) + { + $item = current($resultSet); + if ($item instanceof Model) { + $item->eagerlyResultSet($resultSet, $relation); + } + return $resultSet; + } +} + +if (!function_exists('collection')) { + /** + * 数组转换为数据集对象 + * @param array $resultSet 数据集数组 + * @return \think\model\Collection|\think\Collection + */ + function collection($resultSet) + { + $item = current($resultSet); + if ($item instanceof Model) { + return \think\model\Collection::make($resultSet); + } else { + return \think\Collection::make($resultSet); + } + } +} diff --git a/source/thinkphp/lang/zh-cn.php b/source/thinkphp/lang/zh-cn.php new file mode 100644 index 0000000..eb7a914 --- /dev/null +++ b/source/thinkphp/lang/zh-cn.php @@ -0,0 +1,136 @@ + +// +---------------------------------------------------------------------- + +// 核心中文语言包 +return [ + // 系统错误提示 + 'Undefined variable' => '未定义变量', + 'Undefined index' => '未定义数组索引', + 'Undefined offset' => '未定义数组下标', + 'Parse error' => '语法解析错误', + 'Type error' => '类型错误', + 'Fatal error' => '致命错误', + 'syntax error' => '语法错误', + + // 框架核心错误提示 + 'dispatch type not support' => '不支持的调度类型', + 'method param miss' => '方法参数错误', + 'method not exists' => '方法不存在', + 'module not exists' => '模块不存在', + 'controller not exists' => '控制器不存在', + 'class not exists' => '类不存在', + 'property not exists' => '类的属性不存在', + 'template not exists' => '模板文件不存在', + 'illegal controller name' => '非法的控制器名称', + 'illegal action name' => '非法的操作名称', + 'url suffix deny' => '禁止的URL后缀访问', + 'Route Not Found' => '当前访问路由未定义', + 'Undefined db type' => '未定义数据库类型', + 'variable type error' => '变量类型错误', + 'PSR-4 error' => 'PSR-4 规范错误', + 'not support total' => '简洁模式下不能获取数据总数', + 'not support last' => '简洁模式下不能获取最后一页', + 'error session handler' => '错误的SESSION处理器类', + 'not allow php tag' => '模板不允许使用PHP语法', + 'not support' => '不支持', + 'redisd master' => 'Redisd 主服务器错误', + 'redisd slave' => 'Redisd 从服务器错误', + 'must run at sae' => '必须在SAE运行', + 'memcache init error' => '未开通Memcache服务,请在SAE管理平台初始化Memcache服务', + 'KVDB init error' => '没有初始化KVDB,请在SAE管理平台初始化KVDB服务', + 'fields not exists' => '数据表字段不存在', + 'where express error' => '查询表达式错误', + 'not support data' => '不支持的数据表达式', + 'no data to update' => '没有任何数据需要更新', + 'miss data to insert' => '缺少需要写入的数据', + 'miss complex primary data' => '缺少复合主键数据', + 'miss update condition' => '缺少更新条件', + 'model data Not Found' => '模型数据不存在', + 'table data not Found' => '表数据不存在', + 'delete without condition' => '没有条件不会执行删除操作', + 'miss relation data' => '缺少关联表数据', + 'tag attr must' => '模板标签属性必须', + 'tag error' => '模板标签错误', + 'cache write error' => '缓存写入失败', + 'sae mc write error' => 'SAE mc 写入错误', + 'route name not exists' => '路由标识不存在(或参数不够)', + 'invalid request' => '非法请求', + 'bind attr has exists' => '模型的属性已经存在', + 'relation data not exists' => '关联数据不存在', + 'relation not support' => '关联不支持', + 'chunk not support order' => 'Chunk不支持调用order方法', + 'closure not support cache(true)' => '使用闭包查询不支持cache(true),请指定缓存Key', + + // 上传错误信息 + 'unknown upload error' => '未知上传错误!', + 'file write error' => '文件写入失败!', + 'upload temp dir not found' => '找不到临时文件夹!', + 'no file to uploaded' => '没有文件被上传!', + 'only the portion of file is uploaded' => '文件只有部分被上传!', + 'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!', + 'upload write error' => '文件上传保存错误!', + 'has the same filename: {:filename}' => '存在同名文件:{:filename}', + 'upload illegal files' => '非法上传文件', + 'illegal image files' => '非法图片文件', + 'extensions to upload is not allowed' => '上传文件后缀不允许', + 'mimetype to upload is not allowed' => '上传文件MIME类型不允许!', + 'filesize not match' => '上传文件大小不符!', + 'directory {:path} creation failed' => '目录 {:path} 创建失败!', + + // Validate Error Message + ':attribute require' => ':attribute不能为空', + ':attribute must be numeric' => ':attribute必须是数字', + ':attribute must be integer' => ':attribute必须是整数', + ':attribute must be float' => ':attribute必须是浮点数', + ':attribute must be bool' => ':attribute必须是布尔值', + ':attribute not a valid email address' => ':attribute格式不符', + ':attribute not a valid mobile' => ':attribute格式不符', + ':attribute must be a array' => ':attribute必须是数组', + ':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1', + ':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式', + ':attribute not a valid file' => ':attribute不是有效的上传文件', + ':attribute not a valid image' => ':attribute不是有效的图像文件', + ':attribute must be alpha' => ':attribute只能是字母', + ':attribute must be alpha-numeric' => ':attribute只能是字母和数字', + ':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-', + ':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP', + ':attribute must be chinese' => ':attribute只能是汉字', + ':attribute must be chinese or alpha' => ':attribute只能是汉字、字母', + ':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字', + ':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-', + ':attribute not a valid url' => ':attribute不是有效的URL地址', + ':attribute not a valid ip' => ':attribute不是有效的IP地址', + ':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule', + ':attribute must be in :rule' => ':attribute必须在 :rule 范围内', + ':attribute be notin :rule' => ':attribute不能在 :rule 范围内', + ':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间', + ':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间', + 'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule', + 'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule', + 'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule', + ':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule', + ':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule', + ':attribute not within :rule' => '不在有效期内 :rule', + 'access IP is not allowed' => '不允许的IP访问', + 'access IP denied' => '禁止的IP访问', + ':attribute out of accord with :2' => ':attribute和确认字段:2不一致', + ':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同', + ':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule', + ':attribute must greater than :rule' => ':attribute必须大于 :rule', + ':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule', + ':attribute must less than :rule' => ':attribute必须小于 :rule', + ':attribute must equal :rule' => ':attribute必须等于 :rule', + ':attribute has exists' => ':attribute已存在', + ':attribute not conform to the rules' => ':attribute不符合指定规则', + 'invalid Request method' => '无效的请求类型', + 'invalid token' => '令牌数据无效', + 'not conform to the rules' => '规则错误', +]; diff --git a/source/thinkphp/library/think/App.php b/source/thinkphp/library/think/App.php new file mode 100644 index 0000000..f572b90 --- /dev/null +++ b/source/thinkphp/library/think/App.php @@ -0,0 +1,677 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; +use think\exception\HttpException; +use think\exception\HttpResponseException; +use think\exception\RouteNotFoundException; + +/** + * App 应用管理 + * @author liu21st + */ +class App +{ + /** + * @var bool 是否初始化过 + */ + protected static $init = false; + + /** + * @var string 当前模块路径 + */ + public static $modulePath; + + /** + * @var bool 应用调试模式 + */ + public static $debug = true; + + /** + * @var string 应用类库命名空间 + */ + public static $namespace = 'app'; + + /** + * @var bool 应用类库后缀 + */ + public static $suffix = false; + + /** + * @var bool 应用路由检测 + */ + protected static $routeCheck; + + /** + * @var bool 严格路由检测 + */ + protected static $routeMust; + + /** + * @var array 请求调度分发 + */ + protected static $dispatch; + + /** + * @var array 额外加载文件 + */ + protected static $file = []; + + /** + * 执行应用程序 + * @access public + * @param Request $request 请求对象 + * @return Response + * @throws Exception + */ + public static function run(Request $request = null) + { + $request = is_null($request) ? Request::instance() : $request; + + try { + $config = self::initCommon(); + + // 模块/控制器绑定 + if (defined('BIND_MODULE')) { + BIND_MODULE && Route::bind(BIND_MODULE); + } elseif ($config['auto_bind_module']) { + // 入口自动绑定 + $name = pathinfo($request->baseFile(), PATHINFO_FILENAME); + if ($name && 'index' != $name && is_dir(APP_PATH . $name)) { + Route::bind($name); + } + } + + $request->filter($config['default_filter']); + + // 默认语言 + Lang::range($config['default_lang']); + // 开启多语言机制 检测当前语言 + $config['lang_switch_on'] && Lang::detect(); + $request->langset(Lang::range()); + + // 加载系统语言包 + Lang::load([ + THINK_PATH . 'lang' . DS . $request->langset() . EXT, + APP_PATH . 'lang' . DS . $request->langset() . EXT, + ]); + + // 监听 app_dispatch + Hook::listen('app_dispatch', self::$dispatch); + // 获取应用调度信息 + $dispatch = self::$dispatch; + + // 未设置调度信息则进行 URL 路由检测 + if (empty($dispatch)) { + $dispatch = self::routeCheck($request, $config); + } + + // 记录当前调度信息 + $request->dispatch($dispatch); + + // 记录路由和请求信息 + if (self::$debug) { + Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); + Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info'); + Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); + } + + // 监听 app_begin + Hook::listen('app_begin', $dispatch); + + // 请求缓存检查 + $request->cache( + $config['request_cache'], + $config['request_cache_expire'], + $config['request_cache_except'] + ); + + $data = self::exec($dispatch, $config); + } catch (HttpResponseException $exception) { + $data = $exception->getResponse(); + } + + // 清空类的实例化 + Loader::clearInstance(); + + // 输出数据到客户端 + if ($data instanceof Response) { + $response = $data; + } elseif (!is_null($data)) { + // 默认自动识别响应输出类型 + $type = $request->isAjax() ? + Config::get('default_ajax_return') : + Config::get('default_return_type'); + + $response = Response::create($data, $type); + } else { + $response = Response::create(); + } + + // 监听 app_end + Hook::listen('app_end', $response); + + return $response; + } + + /** + * 初始化应用,并返回配置信息 + * @access public + * @return array + */ + public static function initCommon() + { + if (empty(self::$init)) { + if (defined('APP_NAMESPACE')) { + self::$namespace = APP_NAMESPACE; + } + + Loader::addNamespace(self::$namespace, APP_PATH); + + // 初始化应用 + $config = self::init(); + self::$suffix = $config['class_suffix']; + + // 应用调试模式 + self::$debug = Env::get('app_debug', Config::get('app_debug')); + + if (!self::$debug) { + ini_set('display_errors', 'Off'); + } elseif (!IS_CLI) { + // 重新申请一块比较大的 buffer + if (ob_get_level() > 0) { + $output = ob_get_clean(); + } + + ob_start(); + + if (!empty($output)) { + echo $output; + } + + } + + if (!empty($config['root_namespace'])) { + Loader::addNamespace($config['root_namespace']); + } + + // 加载额外文件 + if (!empty($config['extra_file_list'])) { + foreach ($config['extra_file_list'] as $file) { + $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; + if (is_file($file) && !isset(self::$file[$file])) { + include $file; + self::$file[$file] = true; + } + } + } + + // 设置系统时区 + date_default_timezone_set($config['default_timezone']); + + // 监听 app_init + Hook::listen('app_init'); + + self::$init = true; + } + + return Config::get(); + } + + /** + * 初始化应用或模块 + * @access public + * @param string $module 模块名 + * @return array + */ + private static function init($module = '') + { + // 定位模块目录 + $module = $module ? $module . DS : ''; + + // 加载初始化文件 + if (is_file(APP_PATH . $module . 'init' . EXT)) { + include APP_PATH . $module . 'init' . EXT; + } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) { + include RUNTIME_PATH . $module . 'init' . EXT; + } else { + // 加载模块配置 + $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); + + // 读取数据库配置文件 + $filename = CONF_PATH . $module . 'database' . CONF_EXT; + Config::load($filename, 'database'); + + // 读取扩展配置文件 + if (is_dir(CONF_PATH . $module . 'extra')) { + $dir = CONF_PATH . $module . 'extra'; + $files = scandir($dir); + foreach ($files as $file) { + if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) { + $filename = $dir . DS . $file; + Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); + } + } + } + + // 加载应用状态配置 + if ($config['app_status']) { + Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); + } + + // 加载行为扩展文件 + if (is_file(CONF_PATH . $module . 'tags' . EXT)) { + Hook::import(include CONF_PATH . $module . 'tags' . EXT); + } + + // 加载公共文件 + $path = APP_PATH . $module; + if (is_file($path . 'common' . EXT)) { + include $path . 'common' . EXT; + } + + // 加载当前模块语言包 + if ($module) { + Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); + } + } + + return Config::get(); + } + + /** + * 设置当前请求的调度信息 + * @access public + * @param array|string $dispatch 调度信息 + * @param string $type 调度类型 + * @return void + */ + public static function dispatch($dispatch, $type = 'module') + { + self::$dispatch = ['type' => $type, $type => $dispatch]; + } + + /** + * 执行函数或者闭包方法 支持参数调用 + * @access public + * @param string|array|\Closure $function 函数或者闭包 + * @param array $vars 变量 + * @return mixed + */ + public static function invokeFunction($function, $vars = []) + { + $reflect = new \ReflectionFunction($function); + $args = self::bindParams($reflect, $vars); + + // 记录执行信息 + self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); + + return $reflect->invokeArgs($args); + } + + /** + * 调用反射执行类的方法 支持参数绑定 + * @access public + * @param string|array $method 方法 + * @param array $vars 变量 + * @return mixed + */ + public static function invokeMethod($method, $vars = []) + { + if (is_array($method)) { + $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); + $reflect = new \ReflectionMethod($class, $method[1]); + } else { + // 静态方法 + $reflect = new \ReflectionMethod($method); + } + + $args = self::bindParams($reflect, $vars); + + self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info'); + + return $reflect->invokeArgs(isset($class) ? $class : null, $args); + } + + /** + * 调用反射执行类的实例化 支持依赖注入 + * @access public + * @param string $class 类名 + * @param array $vars 变量 + * @return mixed + */ + public static function invokeClass($class, $vars = []) + { + $reflect = new \ReflectionClass($class); + $constructor = $reflect->getConstructor(); + $args = $constructor ? self::bindParams($constructor, $vars) : []; + + return $reflect->newInstanceArgs($args); + } + + /** + * 绑定参数 + * @access private + * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类 + * @param array $vars 变量 + * @return array + */ + private static function bindParams($reflect, $vars = []) + { + // 自动获取请求变量 + if (empty($vars)) { + $vars = Config::get('url_param_type') ? + Request::instance()->route() : + Request::instance()->param(); + } + + $args = []; + if ($reflect->getNumberOfParameters() > 0) { + // 判断数组类型 数字数组时按顺序绑定参数 + reset($vars); + $type = key($vars) === 0 ? 1 : 0; + + foreach ($reflect->getParameters() as $param) { + $args[] = self::getParamValue($param, $vars, $type); + } + } + + return $args; + } + + /** + * 获取参数值 + * @access private + * @param \ReflectionParameter $param 参数 + * @param array $vars 变量 + * @param string $type 类别 + * @return array + */ + private static function getParamValue($param, &$vars, $type) + { + $name = $param->getName(); + $class = $param->getClass(); + + if ($class) { + $className = $class->getName(); + $bind = Request::instance()->$name; + + if ($bind instanceof $className) { + $result = $bind; + } else { + if (method_exists($className, 'invoke')) { + $method = new \ReflectionMethod($className, 'invoke'); + + if ($method->isPublic() && $method->isStatic()) { + return $className::invoke(Request::instance()); + } + } + + $result = method_exists($className, 'instance') ? + $className::instance() : + new $className; + } + } elseif (1 == $type && !empty($vars)) { + $result = array_shift($vars); + } elseif (0 == $type && isset($vars[$name])) { + $result = $vars[$name]; + } elseif ($param->isDefaultValueAvailable()) { + $result = $param->getDefaultValue(); + } else { + throw new \InvalidArgumentException('method param miss:' . $name); + } + + return $result; + } + + /** + * 执行调用分发 + * @access protected + * @param array $dispatch 调用信息 + * @param array $config 配置信息 + * @return Response|mixed + * @throws \InvalidArgumentException + */ + protected static function exec($dispatch, $config) + { + switch ($dispatch['type']) { + case 'redirect': // 重定向跳转 + $data = Response::create($dispatch['url'], 'redirect') + ->code($dispatch['status']); + break; + case 'module': // 模块/控制器/操作 + $data = self::module( + $dispatch['module'], + $config, + isset($dispatch['convert']) ? $dispatch['convert'] : null + ); + break; + case 'controller': // 执行控制器操作 + $vars = array_merge(Request::instance()->param(), $dispatch['var']); + $data = Loader::action( + $dispatch['controller'], + $vars, + $config['url_controller_layer'], + $config['controller_suffix'] + ); + break; + case 'method': // 回调方法 + $vars = array_merge(Request::instance()->param(), $dispatch['var']); + $data = self::invokeMethod($dispatch['method'], $vars); + break; + case 'function': // 闭包 + $data = self::invokeFunction($dispatch['function']); + break; + case 'response': // Response 实例 + $data = $dispatch['response']; + break; + default: + throw new \InvalidArgumentException('dispatch type not support'); + } + + return $data; + } + + /** + * 执行模块 + * @access public + * @param array $result 模块/控制器/操作 + * @param array $config 配置参数 + * @param bool $convert 是否自动转换控制器和操作名 + * @return mixed + * @throws HttpException + */ + public static function module($result, $config, $convert = null) + { + if (is_string($result)) { + $result = explode('/', $result); + } + + $request = Request::instance(); + + if ($config['app_multi_module']) { + // 多模块部署 + $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); + $bind = Route::getBind('module'); + $available = false; + + if ($bind) { + // 绑定模块 + list($bindModule) = explode('/', $bind); + + if (empty($result[0])) { + $module = $bindModule; + $available = true; + } elseif ($module == $bindModule) { + $available = true; + } + } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) { + $available = true; + } + + // 模块初始化 + if ($module && $available) { + // 初始化模块 + $request->module($module); + $config = self::init($module); + + // 模块请求缓存检查 + $request->cache( + $config['request_cache'], + $config['request_cache_expire'], + $config['request_cache_except'] + ); + } else { + throw new HttpException(404, 'module not exists:' . $module); + } + } else { + // 单一模块部署 + $module = ''; + $request->module($module); + } + + // 设置默认过滤机制 + $request->filter($config['default_filter']); + + // 当前模块路径 + App::$modulePath = APP_PATH . ($module ? $module . DS : ''); + + // 是否自动转换控制器和操作名 + $convert = is_bool($convert) ? $convert : $config['url_convert']; + + // 获取控制器名 + $controller = strip_tags($result[1] ?: $config['default_controller']); + + if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { + throw new HttpException(404, 'controller not exists:' . $controller); + } + + $controller = $convert ? strtolower($controller) : $controller; + + // 获取操作名 + $actionName = strip_tags($result[2] ?: $config['default_action']); + if (!empty($config['action_convert'])) { + $actionName = Loader::parseName($actionName, 1); + } else { + $actionName = $convert ? strtolower($actionName) : $actionName; + } + + // 设置当前请求的控制器、操作 + $request->controller(Loader::parseName($controller, 1))->action($actionName); + + // 监听module_init + Hook::listen('module_init', $request); + + try { + $instance = Loader::controller( + $controller, + $config['url_controller_layer'], + $config['controller_suffix'], + $config['empty_controller'] + ); + } catch (ClassNotFoundException $e) { + throw new HttpException(404, 'controller not exists:' . $e->getClass()); + } + + // 获取当前操作名 + $action = $actionName . $config['action_suffix']; + + $vars = []; + if (is_callable([$instance, $action])) { + // 执行操作方法 + $call = [$instance, $action]; + // 严格获取当前操作方法名 + $reflect = new \ReflectionMethod($instance, $action); + $methodName = $reflect->getName(); + $suffix = $config['action_suffix']; + $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName; + $request->action($actionName); + + } elseif (is_callable([$instance, '_empty'])) { + // 空操作 + $call = [$instance, '_empty']; + $vars = [$actionName]; + } else { + // 操作不存在 + throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); + } + + Hook::listen('action_begin', $call); + + return self::invokeMethod($call, $vars); + } + + /** + * URL路由检测(根据PATH_INFO) + * @access public + * @param \think\Request $request 请求实例 + * @param array $config 配置信息 + * @return array + * @throws \think\Exception + */ + public static function routeCheck($request, array $config) + { + $path = $request->path(); + $depr = $config['pathinfo_depr']; + $result = false; + + // 路由检测 + $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; + if ($check) { + // 开启路由 + if (is_file(RUNTIME_PATH . 'route.php')) { + // 读取路由缓存 + $rules = include RUNTIME_PATH . 'route.php'; + is_array($rules) && Route::rules($rules); + } else { + $files = $config['route_config_file']; + foreach ($files as $file) { + if (is_file(CONF_PATH . $file . CONF_EXT)) { + // 导入路由配置 + $rules = include CONF_PATH . $file . CONF_EXT; + is_array($rules) && Route::import($rules); + } + } + } + + // 路由检测(根据路由定义返回不同的URL调度) + $result = Route::check($request, $path, $depr, $config['url_domain_deploy']); + $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; + + if ($must && false === $result) { + // 路由无效 + throw new RouteNotFoundException(); + } + } + + // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索 + if (false === $result) { + $result = Route::parseUrl($path, $depr, $config['controller_auto_search']); + } + + return $result; + } + + /** + * 设置应用的路由检测机制 + * @access public + * @param bool $route 是否需要检测路由 + * @param bool $must 是否强制检测路由 + * @return void + */ + public static function route($route, $must = false) + { + self::$routeCheck = $route; + self::$routeMust = $must; + } +} diff --git a/source/thinkphp/library/think/Build.php b/source/thinkphp/library/think/Build.php new file mode 100644 index 0000000..de7c327 --- /dev/null +++ b/source/thinkphp/library/think/Build.php @@ -0,0 +1,235 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Build +{ + /** + * 根据传入的 build 资料创建目录和文件 + * @access public + * @param array $build build 列表 + * @param string $namespace 应用类库命名空间 + * @param bool $suffix 类库后缀 + * @return void + * @throws Exception + */ + public static function run(array $build = [], $namespace = 'app', $suffix = false) + { + // 锁定 + $lock = APP_PATH . 'build.lock'; + + // 如果锁定文件不可写(不存在)则进行处理,否则表示已经有程序在处理了 + if (!is_writable($lock)) { + if (!touch($lock)) { + throw new Exception( + '应用目录[' . APP_PATH . ']不可写,目录无法自动生成!
请手动生成项目目录~', + 10006 + ); + } + + foreach ($build as $module => $list) { + if ('__dir__' == $module) { + // 创建目录列表 + self::buildDir($list); + } elseif ('__file__' == $module) { + // 创建文件列表 + self::buildFile($list); + } else { + // 创建模块 + self::module($module, $list, $namespace, $suffix); + } + } + + // 解除锁定 + unlink($lock); + } + } + + /** + * 创建目录 + * @access protected + * @param array $list 目录列表 + * @return void + */ + protected static function buildDir($list) + { + foreach ($list as $dir) { + // 目录不存在则创建目录 + !is_dir(APP_PATH . $dir) && mkdir(APP_PATH . $dir, 0755, true); + } + } + + /** + * 创建文件 + * @access protected + * @param array $list 文件列表 + * @return void + */ + protected static function buildFile($list) + { + foreach ($list as $file) { + // 先创建目录 + if (!is_dir(APP_PATH . dirname($file))) { + mkdir(APP_PATH . dirname($file), 0755, true); + } + + // 再创建文件 + if (!is_file(APP_PATH . $file)) { + file_put_contents( + APP_PATH . $file, + 'php' == pathinfo($file, PATHINFO_EXTENSION) ? " ['config.php', 'common.php'], + '__dir__' => ['controller', 'model', 'view'], + ]; + } + + // 创建子目录和文件 + foreach ($list as $path => $file) { + $modulePath = APP_PATH . $module . DS; + + if ('__dir__' == $path) { + // 生成子目录 + foreach ($file as $dir) { + self::checkDirBuild($modulePath . $dir); + } + } elseif ('__file__' == $path) { + // 生成(空白)文件 + foreach ($file as $name) { + if (!is_file($modulePath . $name)) { + file_put_contents( + $modulePath . $name, + 'php' == pathinfo($name, PATHINFO_EXTENSION) ? " +// +---------------------------------------------------------------------- + +namespace think; + +use think\cache\Driver; + +class Cache +{ + /** + * @var array 缓存的实例 + */ + public static $instance = []; + + /** + * @var int 缓存读取次数 + */ + public static $readTimes = 0; + + /** + * @var int 缓存写入次数 + */ + public static $writeTimes = 0; + + /** + * @var object 操作句柄 + */ + public static $handler; + + /** + * 连接缓存驱动 + * @access public + * @param array $options 配置数组 + * @param bool|string $name 缓存连接标识 true 强制重新连接 + * @return Driver + */ + public static function connect(array $options = [], $name = false) + { + $type = !empty($options['type']) ? $options['type'] : 'File'; + + if (false === $name) { + $name = md5(serialize($options)); + } + + if (true === $name || !isset(self::$instance[$name])) { + $class = false === strpos($type, '\\') ? + '\\think\\cache\\driver\\' . ucwords($type) : + $type; + + // 记录初始化信息 + App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info'); + + if (true === $name) { + return new $class($options); + } + + self::$instance[$name] = new $class($options); + } + + return self::$instance[$name]; + } + + /** + * 自动初始化缓存 + * @access public + * @param array $options 配置数组 + * @return Driver + */ + public static function init(array $options = []) + { + if (is_null(self::$handler)) { + if (empty($options) && 'complex' == Config::get('cache.type')) { + $default = Config::get('cache.default'); + // 获取默认缓存配置,并连接 + $options = Config::get('cache.' . $default['type']) ?: $default; + } elseif (empty($options)) { + $options = Config::get('cache'); + } + + self::$handler = self::connect($options); + } + + return self::$handler; + } + + /** + * 切换缓存类型 需要配置 cache.type 为 complex + * @access public + * @param string $name 缓存标识 + * @return Driver + */ + public static function store($name = '') + { + if ('' !== $name && 'complex' == Config::get('cache.type')) { + return self::connect(Config::get('cache.' . $name), strtolower($name)); + } + + return self::init(); + } + + /** + * 判断缓存是否存在 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public static function has($name) + { + self::$readTimes++; + + return self::init()->has($name); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存标识 + * @param mixed $default 默认值 + * @return mixed + */ + public static function get($name, $default = false) + { + self::$readTimes++; + + return self::init()->get($name, $default); + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存标识 + * @param mixed $value 存储数据 + * @param int|null $expire 有效时间 0为永久 + * @return boolean + */ + public static function set($name, $value, $expire = null) + { + self::$writeTimes++; + + return self::init()->set($name, $value, $expire); + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public static function inc($name, $step = 1) + { + self::$writeTimes++; + + return self::init()->inc($name, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public static function dec($name, $step = 1) + { + self::$writeTimes++; + + return self::init()->dec($name, $step); + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存标识 + * @return boolean + */ + public static function rm($name) + { + self::$writeTimes++; + + return self::init()->rm($name); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public static function clear($tag = null) + { + self::$writeTimes++; + + return self::init()->clear($tag); + } + + /** + * 读取缓存并删除 + * @access public + * @param string $name 缓存变量名 + * @return mixed + */ + public static function pull($name) + { + self::$readTimes++; + self::$writeTimes++; + + return self::init()->pull($name); + } + + /** + * 如果不存在则写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 + * @return mixed + */ + public static function remember($name, $value, $expire = null) + { + self::$readTimes++; + + return self::init()->remember($name, $value, $expire); + } + + /** + * 缓存标签 + * @access public + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 + * @return Driver + */ + public static function tag($name, $keys = null, $overlay = false) + { + return self::init()->tag($name, $keys, $overlay); + } + +} diff --git a/source/thinkphp/library/think/Collection.php b/source/thinkphp/library/think/Collection.php new file mode 100644 index 0000000..f872476 --- /dev/null +++ b/source/thinkphp/library/think/Collection.php @@ -0,0 +1,467 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use IteratorAggregate; +use JsonSerializable; + +class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable +{ + /** + * @var array 数据 + */ + protected $items = []; + + /** + * Collection constructor. + * @access public + * @param array $items 数据 + */ + public function __construct($items = []) + { + $this->items = $this->convertToArray($items); + } + + /** + * 创建 Collection 实例 + * @access public + * @param array $items 数据 + * @return static + */ + public static function make($items = []) + { + return new static($items); + } + + /** + * 判断数据是否为空 + * @access public + * @return bool + */ + public function isEmpty() + { + return empty($this->items); + } + + /** + * 将数据转成数组 + * @access public + * @return array + */ + public function toArray() + { + return array_map(function ($value) { + return ($value instanceof Model || $value instanceof self) ? + $value->toArray() : + $value; + }, $this->items); + } + + /** + * 获取全部的数据 + * @access public + * @return array + */ + public function all() + { + return $this->items; + } + + /** + * 交换数组中的键和值 + * @access public + * @return static + */ + public function flip() + { + return new static(array_flip($this->items)); + } + + /** + * 返回数组中所有的键名组成的新 Collection 实例 + * @access public + * @return static + */ + public function keys() + { + return new static(array_keys($this->items)); + } + + /** + * 返回数组中所有的值组成的新 Collection 实例 + * @access public + * @return static + */ + public function values() + { + return new static(array_values($this->items)); + } + + /** + * 合并数组并返回一个新的 Collection 实例 + * @access public + * @param mixed $items 新的数据 + * @return static + */ + public function merge($items) + { + return new static(array_merge($this->items, $this->convertToArray($items))); + } + + /** + * 比较数组,返回差集生成的新 Collection 实例 + * @access public + * @param mixed $items 做比较的数据 + * @return static + */ + public function diff($items) + { + return new static(array_diff($this->items, $this->convertToArray($items))); + } + + /** + * 比较数组,返回交集组成的 Collection 新实例 + * @access public + * @param mixed $items 比较数据 + * @return static + */ + public function intersect($items) + { + return new static(array_intersect($this->items, $this->convertToArray($items))); + } + + /** + * 返回并删除数据中的的最后一个元素(出栈) + * @access public + * @return mixed + */ + public function pop() + { + return array_pop($this->items); + } + + /** + * 返回并删除数据中首个元素 + * @access public + * @return mixed + */ + public function shift() + { + return array_shift($this->items); + } + + /** + * 在数组开头插入一个元素 + * @access public + * @param mixed $value 值 + * @param mixed $key 键名 + * @return void + */ + public function unshift($value, $key = null) + { + if (is_null($key)) { + array_unshift($this->items, $value); + } else { + $this->items = [$key => $value] + $this->items; + } + } + + /** + * 在数组结尾插入一个元素 + * @access public + * @param mixed $value 值 + * @param mixed $key 键名 + * @return void + */ + public function push($value, $key = null) + { + if (is_null($key)) { + $this->items[] = $value; + } else { + $this->items[$key] = $value; + } + } + + /** + * 通过使用用户自定义函数,以字符串返回数组 + * @access public + * @param callable $callback 回调函数 + * @param mixed $initial 初始值 + * @return mixed + */ + public function reduce(callable $callback, $initial = null) + { + return array_reduce($this->items, $callback, $initial); + } + + /** + * 以相反的顺序创建一个新的 Collection 实例 + * @access public + * @return static + */ + public function reverse() + { + return new static(array_reverse($this->items)); + } + + /** + * 把数据分割为新的数组块 + * @access public + * @param int $size 分隔长度 + * @param bool $preserveKeys 是否保持原数据索引 + * @return static + */ + public function chunk($size, $preserveKeys = false) + { + $chunks = []; + + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { + $chunks[] = new static($chunk); + } + + return new static($chunks); + } + + /** + * 给数据中的每个元素执行回调 + * @access public + * @param callable $callback 回调函数 + * @return $this + */ + public function each(callable $callback) + { + foreach ($this->items as $key => $item) { + $result = $callback($item, $key); + + if (false === $result) { + break; + } + + if (!is_object($item)) { + $this->items[$key] = $result; + } + } + + return $this; + } + + /** + * 用回调函数过滤数据中的元素 + * @access public + * @param callable|null $callback 回调函数 + * @return static + */ + public function filter(callable $callback = null) + { + return new static(array_filter($this->items, $callback ?: null)); + } + + /** + * 返回数据中指定的一列 + * @access public + * @param mixed $columnKey 键名 + * @param null $indexKey 作为索引值的列 + * @return array + */ + public function column($columnKey, $indexKey = null) + { + if (function_exists('array_column')) { + return array_column($this->items, $columnKey, $indexKey); + } + + $result = []; + foreach ($this->items as $row) { + $key = $value = null; + $keySet = $valueSet = false; + + if (null !== $indexKey && array_key_exists($indexKey, $row)) { + $key = (string) $row[$indexKey]; + $keySet = true; + } + + if (null === $columnKey) { + $valueSet = true; + $value = $row; + } elseif (is_array($row) && array_key_exists($columnKey, $row)) { + $valueSet = true; + $value = $row[$columnKey]; + } + + if ($valueSet) { + if ($keySet) { + $result[$key] = $value; + } else { + $result[] = $value; + } + } + } + + return $result; + } + + /** + * 对数据排序,并返回排序后的数据组成的新 Collection 实例 + * @access public + * @param callable|null $callback 回调函数 + * @return static + */ + public function sort(callable $callback = null) + { + $items = $this->items; + $callback = $callback ?: function ($a, $b) { + return $a == $b ? 0 : (($a < $b) ? -1 : 1); + }; + + uasort($items, $callback); + return new static($items); + } + + /** + * 将数据打乱后组成新的 Collection 实例 + * @access public + * @return static + */ + public function shuffle() + { + $items = $this->items; + + shuffle($items); + return new static($items); + } + + /** + * 截取数据并返回新的 Collection 实例 + * @access public + * @param int $offset 起始位置 + * @param int $length 截取长度 + * @param bool $preserveKeys 是否保持原先的键名 + * @return static + */ + public function slice($offset, $length = null, $preserveKeys = false) + { + return new static(array_slice($this->items, $offset, $length, $preserveKeys)); + } + + /** + * 指定的键是否存在 + * @access public + * @param mixed $offset 键名 + * @return bool + */ + public function offsetExists($offset) + { + return array_key_exists($offset, $this->items); + } + + /** + * 获取指定键对应的值 + * @access public + * @param mixed $offset 键名 + * @return mixed + */ + public function offsetGet($offset) + { + return $this->items[$offset]; + } + + /** + * 设置键值 + * @access public + * @param mixed $offset 键名 + * @param mixed $value 值 + * @return void + */ + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->items[] = $value; + } else { + $this->items[$offset] = $value; + } + } + + /** + * 删除指定键值 + * @access public + * @param mixed $offset 键名 + * @return void + */ + public function offsetUnset($offset) + { + unset($this->items[$offset]); + } + + /** + * 统计数据的个数 + * @access public + * @return int + */ + public function count() + { + return count($this->items); + } + + /** + * 获取数据的迭代器 + * @access public + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->items); + } + + /** + * 将数据反序列化成数组 + * @access public + * @return array + */ + public function jsonSerialize() + { + return $this->toArray(); + } + + /** + * 转换当前数据集为 JSON 字符串 + * @access public + * @param integer $options json 参数 + * @return string + */ + public function toJson($options = JSON_UNESCAPED_UNICODE) + { + return json_encode($this->toArray(), $options); + } + + /** + * 将数据转换成字符串 + * @access public + * @return string + */ + public function __toString() + { + return $this->toJson(); + } + + /** + * 将数据转换成数组 + * @access protected + * @param mixed $items 数据 + * @return array + */ + protected function convertToArray($items) + { + return $items instanceof self ? $items->all() : (array) $items; + } +} diff --git a/source/thinkphp/library/think/Config.php b/source/thinkphp/library/think/Config.php new file mode 100644 index 0000000..8fa668d --- /dev/null +++ b/source/thinkphp/library/think/Config.php @@ -0,0 +1,214 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Config +{ + /** + * @var array 配置参数 + */ + private static $config = []; + + /** + * @var string 参数作用域 + */ + private static $range = '_sys_'; + + /** + * 设定配置参数的作用域 + * @access public + * @param string $range 作用域 + * @return void + */ + public static function range($range) + { + self::$range = $range; + + if (!isset(self::$config[$range])) self::$config[$range] = []; + } + + /** + * 解析配置文件或内容 + * @access public + * @param string $config 配置文件路径或内容 + * @param string $type 配置解析类型 + * @param string $name 配置名(如设置即表示二级配置) + * @param string $range 作用域 + * @return mixed + */ + public static function parse($config, $type = '', $name = '', $range = '') + { + $range = $range ?: self::$range; + + if (empty($type)) $type = pathinfo($config, PATHINFO_EXTENSION); + + $class = false !== strpos($type, '\\') ? + $type : + '\\think\\config\\driver\\' . ucwords($type); + + return self::set((new $class())->parse($config), $name, $range); + } + + /** + * 加载配置文件(PHP格式) + * @access public + * @param string $file 配置文件名 + * @param string $name 配置名(如设置即表示二级配置) + * @param string $range 作用域 + * @return mixed + */ + public static function load($file, $name = '', $range = '') + { + $range = $range ?: self::$range; + + if (!isset(self::$config[$range])) self::$config[$range] = []; + + if (is_file($file)) { + $name = strtolower($name); + $type = pathinfo($file, PATHINFO_EXTENSION); + + if ('php' == $type) { + return self::set(include $file, $name, $range); + } + + if ('yaml' == $type && function_exists('yaml_parse_file')) { + return self::set(yaml_parse_file($file), $name, $range); + } + + return self::parse($file, $type, $name, $range); + } + + return self::$config[$range]; + } + + /** + * 检测配置是否存在 + * @access public + * @param string $name 配置参数名(支持二级配置 . 号分割) + * @param string $range 作用域 + * @return bool + */ + public static function has($name, $range = '') + { + $range = $range ?: self::$range; + + if (!strpos($name, '.')) { + return isset(self::$config[$range][strtolower($name)]); + } + + // 二维数组设置和获取支持 + $name = explode('.', $name, 2); + return isset(self::$config[$range][strtolower($name[0])][$name[1]]); + } + + /** + * 获取配置参数 为空则获取所有配置 + * @access public + * @param string $name 配置参数名(支持二级配置 . 号分割) + * @param string $range 作用域 + * @return mixed + */ + public static function get($name = null, $range = '') + { + $range = $range ?: self::$range; + + // 无参数时获取所有 + if (empty($name) && isset(self::$config[$range])) { + return self::$config[$range]; + } + + // 非二级配置时直接返回 + if (!strpos($name, '.')) { + $name = strtolower($name); + return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null; + } + + // 二维数组设置和获取支持 + $name = explode('.', $name, 2); + $name[0] = strtolower($name[0]); + + if (!isset(self::$config[$range][$name[0]])) { + // 动态载入额外配置 + $module = Request::instance()->module(); + $file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT; + + is_file($file) && self::load($file, $name[0]); + } + + return isset(self::$config[$range][$name[0]][$name[1]]) ? + self::$config[$range][$name[0]][$name[1]] : + null; + } + + /** + * 设置配置参数 name 为数组则为批量设置 + * @access public + * @param string|array $name 配置参数名(支持二级配置 . 号分割) + * @param mixed $value 配置值 + * @param string $range 作用域 + * @return mixed + */ + public static function set($name, $value = null, $range = '') + { + $range = $range ?: self::$range; + + if (!isset(self::$config[$range])) self::$config[$range] = []; + + // 字符串则表示单个配置设置 + if (is_string($name)) { + if (!strpos($name, '.')) { + self::$config[$range][strtolower($name)] = $value; + } else { + // 二维数组 + $name = explode('.', $name, 2); + self::$config[$range][strtolower($name[0])][$name[1]] = $value; + } + + return $value; + } + + // 数组则表示批量设置 + if (is_array($name)) { + if (!empty($value)) { + self::$config[$range][$value] = isset(self::$config[$range][$value]) ? + array_merge(self::$config[$range][$value], $name) : + $name; + + return self::$config[$range][$value]; + } + + return self::$config[$range] = array_merge( + self::$config[$range], array_change_key_case($name) + ); + } + + // 为空直接返回已有配置 + return self::$config[$range]; + } + + /** + * 重置配置参数 + * @access public + * @param string $range 作用域 + * @return void + */ + public static function reset($range = '') + { + $range = $range ?: self::$range; + + if (true === $range) { + self::$config = []; + } else { + self::$config[$range] = []; + } + } +} diff --git a/source/thinkphp/library/think/Console.php b/source/thinkphp/library/think/Console.php new file mode 100644 index 0000000..32b2572 --- /dev/null +++ b/source/thinkphp/library/think/Console.php @@ -0,0 +1,863 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\console\Command; +use think\console\command\Help as HelpCommand; +use think\console\Input; +use think\console\input\Argument as InputArgument; +use think\console\input\Definition as InputDefinition; +use think\console\input\Option as InputOption; +use think\console\Output; +use think\console\output\driver\Buffer; + +class Console +{ + /** + * @var string 命令名称 + */ + private $name; + + /** + * @var string 命令版本 + */ + private $version; + + /** + * @var Command[] 命令 + */ + private $commands = []; + + /** + * @var bool 是否需要帮助信息 + */ + private $wantHelps = false; + + /** + * @var bool 是否捕获异常 + */ + private $catchExceptions = true; + + /** + * @var bool 是否自动退出执行 + */ + private $autoExit = true; + + /** + * @var InputDefinition 输入定义 + */ + private $definition; + + /** + * @var string 默认执行的命令 + */ + private $defaultCommand; + + /** + * @var array 默认提供的命令 + */ + private static $defaultCommands = [ + "think\\console\\command\\Help", + "think\\console\\command\\Lists", + "think\\console\\command\\Build", + "think\\console\\command\\Clear", + "think\\console\\command\\make\\Controller", + "think\\console\\command\\make\\Model", + "think\\console\\command\\optimize\\Autoload", + "think\\console\\command\\optimize\\Config", + "think\\console\\command\\optimize\\Route", + "think\\console\\command\\optimize\\Schema", + ]; + + /** + * Console constructor. + * @access public + * @param string $name 名称 + * @param string $version 版本 + * @param null|string $user 执行用户 + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN', $user = null) + { + $this->name = $name; + $this->version = $version; + + if ($user) { + $this->setUser($user); + } + + $this->defaultCommand = 'list'; + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + /** + * 设置执行用户 + * @param $user + */ + public function setUser($user) + { + $user = posix_getpwnam($user); + if ($user) { + posix_setuid($user['uid']); + posix_setgid($user['gid']); + } + } + + /** + * 初始化 Console + * @access public + * @param bool $run 是否运行 Console + * @return int|Console + */ + public static function init($run = true) + { + static $console; + + if (!$console) { + $config = Config::get('console'); + // 实例化 console + $console = new self($config['name'], $config['version'], $config['user']); + + // 读取指令集 + if (is_file(CONF_PATH . 'command' . EXT)) { + $commands = include CONF_PATH . 'command' . EXT; + + if (is_array($commands)) { + foreach ($commands as $command) { + class_exists($command) && + is_subclass_of($command, "\\think\\console\\Command") && + $console->add(new $command()); // 注册指令 + } + } + } + } + + return $run ? $console->run() : $console; + } + + /** + * 调用命令 + * @access public + * @param string $command + * @param array $parameters + * @param string $driver + * @return Output + */ + public static function call($command, array $parameters = [], $driver = 'buffer') + { + $console = self::init(false); + + array_unshift($parameters, $command); + + $input = new Input($parameters); + $output = new Output($driver); + + $console->setCatchExceptions(false); + $console->find($command)->run($input, $output); + + return $output; + } + + /** + * 执行当前的指令 + * @access public + * @return int + * @throws \Exception + */ + public function run() + { + $input = new Input(); + $output = new Output(); + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) throw $e; + + $output->renderException($e); + + $exitCode = $e->getCode(); + + if (is_numeric($exitCode)) { + $exitCode = ((int) $exitCode) ?: 1; + } else { + $exitCode = 1; + } + } + + if ($this->autoExit) { + if ($exitCode > 255) $exitCode = 255; + + exit($exitCode); + } + + return $exitCode; + } + + /** + * 执行指令 + * @access public + * @param Input $input 输入 + * @param Output $output 输出 + * @return int + */ + public function doRun(Input $input, Output $output) + { + // 获取版本信息 + if (true === $input->hasParameterOption(['--version', '-V'])) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + $name = $this->getCommandName($input); + + // 获取帮助信息 + if (true === $input->hasParameterOption(['--help', '-h'])) { + if (!$name) { + $name = 'help'; + $input = new Input(['help']); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $input = new Input([$this->defaultCommand]); + } + + return $this->doRunCommand($this->find($name), $input, $output); + } + + /** + * 设置输入参数定义 + * @access public + * @param InputDefinition $definition 输入定义 + * @return $this; + */ + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + + return $this; + } + + /** + * 获取输入参数定义 + * @access public + * @return InputDefinition + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * 获取帮助信息 + * @access public + * @return string + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * 设置是否捕获异常 + * @access public + * @param bool $boolean 是否捕获 + * @return $this + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + + return $this; + } + + /** + * 设置是否自动退出 + * @access public + * @param bool $boolean 是否自动退出 + * @return $this + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + + return $this; + } + + /** + * 获取名称 + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 设置名称 + * @access public + * @param string $name 名称 + * @return $this + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * 获取版本 + * @access public + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * 设置版本 + * @access public + * @param string $version 版本信息 + * @return $this + */ + public function setVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * 获取完整的版本号 + * @access public + * @return string + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf( + '%s version %s', + $this->getName(), + $this->getVersion() + ); + } + + return 'Console Tool'; + } + + /** + * 注册一个指令 + * @access public + * @param string $name 指令名称 + * @return Command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * 批量添加指令 + * @access public + * @param Command[] $commands 指令实例 + * @return $this + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) $this->add($command); + + return $this; + } + + /** + * 添加一个指令 + * @access public + * @param Command $command 命令实例 + * @return Command|bool + */ + public function add(Command $command) + { + if (!$command->isEnabled()) { + $command->setConsole(null); + return false; + } + + $command->setConsole($this); + + if (null === $command->getDefinition()) { + throw new \LogicException( + sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)) + ); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * 获取指令 + * @access public + * @param string $name 指令名称 + * @return Command + * @throws \InvalidArgumentException + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new \InvalidArgumentException( + sprintf('The command "%s" does not exist.', $name) + ); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + /** @var HelpCommand $helpCommand */ + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * 某个指令是否存在 + * @access public + * @param string $name 指令名称 + * @return bool + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * 获取所有的命名空间 + * @access public + * @return array + */ + public function getNamespaces() + { + $namespaces = []; + + foreach ($this->commands as $command) { + $namespaces = array_merge( + $namespaces, $this->extractAllNamespaces($command->getName()) + ); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge( + $namespaces, $this->extractAllNamespaces($alias) + ); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * 查找注册命名空间中的名称或缩写 + * @access public + * @param string $namespace + * @return string + * @throws \InvalidArgumentException + */ + public function findNamespace($namespace) + { + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { + return preg_quote($matches[1]) . '[^:]*'; + }, $namespace); + + $allNamespaces = $this->getNamespaces(); + $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf( + 'There are no commands defined in the "%s" namespace.', $namespace + ); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + $exact = in_array($namespace, $namespaces, true); + + if (count($namespaces) > 1 && !$exact) { + throw new \InvalidArgumentException( + sprintf( + 'The namespace "%s" is ambiguous (%s).', + $namespace, + $this->getAbbreviationSuggestions(array_values($namespaces))) + ); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * 查找指令 + * @access public + * @param string $name 名称或者别名 + * @return Command + * @throws \InvalidArgumentException + */ + public function find($name) + { + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { + return preg_quote($matches[1]) . '[^:]*'; + }, $name); + + $allCommands = array_keys($this->commands); + $commands = preg_grep('{^' . $expr . '}', $allCommands); + + if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) { + if (false !== ($pos = strrpos($name, ':'))) { + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($commands) > 1) { + $commandList = $this->commands; + $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { + $commandName = $commandList[$nameOrAlias]->getName(); + + return $commandName === $nameOrAlias || !in_array($commandName, $commands); + }); + } + + $exact = in_array($name, $commands, true); + if (count($commands) > 1 && !$exact) { + $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); + + throw new \InvalidArgumentException( + sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions) + ); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * 获取所有的指令 + * @access public + * @param string $namespace 命名空间 + * @return Command[] + */ + public function all($namespace = null) + { + if (null === $namespace) return $this->commands; + + $commands = []; + + foreach ($this->commands as $name => $command) { + $ext = $this->extractNamespace($name, substr_count($namespace, ':') + 1); + + if ($ext === $namespace) $commands[$name] = $command; + } + + return $commands; + } + + /** + * 获取可能的指令名 + * @access public + * @param array $names 指令名 + * @return array + */ + public static function getAbbreviations($names) + { + $abbrevs = []; + foreach ($names as $name) { + for ($len = strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * 配置基于用户的参数和选项的输入和输出实例 + * @access protected + * @param Input $input 输入实例 + * @param Output $output 输出实例 + * @return void + */ + protected function configureIO(Input $input, Output $output) + { + if (true === $input->hasParameterOption(['--ansi'])) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(['--no-ansi'])) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(['--no-interaction', '-n'])) { + $input->setInteractive(false); + } + + if (true === $input->hasParameterOption(['--quiet', '-q'])) { + $output->setVerbosity(Output::VERBOSITY_QUIET); + } else { + if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) { + $output->setVerbosity(Output::VERBOSITY_DEBUG); + } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) { + $output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE); + } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) { + $output->setVerbosity(Output::VERBOSITY_VERBOSE); + } + } + } + + /** + * 执行指令 + * @access protected + * @param Command $command 指令实例 + * @param Input $input 输入实例 + * @param Output $output 输出实例 + * @return int + * @throws \Exception + */ + protected function doRunCommand(Command $command, Input $input, Output $output) + { + return $command->run($input, $output); + } + + /** + * 获取指令的名称 + * @access protected + * @param Input $input 输入实例 + * @return string + */ + protected function getCommandName(Input $input) + { + return $input->getFirstArgument(); + } + + /** + * 获取默认输入定义 + * @access protected + * @return InputDefinition + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition([ + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + ]); + } + + /** + * 获取默认命令 + * @access protected + * @return Command[] + */ + protected function getDefaultCommands() + { + $defaultCommands = []; + + foreach (self::$defaultCommands as $class) { + if (class_exists($class) && is_subclass_of($class, "think\\console\\Command")) { + $defaultCommands[] = new $class(); + } + } + + return $defaultCommands; + } + + /** + * 添加默认指令 + * @access public + * @param array $classes 指令 + * @return void + */ + public static function addDefaultCommands(array $classes) + { + self::$defaultCommands = array_merge(self::$defaultCommands, $classes); + } + + /** + * 获取可能的建议 + * @access private + * @param array $abbrevs + * @return string + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf( + '%s, %s%s', + $abbrevs[0], + $abbrevs[1], + count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '' + ); + } + + /** + * 返回指令的命名空间部分 + * @access public + * @param string $name 指令名称 + * @param string $limit 部分的命名空间的最大数量 + * @return string + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * 查找可替代的建议 + * @access private + * @param string $name 指令名称 + * @param array|\Traversable $collection 建议集合 + * @return array + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = []; + $collectionParts = []; + + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + + if ($lev <= strlen($subname) / 3 || + '' !== $subname && + false !== strpos($parts[$i], $subname) + ) { + $alternatives[$collectionName] = $exists ? + $alternatives[$collectionName] + $lev : + $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? + $alternatives[$item] - $lev : + $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { + return $lev < 2 * $threshold; + }); + + asort($alternatives); + + return array_keys($alternatives); + } + + /** + * 设置默认的指令 + * @access public + * @param string $commandName 指令名称 + * @return $this + */ + public function setDefaultCommand($commandName) + { + $this->defaultCommand = $commandName; + + return $this; + } + + /** + * 返回所有的命名空间 + * @access private + * @param string $name 指令名称 + * @return array + */ + private function extractAllNamespaces($name) + { + $namespaces = []; + + foreach (explode(':', $name, -1) as $part) { + if (count($namespaces)) { + $namespaces[] = end($namespaces) . ':' . $part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } + +} diff --git a/source/thinkphp/library/think/Controller.php b/source/thinkphp/library/think/Controller.php new file mode 100644 index 0000000..77225b7 --- /dev/null +++ b/source/thinkphp/library/think/Controller.php @@ -0,0 +1,229 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ValidateException; +use traits\controller\Jump; + +Loader::import('controller/Jump', TRAIT_PATH, EXT); + +class Controller +{ + use Jump; + + /** + * @var \think\View 视图类实例 + */ + protected $view; + + /** + * @var \think\Request Request 实例 + */ + protected $request; + + /** + * @var bool 验证失败是否抛出异常 + */ + protected $failException = false; + + /** + * @var bool 是否批量验证 + */ + protected $batchValidate = false; + + /** + * @var array 前置操作方法列表 + */ + protected $beforeActionList = []; + + /** + * 构造方法 + * @access public + * @param Request $request Request 对象 + */ + public function __construct(Request $request = null) + { + $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->request = is_null($request) ? Request::instance() : $request; + + // 控制器初始化 + $this->_initialize(); + + // 前置操作方法 + if ($this->beforeActionList) { + foreach ($this->beforeActionList as $method => $options) { + is_numeric($method) ? + $this->beforeAction($options) : + $this->beforeAction($method, $options); + } + } + } + + /** + * 初始化操作 + * @access protected + */ + protected function _initialize() + { + } + + /** + * 前置操作 + * @access protected + * @param string $method 前置操作方法名 + * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]] + * @return void + */ + protected function beforeAction($method, $options = []) + { + if (isset($options['only'])) { + if (is_string($options['only'])) { + $options['only'] = explode(',', $options['only']); + } + + if (!in_array($this->request->action(), $options['only'])) { + return; + } + } elseif (isset($options['except'])) { + if (is_string($options['except'])) { + $options['except'] = explode(',', $options['except']); + } + + if (in_array($this->request->action(), $options['except'])) { + return; + } + } + + call_user_func([$this, $method]); + } + + /** + * 加载模板输出 + * @access protected + * @param string $template 模板文件名 + * @param array $vars 模板输出变量 + * @param array $replace 模板替换 + * @param array $config 模板参数 + * @return mixed + */ + protected function fetch($template = '', $vars = [], $replace = [], $config = []) + { + return $this->view->fetch($template, $vars, $replace, $config); + } + + /** + * 渲染内容输出 + * @access protected + * @param string $content 模板内容 + * @param array $vars 模板输出变量 + * @param array $replace 替换内容 + * @param array $config 模板参数 + * @return mixed + */ + protected function display($content = '', $vars = [], $replace = [], $config = []) + { + return $this->view->display($content, $vars, $replace, $config); + } + + /** + * 模板变量赋值 + * @access protected + * @param mixed $name 要显示的模板变量 + * @param mixed $value 变量的值 + * @return $this + */ + protected function assign($name, $value = '') + { + $this->view->assign($name, $value); + + return $this; + } + + /** + * 初始化模板引擎 + * @access protected + * @param array|string $engine 引擎参数 + * @return $this + */ + protected function engine($engine) + { + $this->view->engine($engine); + + return $this; + } + + /** + * 设置验证失败后是否抛出异常 + * @access protected + * @param bool $fail 是否抛出异常 + * @return $this + */ + protected function validateFailException($fail = true) + { + $this->failException = $fail; + + return $this; + } + + /** + * 验证数据 + * @access protected + * @param array $data 数据 + * @param string|array $validate 验证器名或者验证规则数组 + * @param array $message 提示信息 + * @param bool $batch 是否批量验证 + * @param mixed $callback 回调方法(闭包) + * @return array|string|true + * @throws ValidateException + */ + protected function validate($data, $validate, $message = [], $batch = false, $callback = null) + { + if (is_array($validate)) { + $v = Loader::validate(); + $v->rule($validate); + } else { + // 支持场景 + if (strpos($validate, '.')) { + list($validate, $scene) = explode('.', $validate); + } + + $v = Loader::validate($validate); + + !empty($scene) && $v->scene($scene); + } + + // 批量验证 + if ($batch || $this->batchValidate) { + $v->batch(true); + } + + // 设置错误信息 + if (is_array($message)) { + $v->message($message); + } + + // 使用回调验证 + if ($callback && is_callable($callback)) { + call_user_func_array($callback, [$v, &$data]); + } + + if (!$v->check($data)) { + if ($this->failException) { + throw new ValidateException($v->getError()); + } + + return $v->getError(); + } + + return true; + } +} diff --git a/source/thinkphp/library/think/Cookie.php b/source/thinkphp/library/think/Cookie.php new file mode 100644 index 0000000..61b47cc --- /dev/null +++ b/source/thinkphp/library/think/Cookie.php @@ -0,0 +1,268 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Cookie +{ + /** + * @var array cookie 设置参数 + */ + protected static $config = [ + 'prefix' => '', // cookie 名称前缀 + 'expire' => 0, // cookie 保存时间 + 'path' => '/', // cookie 保存路径 + 'domain' => '', // cookie 有效域名 + 'secure' => false, // cookie 启用安全传输 + 'httponly' => false, // httponly 设置 + 'setcookie' => true, // 是否使用 setcookie + ]; + + /** + * @var bool 是否完成初始化了 + */ + protected static $init; + + /** + * Cookie初始化 + * @access public + * @param array $config 配置参数 + * @return void + */ + public static function init(array $config = []) + { + if (empty($config)) { + $config = Config::get('cookie'); + } + + self::$config = array_merge(self::$config, array_change_key_case($config)); + + if (!empty(self::$config['httponly'])) { + ini_set('session.cookie_httponly', 1); + } + + self::$init = true; + } + + /** + * 设置或者获取 cookie 作用域(前缀) + * @access public + * @param string $prefix 前缀 + * @return string| + */ + public static function prefix($prefix = '') + { + if (empty($prefix)) { + return self::$config['prefix']; + } + + return self::$config['prefix'] = $prefix; + } + + /** + * Cookie 设置、获取、删除 + * @access public + * @param string $name cookie 名称 + * @param mixed $value cookie 值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void + */ + public static function set($name, $value = '', $option = null) + { + !isset(self::$init) && self::init(); + + // 参数设置(会覆盖黙认设置) + if (!is_null($option)) { + if (is_numeric($option)) { + $option = ['expire' => $option]; + } elseif (is_string($option)) { + parse_str($option, $option); + } + + $config = array_merge(self::$config, array_change_key_case($option)); + } else { + $config = self::$config; + } + + $name = $config['prefix'] . $name; + + // 设置 cookie + if (is_array($value)) { + array_walk_recursive($value, 'self::jsonFormatProtect', 'encode'); + $value = 'think:' . json_encode($value); + } + + $expire = !empty($config['expire']) ? + $_SERVER['REQUEST_TIME'] + intval($config['expire']) : + 0; + + if ($config['setcookie']) { + setcookie( + $name, $value, $expire, $config['path'], $config['domain'], + $config['secure'], $config['httponly'] + ); + } + + $_COOKIE[$name] = $value; + } + + /** + * 永久保存 Cookie 数据 + * @access public + * @param string $name cookie 名称 + * @param mixed $value cookie 值 + * @param mixed $option 可选参数 可能会是 null|integer|string + * @return void + */ + public static function forever($name, $value = '', $option = null) + { + if (is_null($option) || is_numeric($option)) { + $option = []; + } + + $option['expire'] = 315360000; + + self::set($name, $value, $option); + } + + /** + * 判断是否有 Cookie 数据 + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 + * @return bool + */ + public static function has($name, $prefix = null) + { + !isset(self::$init) && self::init(); + + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; + + return isset($_COOKIE[$prefix . $name]); + } + + /** + * 获取 Cookie 的值 + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 + * @return mixed + */ + public static function get($name = '', $prefix = null) + { + !isset(self::$init) && self::init(); + + $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; + $key = $prefix . $name; + + if ('' == $name) { + // 获取全部 + if ($prefix) { + $value = []; + + foreach ($_COOKIE as $k => $val) { + if (0 === strpos($k, $prefix)) { + $value[$k] = $val; + } + + } + } else { + $value = $_COOKIE; + } + } elseif (isset($_COOKIE[$key])) { + $value = $_COOKIE[$key]; + + if (0 === strpos($value, 'think:')) { + $value = json_decode(substr($value, 6), true); + array_walk_recursive($value, 'self::jsonFormatProtect', 'decode'); + } + } else { + $value = null; + } + + return $value; + } + + /** + * 删除 Cookie + * @access public + * @param string $name cookie 名称 + * @param string|null $prefix cookie 前缀 + * @return void + */ + public static function delete($name, $prefix = null) + { + !isset(self::$init) && self::init(); + + $config = self::$config; + $prefix = !is_null($prefix) ? $prefix : $config['prefix']; + $name = $prefix . $name; + + if ($config['setcookie']) { + setcookie( + $name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], + $config['domain'], $config['secure'], $config['httponly'] + ); + } + + // 删除指定 cookie + unset($_COOKIE[$name]); + } + + /** + * 清除指定前缀的所有 cookie + * @access public + * @param string|null $prefix cookie 前缀 + * @return void + */ + public static function clear($prefix = null) + { + if (empty($_COOKIE)) { + return; + } + + !isset(self::$init) && self::init(); + + // 要删除的 cookie 前缀,不指定则删除 config 设置的指定前缀 + $config = self::$config; + $prefix = !is_null($prefix) ? $prefix : $config['prefix']; + + if ($prefix) { + foreach ($_COOKIE as $key => $val) { + if (0 === strpos($key, $prefix)) { + if ($config['setcookie']) { + setcookie( + $key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], + $config['domain'], $config['secure'], $config['httponly'] + ); + } + + unset($_COOKIE[$key]); + } + } + } + } + + /** + * json 转换时的格式保护 + * @access protected + * @param mixed $val 要转换的值 + * @param string $key 键名 + * @param string $type 转换类别 + * @return void + */ + protected static function jsonFormatProtect(&$val, $key, $type = 'encode') + { + if (!empty($val) && true !== $val) { + $val = 'decode' == $type ? urldecode($val) : urlencode($val); + } + } +} diff --git a/source/thinkphp/library/think/Db.php b/source/thinkphp/library/think/Db.php new file mode 100644 index 0000000..80f08d2 --- /dev/null +++ b/source/thinkphp/library/think/Db.php @@ -0,0 +1,180 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\db\Connection; +use think\db\Query; + +/** + * Class Db + * @package think + * @method Query table(string $table) static 指定数据表(含前缀) + * @method Query name(string $name) static 指定数据表(不含前缀) + * @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件 + * @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询 + * @method Query union(mixed $union, boolean $all = false) static UNION查询 + * @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT + * @method Query order(mixed $field, string $order = null) static 查询ORDER + * @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存 + * @method mixed value(string $field) static 获取某个字段的值 + * @method array column(string $field, string $key = '') static 获取某个列的值 + * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询 + * @method mixed find(mixed $data = null) static 查询单个记录 + * @method mixed select(mixed $data = null) static 查询多个记录 + * @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录 + * @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID + * @method integer insertAll(array $dataSet) static 插入多条记录 + * @method integer update(array $data) static 更新记录 + * @method integer delete(mixed $data = null) static 删除记录 + * @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据 + * @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询 + * @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行 + * @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询 + * @method mixed transaction(callable $callback) static 执行数据库事务 + * @method void startTrans() static 启动事务 + * @method void commit() static 用于非自动提交状态下面的查询提交 + * @method void rollback() static 事务回滚 + * @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句 + * @method string quote(string $str) static SQL指令安全过滤 + * @method string getLastInsID($sequence = null) static 获取最近插入的ID + */ +class Db +{ + /** + * @var Connection[] 数据库连接实例 + */ + private static $instance = []; + + /** + * @var int 查询次数 + */ + public static $queryTimes = 0; + + /** + * @var int 执行次数 + */ + public static $executeTimes = 0; + + /** + * 数据库初始化,并取得数据库类实例 + * @access public + * @param mixed $config 连接配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return Connection + * @throws Exception + */ + public static function connect($config = [], $name = false) + { + if (false === $name) { + $name = md5(serialize($config)); + } + + if (true === $name || !isset(self::$instance[$name])) { + // 解析连接参数 支持数组和字符串 + $options = self::parseConfig($config); + + if (empty($options['type'])) { + throw new \InvalidArgumentException('Undefined db type'); + } + + $class = false !== strpos($options['type'], '\\') ? + $options['type'] : + '\\think\\db\\connector\\' . ucwords($options['type']); + + // 记录初始化信息 + if (App::$debug) { + Log::record('[ DB ] INIT ' . $options['type'], 'info'); + } + + if (true === $name) { + $name = md5(serialize($config)); + } + + self::$instance[$name] = new $class($options); + } + + return self::$instance[$name]; + } + + /** + * 清除连接实例 + * @access public + * @return void + */ + public static function clear() + { + self::$instance = []; + } + + /** + * 数据库连接参数解析 + * @access private + * @param mixed $config 连接参数 + * @return array + */ + private static function parseConfig($config) + { + if (empty($config)) { + $config = Config::get('database'); + } elseif (is_string($config) && false === strpos($config, '/')) { + $config = Config::get($config); // 支持读取配置参数 + } + + return is_string($config) ? self::parseDsn($config) : $config; + } + + /** + * DSN 解析 + * 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8 + * @access private + * @param string $dsnStr 数据库 DSN 字符串解析 + * @return array + */ + private static function parseDsn($dsnStr) + { + $info = parse_url($dsnStr); + + if (!$info) { + return []; + } + + $dsn = [ + 'type' => $info['scheme'], + 'username' => isset($info['user']) ? $info['user'] : '', + 'password' => isset($info['pass']) ? $info['pass'] : '', + 'hostname' => isset($info['host']) ? $info['host'] : '', + 'hostport' => isset($info['port']) ? $info['port'] : '', + 'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '', + 'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8', + ]; + + if (isset($info['query'])) { + parse_str($info['query'], $dsn['params']); + } else { + $dsn['params'] = []; + } + + return $dsn; + } + + /** + * 调用驱动类的方法 + * @access public + * @param string $method 方法名 + * @param array $params 参数 + * @return mixed + */ + public static function __callStatic($method, $params) + { + return call_user_func_array([self::connect(), $method], $params); + } +} diff --git a/source/thinkphp/library/think/Debug.php b/source/thinkphp/library/think/Debug.php new file mode 100644 index 0000000..df48748 --- /dev/null +++ b/source/thinkphp/library/think/Debug.php @@ -0,0 +1,252 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; +use think\response\Redirect; + +class Debug +{ + /** + * @var array 区间时间信息 + */ + protected static $info = []; + + /** + * @var array 区间内存信息 + */ + protected static $mem = []; + + /** + * 记录时间(微秒)和内存使用情况 + * @access public + * @param string $name 标记位置 + * @param mixed $value 标记值(留空则取当前 time 表示仅记录时间 否则同时记录时间和内存) + * @return void + */ + public static function remark($name, $value = '') + { + self::$info[$name] = is_float($value) ? $value : microtime(true); + + if ('time' != $value) { + self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage(); + self::$mem['peak'][$name] = memory_get_peak_usage(); + } + } + + /** + * 统计某个区间的时间(微秒)使用情况 返回值以秒为单位 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 + * @return string + */ + public static function getRangeTime($start, $end, $dec = 6) + { + if (!isset(self::$info[$end])) { + self::$info[$end] = microtime(true); + } + + return number_format((self::$info[$end] - self::$info[$start]), $dec); + } + + /** + * 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位 + * @access public + * @param integer $dec 小数位 + * @return string + */ + public static function getUseTime($dec = 6) + { + return number_format((microtime(true) - THINK_START_TIME), $dec); + } + + /** + * 获取当前访问的吞吐率情况 + * @access public + * @return string + */ + public static function getThroughputRate() + { + return number_format(1 / self::getUseTime(), 2) . 'req/s'; + } + + /** + * 记录区间的内存使用情况 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 + * @return string + */ + public static function getRangeMem($start, $end, $dec = 2) + { + if (!isset(self::$mem['mem'][$end])) { + self::$mem['mem'][$end] = memory_get_usage(); + } + + $size = self::$mem['mem'][$end] - self::$mem['mem'][$start]; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + + while ($size >= 1024) { + $size /= 1024; + $pos++; + } + + return round($size, $dec) . " " . $a[$pos]; + } + + /** + * 统计从开始到统计时的内存使用情况 + * @access public + * @param integer $dec 小数位 + * @return string + */ + public static function getUseMem($dec = 2) + { + $size = memory_get_usage() - THINK_START_MEM; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + + while ($size >= 1024) { + $size /= 1024; + $pos++; + } + + return round($size, $dec) . " " . $a[$pos]; + } + + /** + * 统计区间的内存峰值情况 + * @access public + * @param string $start 开始标签 + * @param string $end 结束标签 + * @param integer $dec 小数位 + * @return string + */ + public static function getMemPeak($start, $end, $dec = 2) + { + if (!isset(self::$mem['peak'][$end])) { + self::$mem['peak'][$end] = memory_get_peak_usage(); + } + + $size = self::$mem['peak'][$end] - self::$mem['peak'][$start]; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + + while ($size >= 1024) { + $size /= 1024; + $pos++; + } + + return round($size, $dec) . " " . $a[$pos]; + } + + /** + * 获取文件加载信息 + * @access public + * @param bool $detail 是否显示详细 + * @return integer|array + */ + public static function getFile($detail = false) + { + $files = get_included_files(); + + if ($detail) { + $info = []; + + foreach ($files as $file) { + $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; + } + + return $info; + } + + return count($files); + } + + /** + * 浏览器友好的变量输出 + * @access public + * @param mixed $var 变量 + * @param boolean $echo 是否输出(默认为 true,为 false 则返回输出字符串) + * @param string|null $label 标签(默认为空) + * @param integer $flags htmlspecialchars 的标志 + * @return null|string + */ + public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE) + { + $label = (null === $label) ? '' : rtrim($label) . ':'; + + ob_start(); + var_dump($var); + $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', ob_get_clean()); + + if (IS_CLI) { + $output = PHP_EOL . $label . $output . PHP_EOL; + } else { + if (!extension_loaded('xdebug')) { + $output = htmlspecialchars($output, $flags); + } + + $output = '
' . $label . $output . '
'; + } + + if ($echo) { + echo($output); + return; + } + + return $output; + } + + /** + * 调试信息注入到响应中 + * @access public + * @param Response $response 响应实例 + * @param string $content 返回的字符串 + * @return void + */ + public static function inject(Response $response, &$content) + { + $config = Config::get('trace'); + $type = isset($config['type']) ? $config['type'] : 'Html'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type); + + unset($config['type']); + + if (!class_exists($class)) { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + + /** @var \think\debug\Console|\think\debug\Html $trace */ + $trace = new $class($config); + + if ($response instanceof Redirect) { + // TODO 记录 + } else { + $output = $trace->output($response, Log::getLog()); + + if (is_string($output)) { + // trace 调试信息注入 + $pos = strripos($content, ''); + if (false !== $pos) { + $content = substr($content, 0, $pos) . $output . substr($content, $pos); + } else { + $content = $content . $output; + } + } + } + } +} diff --git a/source/thinkphp/library/think/Env.php b/source/thinkphp/library/think/Env.php new file mode 100644 index 0000000..0a8b250 --- /dev/null +++ b/source/thinkphp/library/think/Env.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Env +{ + /** + * 获取环境变量值 + * @access public + * @param string $name 环境变量名(支持二级 . 号分割) + * @param string $default 默认值 + * @return mixed + */ + public static function get($name, $default = null) + { + $result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name))); + + if (false !== $result) { + if ('false' === $result) { + $result = false; + } elseif ('true' === $result) { + $result = true; + } + + return $result; + } + + return $default; + } +} diff --git a/source/thinkphp/library/think/Error.php b/source/thinkphp/library/think/Error.php new file mode 100644 index 0000000..5f361d5 --- /dev/null +++ b/source/thinkphp/library/think/Error.php @@ -0,0 +1,136 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\console\Output as ConsoleOutput; +use think\exception\ErrorException; +use think\exception\Handle; +use think\exception\ThrowableError; + +class Error +{ + /** + * 注册异常处理 + * @access public + * @return void + */ + public static function register() + { + error_reporting(E_ALL); + set_error_handler([__CLASS__, 'appError']); + set_exception_handler([__CLASS__, 'appException']); + register_shutdown_function([__CLASS__, 'appShutdown']); + } + + /** + * 异常处理 + * @access public + * @param \Exception|\Throwable $e 异常 + * @return void + */ + public static function appException($e) + { + if (!$e instanceof \Exception) { + $e = new ThrowableError($e); + } + + $handler = self::getExceptionHandler(); + $handler->report($e); + + if (IS_CLI) { + $handler->renderForConsole(new ConsoleOutput, $e); + } else { + $handler->render($e)->send(); + } + } + + /** + * 错误处理 + * @access public + * @param integer $errno 错误编号 + * @param integer $errstr 详细错误信息 + * @param string $errfile 出错的文件 + * @param integer $errline 出错行号 + * @return void + * @throws ErrorException + */ + public static function appError($errno, $errstr, $errfile = '', $errline = 0) + { + $exception = new ErrorException($errno, $errstr, $errfile, $errline); + + // 符合异常处理的则将错误信息托管至 think\exception\ErrorException + if (error_reporting() & $errno) { + throw $exception; + } + + self::getExceptionHandler()->report($exception); + } + + /** + * 异常中止处理 + * @access public + * @return void + */ + public static function appShutdown() + { + // 将错误信息托管至 think\ErrorException + if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) { + self::appException(new ErrorException( + $error['type'], $error['message'], $error['file'], $error['line'] + )); + } + + // 写入日志 + Log::save(); + } + + /** + * 确定错误类型是否致命 + * @access protected + * @param int $type 错误类型 + * @return bool + */ + protected static function isFatal($type) + { + return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]); + } + + /** + * 获取异常处理的实例 + * @access public + * @return Handle + */ + public static function getExceptionHandler() + { + static $handle; + + if (!$handle) { + // 异常处理 handle + $class = Config::get('exception_handle'); + + if ($class && is_string($class) && class_exists($class) && + is_subclass_of($class, "\\think\\exception\\Handle") + ) { + $handle = new $class; + } else { + $handle = new Handle; + + if ($class instanceof \Closure) { + $handle->setRender($class); + } + + } + } + + return $handle; + } +} diff --git a/source/thinkphp/library/think/Exception.php b/source/thinkphp/library/think/Exception.php new file mode 100644 index 0000000..1ef06bd --- /dev/null +++ b/source/thinkphp/library/think/Exception.php @@ -0,0 +1,55 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Exception extends \Exception +{ + /** + * @var array 保存异常页面显示的额外 Debug 数据 + */ + protected $data = []; + + /** + * 设置异常额外的 Debug 数据 + * 数据将会显示为下面的格式 + * + * Exception Data + * -------------------------------------------------- + * Label 1 + * key1 value1 + * key2 value2 + * Label 2 + * key1 value1 + * key2 value2 + * + * @access protected + * @param string $label 数据分类,用于异常页面显示 + * @param array $data 需要显示的数据,必须为关联数组 + * @return void + */ + final protected function setData($label, array $data) + { + $this->data[$label] = $data; + } + + /** + * 获取异常额外 Debug 数据 + * 主要用于输出到异常页面便于调试 + * @access public + * @return array + */ + final public function getData() + { + return $this->data; + } + +} diff --git a/source/thinkphp/library/think/File.php b/source/thinkphp/library/think/File.php new file mode 100644 index 0000000..d2ed220 --- /dev/null +++ b/source/thinkphp/library/think/File.php @@ -0,0 +1,478 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use SplFileObject; + +class File extends SplFileObject +{ + /** + * @var string 错误信息 + */ + private $error = ''; + + /** + * @var string 当前完整文件名 + */ + protected $filename; + + /** + * @var string 上传文件名 + */ + protected $saveName; + + /** + * @var string 文件上传命名规则 + */ + protected $rule = 'date'; + + /** + * @var array 文件上传验证规则 + */ + protected $validate = []; + + /** + * @var bool 单元测试 + */ + protected $isTest; + + /** + * @var array 上传文件信息 + */ + protected $info; + + /** + * @var array 文件 hash 信息 + */ + protected $hash = []; + + /** + * File constructor. + * @access public + * @param string $filename 文件名称 + * @param string $mode 访问模式 + */ + public function __construct($filename, $mode = 'r') + { + parent::__construct($filename, $mode); + $this->filename = $this->getRealPath() ?: $this->getPathname(); + } + + /** + * 设置是否是单元测试 + * @access public + * @param bool $test 是否是测试 + * @return $this + */ + public function isTest($test = false) + { + $this->isTest = $test; + + return $this; + } + + /** + * 设置上传信息 + * @access public + * @param array $info 上传文件信息 + * @return $this + */ + public function setUploadInfo($info) + { + $this->info = $info; + + return $this; + } + + /** + * 获取上传文件的信息 + * @access public + * @param string $name 信息名称 + * @return array|string + */ + public function getInfo($name = '') + { + return isset($this->info[$name]) ? $this->info[$name] : $this->info; + } + + /** + * 获取上传文件的文件名 + * @access public + * @return string + */ + public function getSaveName() + { + return $this->saveName; + } + + /** + * 设置上传文件的保存文件名 + * @access public + * @param string $saveName 保存名称 + * @return $this + */ + public function setSaveName($saveName) + { + $this->saveName = $saveName; + + return $this; + } + + /** + * 获取文件的哈希散列值 + * @access public + * @param string $type 类型 + * @return string + */ + public function hash($type = 'sha1') + { + if (!isset($this->hash[$type])) { + $this->hash[$type] = hash_file($type, $this->filename); + } + + return $this->hash[$type]; + } + + /** + * 检查目录是否可写 + * @access protected + * @param string $path 目录 + * @return boolean + */ + protected function checkPath($path) + { + if (is_dir($path) || mkdir($path, 0755, true)) { + return true; + } + + $this->error = ['directory {:path} creation failed', ['path' => $path]]; + + return false; + } + + /** + * 获取文件类型信息 + * @access public + * @return string + */ + public function getMime() + { + $finfo = finfo_open(FILEINFO_MIME_TYPE); + + return finfo_file($finfo, $this->filename); + } + + /** + * 设置文件的命名规则 + * @access public + * @param string $rule 文件命名规则 + * @return $this + */ + public function rule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * 设置上传文件的验证规则 + * @access public + * @param array $rule 验证规则 + * @return $this + */ + public function validate(array $rule = []) + { + $this->validate = $rule; + + return $this; + } + + /** + * 检测是否合法的上传文件 + * @access public + * @return bool + */ + public function isValid() + { + return $this->isTest ? is_file($this->filename) : is_uploaded_file($this->filename); + } + + /** + * 检测上传文件 + * @access public + * @param array $rule 验证规则 + * @return bool + */ + public function check($rule = []) + { + $rule = $rule ?: $this->validate; + + /* 检查文件大小 */ + if (isset($rule['size']) && !$this->checkSize($rule['size'])) { + $this->error = 'filesize not match'; + return false; + } + + /* 检查文件 Mime 类型 */ + if (isset($rule['type']) && !$this->checkMime($rule['type'])) { + $this->error = 'mimetype to upload is not allowed'; + return false; + } + + /* 检查文件后缀 */ + if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) { + $this->error = 'extensions to upload is not allowed'; + return false; + } + + /* 检查图像文件 */ + if (!$this->checkImg()) { + $this->error = 'illegal image files'; + return false; + } + + return true; + } + + /** + * 检测上传文件后缀 + * @access public + * @param array|string $ext 允许后缀 + * @return bool + */ + public function checkExt($ext) + { + if (is_string($ext)) { + $ext = explode(',', $ext); + } + + $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); + + return in_array($extension, $ext); + } + + /** + * 检测图像文件 + * @access public + * @return bool + */ + public function checkImg() + { + $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION)); + + // 如果上传的不是图片,或者是图片而且后缀确实符合图片类型则返回 true + return !in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) || in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13]); + } + + /** + * 判断图像类型 + * @access protected + * @param string $image 图片名称 + * @return bool|int + */ + protected function getImageType($image) + { + if (function_exists('exif_imagetype')) { + return exif_imagetype($image); + } + + try { + $info = getimagesize($image); + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; + } + } + + /** + * 检测上传文件大小 + * @access public + * @param integer $size 最大大小 + * @return bool + */ + public function checkSize($size) + { + return $this->getSize() <= $size; + } + + /** + * 检测上传文件类型 + * @access public + * @param array|string $mime 允许类型 + * @return bool + */ + public function checkMime($mime) + { + $mime = is_string($mime) ? explode(',', $mime) : $mime; + + return in_array(strtolower($this->getMime()), $mime); + } + + /** + * 移动文件 + * @access public + * @param string $path 保存路径 + * @param string|bool $savename 保存的文件名 默认自动生成 + * @param boolean $replace 同名文件是否覆盖 + * @return false|File + */ + public function move($path, $savename = true, $replace = true) + { + // 文件上传失败,捕获错误代码 + if (!empty($this->info['error'])) { + $this->error($this->info['error']); + return false; + } + + // 检测合法性 + if (!$this->isValid()) { + $this->error = 'upload illegal files'; + return false; + } + + // 验证上传 + if (!$this->check()) { + return false; + } + + $path = rtrim($path, DS) . DS; + // 文件保存命名规则 + $saveName = $this->buildSaveName($savename); + $filename = $path . $saveName; + + // 检测目录 + if (false === $this->checkPath(dirname($filename))) { + return false; + } + + // 不覆盖同名文件 + if (!$replace && is_file($filename)) { + $this->error = ['has the same filename: {:filename}', ['filename' => $filename]]; + return false; + } + + /* 移动文件 */ + if ($this->isTest) { + rename($this->filename, $filename); + } elseif (!move_uploaded_file($this->filename, $filename)) { + $this->error = 'upload write error'; + return false; + } + + // 返回 File 对象实例 + $file = new self($filename); + $file->setSaveName($saveName)->setUploadInfo($this->info); + + return $file; + } + + /** + * 获取保存文件名 + * @access protected + * @param string|bool $savename 保存的文件名 默认自动生成 + * @return string + */ + protected function buildSaveName($savename) + { + // 自动生成文件名 + if (true === $savename) { + if ($this->rule instanceof \Closure) { + $savename = call_user_func_array($this->rule, [$this]); + } else { + switch ($this->rule) { + case 'date': + $savename = date('Ymd') . DS . md5(microtime(true)); + break; + default: + if (in_array($this->rule, hash_algos())) { + $hash = $this->hash($this->rule); + $savename = substr($hash, 0, 2) . DS . substr($hash, 2); + } elseif (is_callable($this->rule)) { + $savename = call_user_func($this->rule); + } else { + $savename = date('Ymd') . DS . md5(microtime(true)); + } + } + } + } elseif ('' === $savename || false === $savename) { + $savename = $this->getInfo('name'); + } + + if (!strpos($savename, '.')) { + $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION); + } + + return $savename; + } + + /** + * 获取错误代码信息 + * @access private + * @param int $errorNo 错误号 + * @return $this + */ + private function error($errorNo) + { + switch ($errorNo) { + case 1: + case 2: + $this->error = 'upload File size exceeds the maximum value'; + break; + case 3: + $this->error = 'only the portion of file is uploaded'; + break; + case 4: + $this->error = 'no file to uploaded'; + break; + case 6: + $this->error = 'upload temp dir not found'; + break; + case 7: + $this->error = 'file write error'; + break; + default: + $this->error = 'unknown upload error'; + } + + return $this; + } + + /** + * 获取错误信息(支持多语言) + * @access public + * @return string + */ + public function getError() + { + if (is_array($this->error)) { + list($msg, $vars) = $this->error; + } else { + $msg = $this->error; + $vars = []; + } + + return Lang::has($msg) ? Lang::get($msg, $vars) : $msg; + } + + /** + * 魔法方法,获取文件的 hash 值 + * @access public + * @param string $method 方法名 + * @param mixed $args 调用参数 + * @return string + */ + public function __call($method, $args) + { + return $this->hash($method); + } +} diff --git a/source/thinkphp/library/think/Hook.php b/source/thinkphp/library/think/Hook.php new file mode 100644 index 0000000..a69ce54 --- /dev/null +++ b/source/thinkphp/library/think/Hook.php @@ -0,0 +1,148 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Hook +{ + /** + * @var array 标签 + */ + private static $tags = []; + + /** + * 动态添加行为扩展到某个标签 + * @access public + * @param string $tag 标签名称 + * @param mixed $behavior 行为名称 + * @param bool $first 是否放到开头执行 + * @return void + */ + public static function add($tag, $behavior, $first = false) + { + isset(self::$tags[$tag]) || self::$tags[$tag] = []; + + if (is_array($behavior) && !is_callable($behavior)) { + if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) { + unset($behavior['_overlay']); + self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior); + } else { + unset($behavior['_overlay']); + self::$tags[$tag] = $behavior; + } + } elseif ($first) { + array_unshift(self::$tags[$tag], $behavior); + } else { + self::$tags[$tag][] = $behavior; + } + } + + /** + * 批量导入插件 + * @access public + * @param array $tags 插件信息 + * @param boolean $recursive 是否递归合并 + * @return void + */ + public static function import(array $tags, $recursive = true) + { + if ($recursive) { + foreach ($tags as $tag => $behavior) { + self::add($tag, $behavior); + } + } else { + self::$tags = $tags + self::$tags; + } + } + + /** + * 获取插件信息 + * @access public + * @param string $tag 插件位置(留空获取全部) + * @return array + */ + public static function get($tag = '') + { + if (empty($tag)) { + return self::$tags; + } + + return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : []; + } + + /** + * 监听标签的行为 + * @access public + * @param string $tag 标签名称 + * @param mixed $params 传入参数 + * @param mixed $extra 额外参数 + * @param bool $once 只获取一个有效返回值 + * @return mixed + */ + public static function listen($tag, &$params = null, $extra = null, $once = false) + { + $results = []; + + foreach (static::get($tag) as $key => $name) { + $results[$key] = self::exec($name, $tag, $params, $extra); + + // 如果返回 false,或者仅获取一个有效返回则中断行为执行 + if (false === $results[$key] || (!is_null($results[$key]) && $once)) { + break; + } + } + + return $once ? end($results) : $results; + } + + /** + * 执行某个行为 + * @access public + * @param mixed $class 要执行的行为 + * @param string $tag 方法名(标签名) + * @param mixed $params 传人的参数 + * @param mixed $extra 额外参数 + * @return mixed + */ + public static function exec($class, $tag = '', &$params = null, $extra = null) + { + App::$debug && Debug::remark('behavior_start', 'time'); + + $method = Loader::parseName($tag, 1, false); + + if ($class instanceof \Closure) { + $result = call_user_func_array($class, [ & $params, $extra]); + $class = 'Closure'; + } elseif (is_array($class)) { + list($class, $method) = $class; + + $result = (new $class())->$method($params, $extra); + $class = $class . '->' . $method; + } elseif (is_object($class)) { + $result = $class->$method($params, $extra); + $class = get_class($class); + } elseif (strpos($class, '::')) { + $result = call_user_func_array($class, [ & $params, $extra]); + } else { + $obj = new $class(); + $method = ($tag && is_callable([$obj, $method])) ? $method : 'run'; + $result = $obj->$method($params, $extra); + } + + if (App::$debug) { + Debug::remark('behavior_end', 'time'); + Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info'); + } + + return $result; + } + +} diff --git a/source/thinkphp/library/think/Lang.php b/source/thinkphp/library/think/Lang.php new file mode 100644 index 0000000..a50d838 --- /dev/null +++ b/source/thinkphp/library/think/Lang.php @@ -0,0 +1,265 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Lang +{ + /** + * @var array 语言数据 + */ + private static $lang = []; + + /** + * @var string 语言作用域 + */ + private static $range = 'zh-cn'; + + /** + * @var string 语言自动侦测的变量 + */ + protected static $langDetectVar = 'lang'; + + /** + * @var string 语言 Cookie 变量 + */ + protected static $langCookieVar = 'think_var'; + + /** + * @var int 语言 Cookie 的过期时间 + */ + protected static $langCookieExpire = 3600; + + /** + * @var array 允许语言列表 + */ + protected static $allowLangList = []; + + /** + * @var array Accept-Language 转义为对应语言包名称 系统默认配置 + */ + protected static $acceptLanguage = ['zh-hans-cn' => 'zh-cn']; + + /** + * 设定当前的语言 + * @access public + * @param string $range 语言作用域 + * @return string + */ + public static function range($range = '') + { + if ($range) { + self::$range = $range; + } + + return self::$range; + } + + /** + * 设置语言定义(不区分大小写) + * @access public + * @param string|array $name 语言变量 + * @param string $value 语言值 + * @param string $range 语言作用域 + * @return mixed + */ + public static function set($name, $value = null, $range = '') + { + $range = $range ?: self::$range; + + if (!isset(self::$lang[$range])) { + self::$lang[$range] = []; + } + + if (is_array($name)) { + return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range]; + } + + return self::$lang[$range][strtolower($name)] = $value; + } + + /** + * 加载语言定义(不区分大小写) + * @access public + * @param array|string $file 语言文件 + * @param string $range 语言作用域 + * @return mixed + */ + public static function load($file, $range = '') + { + $range = $range ?: self::$range; + $file = is_string($file) ? [$file] : $file; + + if (!isset(self::$lang[$range])) { + self::$lang[$range] = []; + } + + $lang = []; + + foreach ($file as $_file) { + if (is_file($_file)) { + // 记录加载信息 + App::$debug && Log::record('[ LANG ] ' . $_file, 'info'); + + $_lang = include $_file; + + if (is_array($_lang)) { + $lang = array_change_key_case($_lang) + $lang; + } + } + } + + if (!empty($lang)) { + self::$lang[$range] = $lang + self::$lang[$range]; + } + + return self::$lang[$range]; + } + + /** + * 获取语言定义(不区分大小写) + * @access public + * @param string|null $name 语言变量 + * @param string $range 语言作用域 + * @return mixed + */ + public static function has($name, $range = '') + { + $range = $range ?: self::$range; + + return isset(self::$lang[$range][strtolower($name)]); + } + + /** + * 获取语言定义(不区分大小写) + * @access public + * @param string|null $name 语言变量 + * @param array $vars 变量替换 + * @param string $range 语言作用域 + * @return mixed + */ + public static function get($name = null, $vars = [], $range = '') + { + $range = $range ?: self::$range; + + // 空参数返回所有定义 + if (empty($name)) { + return self::$lang[$range]; + } + + $key = strtolower($name); + $value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name; + + // 变量解析 + if (!empty($vars) && is_array($vars)) { + /** + * Notes: + * 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0 + * 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数 + */ + if (key($vars) === 0) { + // 数字索引解析 + array_unshift($vars, $value); + $value = call_user_func_array('sprintf', $vars); + } else { + // 关联索引解析 + $replace = array_keys($vars); + foreach ($replace as &$v) { + $v = "{:{$v}}"; + } + $value = str_replace($replace, $vars, $value); + } + + } + + return $value; + } + + /** + * 自动侦测设置获取语言选择 + * @access public + * @return string + */ + public static function detect() + { + $langSet = ''; + + if (isset($_GET[self::$langDetectVar])) { + // url 中设置了语言变量 + $langSet = strtolower($_GET[self::$langDetectVar]); + } elseif (isset($_COOKIE[self::$langCookieVar])) { + // Cookie 中设置了语言变量 + $langSet = strtolower($_COOKIE[self::$langCookieVar]); + } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + // 自动侦测浏览器语言 + preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); + $langSet = strtolower($matches[1]); + $acceptLangs = Config::get('header_accept_lang'); + + if (isset($acceptLangs[$langSet])) { + $langSet = $acceptLangs[$langSet]; + } elseif (isset(self::$acceptLanguage[$langSet])) { + $langSet = self::$acceptLanguage[$langSet]; + } + } + + // 合法的语言 + if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) { + self::$range = $langSet ?: self::$range; + } + + return self::$range; + } + + /** + * 设置语言自动侦测的变量 + * @access public + * @param string $var 变量名称 + * @return void + */ + public static function setLangDetectVar($var) + { + self::$langDetectVar = $var; + } + + /** + * 设置语言的 cookie 保存变量 + * @access public + * @param string $var 变量名称 + * @return void + */ + public static function setLangCookieVar($var) + { + self::$langCookieVar = $var; + } + + /** + * 设置语言的 cookie 的过期时间 + * @access public + * @param string $expire 过期时间 + * @return void + */ + public static function setLangCookieExpire($expire) + { + self::$langCookieExpire = $expire; + } + + /** + * 设置允许的语言列表 + * @access public + * @param array $list 语言列表 + * @return void + */ + public static function setAllowLangList($list) + { + self::$allowLangList = $list; + } +} diff --git a/source/thinkphp/library/think/Loader.php b/source/thinkphp/library/think/Loader.php new file mode 100644 index 0000000..d813a5d --- /dev/null +++ b/source/thinkphp/library/think/Loader.php @@ -0,0 +1,677 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; + +class Loader +{ + /** + * @var array 实例数组 + */ + protected static $instance = []; + + /** + * @var array 类名映射 + */ + protected static $classMap = []; + + /** + * @var array 命名空间别名 + */ + protected static $namespaceAlias = []; + + /** + * @var array PSR-4 命名空间前缀长度映射 + */ + private static $prefixLengthsPsr4 = []; + + /** + * @var array PSR-4 的加载目录 + */ + private static $prefixDirsPsr4 = []; + + /** + * @var array PSR-4 加载失败的回退目录 + */ + private static $fallbackDirsPsr4 = []; + + /** + * @var array PSR-0 命名空间前缀映射 + */ + private static $prefixesPsr0 = []; + + /** + * @var array PSR-0 加载失败的回退目录 + */ + private static $fallbackDirsPsr0 = []; + + /** + * @var array 需要加载的文件 + */ + private static $files = []; + + /** + * 自动加载 + * @access public + * @param string $class 类名 + * @return bool + */ + public static function autoload($class) + { + // 检测命名空间别名 + if (!empty(self::$namespaceAlias)) { + $namespace = dirname($class); + if (isset(self::$namespaceAlias[$namespace])) { + $original = self::$namespaceAlias[$namespace] . '\\' . basename($class); + if (class_exists($original)) { + return class_alias($original, $class, false); + } + } + } + + if ($file = self::findFile($class)) { + // 非 Win 环境不严格区分大小写 + if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) { + __include_file($file); + return true; + } + } + + return false; + } + + /** + * 查找文件 + * @access private + * @param string $class 类名 + * @return bool|string + */ + private static function findFile($class) + { + // 类库映射 + if (!empty(self::$classMap[$class])) { + return self::$classMap[$class]; + } + + // 查找 PSR-4 + $logicalPathPsr4 = strtr($class, '\\', DS) . EXT; + $first = $class[0]; + + if (isset(self::$prefixLengthsPsr4[$first])) { + foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach (self::$prefixDirsPsr4[$prefix] as $dir) { + if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // 查找 PSR-4 fallback dirs + foreach (self::$fallbackDirsPsr4 as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr4)) { + return $file; + } + } + + // 查找 PSR-0 + if (false !== $pos = strrpos($class, '\\')) { + // namespace class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DS) . EXT; + } + + if (isset(self::$prefixesPsr0[$first])) { + foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // 查找 PSR-0 fallback dirs + foreach (self::$fallbackDirsPsr0 as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr0)) { + return $file; + } + } + + // 找不到则设置映射为 false 并返回 + return self::$classMap[$class] = false; + } + + /** + * 注册 classmap + * @access public + * @param string|array $class 类名 + * @param string $map 映射 + * @return void + */ + public static function addClassMap($class, $map = '') + { + if (is_array($class)) { + self::$classMap = array_merge(self::$classMap, $class); + } else { + self::$classMap[$class] = $map; + } + } + + /** + * 注册命名空间 + * @access public + * @param string|array $namespace 命名空间 + * @param string $path 路径 + * @return void + */ + public static function addNamespace($namespace, $path = '') + { + if (is_array($namespace)) { + foreach ($namespace as $prefix => $paths) { + self::addPsr4($prefix . '\\', rtrim($paths, DS), true); + } + } else { + self::addPsr4($namespace . '\\', rtrim($path, DS), true); + } + } + + /** + * 添加 PSR-0 命名空间 + * @access private + * @param array|string $prefix 空间前缀 + * @param array $paths 路径 + * @param bool $prepend 预先设置的优先级更高 + * @return void + */ + private static function addPsr0($prefix, $paths, $prepend = false) + { + if (!$prefix) { + self::$fallbackDirsPsr0 = $prepend ? + array_merge((array) $paths, self::$fallbackDirsPsr0) : + array_merge(self::$fallbackDirsPsr0, (array) $paths); + } else { + $first = $prefix[0]; + + if (!isset(self::$prefixesPsr0[$first][$prefix])) { + self::$prefixesPsr0[$first][$prefix] = (array) $paths; + } else { + self::$prefixesPsr0[$first][$prefix] = $prepend ? + array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) : + array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths); + } + } + } + + /** + * 添加 PSR-4 空间 + * @access private + * @param array|string $prefix 空间前缀 + * @param string $paths 路径 + * @param bool $prepend 预先设置的优先级更高 + * @return void + */ + private static function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + self::$fallbackDirsPsr4 = $prepend ? + array_merge((array) $paths, self::$fallbackDirsPsr4) : + array_merge(self::$fallbackDirsPsr4, (array) $paths); + + } elseif (!isset(self::$prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException( + "A non-empty PSR-4 prefix must end with a namespace separator." + ); + } + + self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + self::$prefixDirsPsr4[$prefix] = (array) $paths; + + } else { + self::$prefixDirsPsr4[$prefix] = $prepend ? + // Prepend directories for an already registered namespace. + array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) : + // Append directories for an already registered namespace. + array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths); + } + } + + /** + * 注册命名空间别名 + * @access public + * @param array|string $namespace 命名空间 + * @param string $original 源文件 + * @return void + */ + public static function addNamespaceAlias($namespace, $original = '') + { + if (is_array($namespace)) { + self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace); + } else { + self::$namespaceAlias[$namespace] = $original; + } + } + + /** + * 注册自动加载机制 + * @access public + * @param callable $autoload 自动加载处理方法 + * @return void + */ + public static function register($autoload = null) + { + // 注册系统自动加载 + spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); + + // Composer 自动加载支持 + if (is_dir(VENDOR_PATH . 'composer')) { + if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) { + require VENDOR_PATH . 'composer' . DS . 'autoload_static.php'; + + $declaredClass = get_declared_classes(); + $composerClass = array_pop($declaredClass); + + foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { + if (property_exists($composerClass, $attr)) { + self::${$attr} = $composerClass::${$attr}; + } + } + } else { + self::registerComposerLoader(); + } + } + + // 注册命名空间定义 + self::addNamespace([ + 'think' => LIB_PATH . 'think' . DS, + 'behavior' => LIB_PATH . 'behavior' . DS, + 'traits' => LIB_PATH . 'traits' . DS, + ]); + + // 加载类库映射文件 + if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { + self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT)); + } + + self::loadComposerAutoloadFiles(); + + // 自动加载 extend 目录 + self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); + } + + /** + * 注册 composer 自动加载 + * @access private + * @return void + */ + private static function registerComposerLoader() + { + if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { + $map = require VENDOR_PATH . 'composer/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + self::addPsr0($namespace, $path); + } + } + + if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) { + $map = require VENDOR_PATH . 'composer/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + self::addPsr4($namespace, $path); + } + } + + if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) { + $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php'; + if ($classMap) { + self::addClassMap($classMap); + } + } + + if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { + self::$files = require VENDOR_PATH . 'composer/autoload_files.php'; + } + } + + // 加载composer autofile文件 + public static function loadComposerAutoloadFiles() + { + foreach (self::$files as $fileIdentifier => $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + __require_file($file); + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } + } + } + + /** + * 导入所需的类库 同 Java 的 Import 本函数有缓存功能 + * @access public + * @param string $class 类库命名空间字符串 + * @param string $baseUrl 起始路径 + * @param string $ext 导入的文件扩展名 + * @return bool + */ + public static function import($class, $baseUrl = '', $ext = EXT) + { + static $_file = []; + $key = $class . $baseUrl; + $class = str_replace(['.', '#'], [DS, '.'], $class); + + if (isset($_file[$key])) { + return true; + } + + if (empty($baseUrl)) { + list($name, $class) = explode(DS, $class, 2); + + if (isset(self::$prefixDirsPsr4[$name . '\\'])) { + // 注册的命名空间 + $baseUrl = self::$prefixDirsPsr4[$name . '\\']; + } elseif ('@' == $name) { + // 加载当前模块应用类库 + $baseUrl = App::$modulePath; + } elseif (is_dir(EXTEND_PATH . $name)) { + $baseUrl = EXTEND_PATH . $name . DS; + } else { + // 加载其它模块的类库 + $baseUrl = APP_PATH . $name . DS; + } + } elseif (substr($baseUrl, -1) != DS) { + $baseUrl .= DS; + } + + // 如果类存在则导入类库文件 + if (is_array($baseUrl)) { + foreach ($baseUrl as $path) { + if (is_file($filename = $path . DS . $class . $ext)) { + break; + } + } + } else { + $filename = $baseUrl . $class . $ext; + } + + if (!empty($filename) && + is_file($filename) && + (!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME)) + ) { + __include_file($filename); + $_file[$key] = true; + + return true; + } + + return false; + } + + /** + * 实例化(分层)模型 + * @access public + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 + * @return object + * @throws ClassNotFoundException + */ + public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common') + { + $uid = $name . $layer; + + if (isset(self::$instance[$uid])) { + return self::$instance[$uid]; + } + + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + + if (class_exists($class)) { + $model = new $class(); + } else { + $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); + + if (class_exists($class)) { + $model = new $class(); + } else { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + } + + return self::$instance[$uid] = $model; + } + + /** + * 实例化(分层)控制器 格式:[模块名/]控制器名 + * @access public + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $empty 空控制器名称 + * @return object + * @throws ClassNotFoundException + */ + public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '') + { + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + + if (class_exists($class)) { + return App::invokeClass($class); + } + + if ($empty) { + $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix); + + if (class_exists($emptyClass)) { + return new $emptyClass(Request::instance()); + } + } + + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + + /** + * 实例化验证类 格式:[模块名/]验证器名 + * @access public + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 + * @return object|false + * @throws ClassNotFoundException + */ + public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common') + { + $name = $name ?: Config::get('default_validate'); + + if (empty($name)) { + return new Validate; + } + + $uid = $name . $layer; + if (isset(self::$instance[$uid])) { + return self::$instance[$uid]; + } + + list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix); + + if (class_exists($class)) { + $validate = new $class; + } else { + $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class); + + if (class_exists($class)) { + $validate = new $class; + } else { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + } + + return self::$instance[$uid] = $validate; + } + + /** + * 解析模块和类名 + * @access protected + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return array + */ + protected static function getModuleAndClass($name, $layer, $appendSuffix) + { + if (false !== strpos($name, '\\')) { + $module = Request::instance()->module(); + $class = $name; + } else { + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name, 2); + } else { + $module = Request::instance()->module(); + } + + $class = self::parseClass($module, $layer, $name, $appendSuffix); + } + + return [$module, $class]; + } + + /** + * 数据库初始化 并取得数据库类实例 + * @access public + * @param mixed $config 数据库配置 + * @param bool|string $name 连接标识 true 强制重新连接 + * @return \think\db\Connection + */ + public static function db($config = [], $name = false) + { + return Db::connect($config, $name); + } + + /** + * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 + * @access public + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @return mixed + */ + public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) + { + $info = pathinfo($url); + $action = $info['basename']; + $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller(); + $class = self::controller($module, $layer, $appendSuffix); + + if ($class) { + if (is_scalar($vars)) { + if (strpos($vars, '=')) { + parse_str($vars, $vars); + } else { + $vars = [$vars]; + } + } + + return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars); + } + + return false; + } + + /** + * 字符串命名风格转换 + * type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格 + * @access public + * @param string $name 字符串 + * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) + * @return string + */ + public static function parseName($name, $type = 0, $ucfirst = true) + { + if ($type) { + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { + return strtoupper($match[1]); + }, $name); + + return $ucfirst ? ucfirst($name) : lcfirst($name); + } + + return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); + } + + /** + * 解析应用类的类名 + * @access public + * @param string $module 模块名 + * @param string $layer 层名 controller model ... + * @param string $name 类名 + * @param bool $appendSuffix 是否添加类名后缀 + * @return string + */ + public static function parseClass($module, $layer, $name, $appendSuffix = false) + { + + $array = explode('\\', str_replace(['/', '.'], '\\', $name)); + $class = self::parseName(array_pop($array), 1); + $class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : ''); + $path = $array ? implode('\\', $array) . '\\' : ''; + + return App::$namespace . '\\' . + ($module ? $module . '\\' : '') . + $layer . '\\' . $path . $class; + } + + /** + * 初始化类的实例 + * @access public + * @return void + */ + public static function clearInstance() + { + self::$instance = []; + } +} + +// 作用范围隔离 + +/** + * include + * @param string $file 文件路径 + * @return mixed + */ +function __include_file($file) +{ + return include $file; +} + +/** + * require + * @param string $file 文件路径 + * @return mixed + */ +function __require_file($file) +{ + return require $file; +} diff --git a/source/thinkphp/library/think/Log.php b/source/thinkphp/library/think/Log.php new file mode 100644 index 0000000..c064306 --- /dev/null +++ b/source/thinkphp/library/think/Log.php @@ -0,0 +1,237 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; + +/** + * Class Log + * @package think + * + * @method void log($msg) static 记录一般日志 + * @method void error($msg) static 记录错误日志 + * @method void info($msg) static 记录一般信息日志 + * @method void sql($msg) static 记录 SQL 查询日志 + * @method void notice($msg) static 记录提示日志 + * @method void alert($msg) static 记录报警日志 + */ +class Log +{ + const LOG = 'log'; + const ERROR = 'error'; + const INFO = 'info'; + const SQL = 'sql'; + const NOTICE = 'notice'; + const ALERT = 'alert'; + const DEBUG = 'debug'; + + /** + * @var array 日志信息 + */ + protected static $log = []; + + /** + * @var array 配置参数 + */ + protected static $config = []; + + /** + * @var array 日志类型 + */ + protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug']; + + /** + * @var log\driver\File|log\driver\Test|log\driver\Socket 日志写入驱动 + */ + protected static $driver; + + /** + * @var string 当前日志授权 key + */ + protected static $key; + + /** + * 日志初始化 + * @access public + * @param array $config 配置参数 + * @return void + */ + public static function init($config = []) + { + $type = isset($config['type']) ? $config['type'] : 'File'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type); + + self::$config = $config; + unset($config['type']); + + if (class_exists($class)) { + self::$driver = new $class($config); + } else { + throw new ClassNotFoundException('class not exists:' . $class, $class); + } + + // 记录初始化信息 + App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info'); + } + + /** + * 获取日志信息 + * @access public + * @param string $type 信息类型 + * @return array|string + */ + public static function getLog($type = '') + { + return $type ? self::$log[$type] : self::$log; + } + + /** + * 记录调试信息 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 信息类型 + * @return void + */ + public static function record($msg, $type = 'log') + { + self::$log[$type][] = $msg; + + // 命令行下面日志写入改进 + IS_CLI && self::save(); + } + + /** + * 清空日志信息 + * @access public + * @return void + */ + public static function clear() + { + self::$log = []; + } + + /** + * 设置当前日志记录的授权 key + * @access public + * @param string $key 授权 key + * @return void + */ + public static function key($key) + { + self::$key = $key; + } + + /** + * 检查日志写入权限 + * @access public + * @param array $config 当前日志配置参数 + * @return bool + */ + public static function check($config) + { + return !self::$key || empty($config['allow_key']) || in_array(self::$key, $config['allow_key']); + } + + /** + * 保存调试信息 + * @access public + * @return bool + */ + public static function save() + { + // 没有需要保存的记录则直接返回 + if (empty(self::$log)) { + return true; + } + + is_null(self::$driver) && self::init(Config::get('log')); + + // 检测日志写入权限 + if (!self::check(self::$config)) { + return false; + } + + if (empty(self::$config['level'])) { + // 获取全部日志 + $log = self::$log; + if (!App::$debug && isset($log['debug'])) { + unset($log['debug']); + } + } else { + // 记录允许级别 + $log = []; + foreach (self::$config['level'] as $level) { + if (isset(self::$log[$level])) { + $log[$level] = self::$log[$level]; + } + } + } + + if ($result = self::$driver->save($log, true)) { + self::$log = []; + } + + Hook::listen('log_write_done', $log); + + return $result; + } + + /** + * 实时写入日志信息 并支持行为 + * @access public + * @param mixed $msg 调试信息 + * @param string $type 信息类型 + * @param bool $force 是否强制写入 + * @return bool + */ + public static function write($msg, $type = 'log', $force = false) + { + $log = self::$log; + + // 如果不是强制写入,而且信息类型不在可记录的类别中则直接返回 false 不做记录 + if (true !== $force && !empty(self::$config['level']) && !in_array($type, self::$config['level'])) { + return false; + } + + // 封装日志信息 + $log[$type][] = $msg; + + // 监听 log_write + Hook::listen('log_write', $log); + + is_null(self::$driver) && self::init(Config::get('log')); + + // 写入日志 + if ($result = self::$driver->save($log, false)) { + self::$log = []; + } + + return $result; + } + + /** + * 静态方法调用 + * @access public + * @param string $method 调用方法 + * @param mixed $args 参数 + * @return void + */ + public static function __callStatic($method, $args) + { + if (in_array($method, self::$type)) { + array_push($args, $method); + + call_user_func_array('\\think\\Log::record', $args); + } + } + +} diff --git a/source/thinkphp/library/think/Model.php b/source/thinkphp/library/think/Model.php new file mode 100644 index 0000000..2dc27b4 --- /dev/null +++ b/source/thinkphp/library/think/Model.php @@ -0,0 +1,2350 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use BadMethodCallException; +use InvalidArgumentException; +use think\db\Query; +use think\exception\ValidateException; +use think\model\Collection as ModelCollection; +use think\model\Relation; +use think\model\relation\BelongsTo; +use think\model\relation\BelongsToMany; +use think\model\relation\HasMany; +use think\model\relation\HasManyThrough; +use think\model\relation\HasOne; +use think\model\relation\MorphMany; +use think\model\relation\MorphOne; +use think\model\relation\MorphTo; + +/** + * Class Model + * @package think + * @mixin Query + */ +abstract class Model implements \JsonSerializable, \ArrayAccess +{ + // 数据库查询对象池 + protected static $links = []; + // 数据库配置 + protected $connection = []; + // 父关联模型对象 + protected $parent; + // 数据库查询对象 + protected $query; + // 当前模型名称 + protected $name; + // 数据表名称 + protected $table; + // 当前类名称 + protected $class; + // 回调事件 + private static $event = []; + // 错误信息 + protected $error; + // 字段验证规则 + protected $validate; + // 数据表主键 复合主键使用数组定义 不设置则自动获取 + protected $pk; + // 数据表字段信息 留空则自动获取 + protected $field = []; + // 数据排除字段 + protected $except = []; + // 数据废弃字段 + protected $disuse = []; + // 只读字段 + protected $readonly = []; + // 显示属性 + protected $visible = []; + // 隐藏属性 + protected $hidden = []; + // 追加属性 + protected $append = []; + // 数据信息 + protected $data = []; + // 原始数据 + protected $origin = []; + // 关联模型 + protected $relation = []; + + // 保存自动完成列表 + protected $auto = []; + // 新增自动完成列表 + protected $insert = []; + // 更新自动完成列表 + protected $update = []; + // 是否需要自动写入时间戳 如果设置为字符串 则表示时间字段的类型 + protected $autoWriteTimestamp; + // 创建时间字段 + protected $createTime = 'create_time'; + // 更新时间字段 + protected $updateTime = 'update_time'; + // 时间字段取出后的默认时间格式 + protected $dateFormat; + // 字段类型或者格式转换 + protected $type = []; + // 是否为更新数据 + protected $isUpdate = false; + // 是否使用Replace + protected $replace = false; + // 是否强制更新所有数据 + protected $force = false; + // 更新条件 + protected $updateWhere; + // 验证失败是否抛出异常 + protected $failException = false; + // 全局查询范围 + protected $useGlobalScope = true; + // 是否采用批量验证 + protected $batchValidate = false; + // 查询数据集对象 + protected $resultSetType; + // 关联自动写入 + protected $relationWrite; + + /** + * 初始化过的模型. + * + * @var array + */ + protected static $initialized = []; + + /** + * 是否从主库读取(主从分布式有效) + * @var array + */ + protected static $readMaster; + + /** + * 构造方法 + * @access public + * @param array|object $data 数据 + */ + public function __construct($data = []) + { + if (is_object($data)) { + $this->data = get_object_vars($data); + } else { + $this->data = $data; + } + + if ($this->disuse) { + // 废弃字段 + foreach ((array) $this->disuse as $key) { + if (array_key_exists($key, $this->data)) { + unset($this->data[$key]); + } + } + } + + // 记录原始数据 + $this->origin = $this->data; + + // 当前类名 + $this->class = get_called_class(); + + if (empty($this->name)) { + // 当前模型名 + $name = str_replace('\\', '/', $this->class); + $this->name = basename($name); + if (Config::get('class_suffix')) { + $suffix = basename(dirname($name)); + $this->name = substr($this->name, 0, -strlen($suffix)); + } + } + + if (is_null($this->autoWriteTimestamp)) { + // 自动写入时间戳 + $this->autoWriteTimestamp = $this->getQuery()->getConfig('auto_timestamp'); + } + + if (is_null($this->dateFormat)) { + // 设置时间戳格式 + $this->dateFormat = $this->getQuery()->getConfig('datetime_format'); + } + + if (is_null($this->resultSetType)) { + $this->resultSetType = $this->getQuery()->getConfig('resultset_type'); + } + // 执行初始化操作 + $this->initialize(); + } + + /** + * 是否从主库读取数据(主从分布有效) + * @access public + * @param bool $all 是否所有模型生效 + * @return $this + */ + public function readMaster($all = false) + { + $model = $all ? '*' : $this->class; + + static::$readMaster[$model] = true; + return $this; + } + + /** + * 创建模型的查询对象 + * @access protected + * @return Query + */ + protected function buildQuery() + { + // 合并数据库配置 + if (!empty($this->connection)) { + if (is_array($this->connection)) { + $connection = array_merge(Config::get('database'), $this->connection); + } else { + $connection = $this->connection; + } + } else { + $connection = []; + } + + $con = Db::connect($connection); + // 设置当前模型 确保查询返回模型对象 + $queryClass = $this->query ?: $con->getConfig('query'); + $query = new $queryClass($con, $this); + + if (isset(static::$readMaster['*']) || isset(static::$readMaster[$this->class])) { + $query->master(true); + } + + // 设置当前数据表和模型名 + if (!empty($this->table)) { + $query->setTable($this->table); + } else { + $query->name($this->name); + } + + if (!empty($this->pk)) { + $query->pk($this->pk); + } + + return $query; + } + + /** + * 创建新的模型实例 + * @access public + * @param array|object $data 数据 + * @param bool $isUpdate 是否为更新 + * @param mixed $where 更新条件 + * @return Model + */ + public function newInstance($data = [], $isUpdate = false, $where = null) + { + return (new static($data))->isUpdate($isUpdate, $where); + } + + /** + * 获取当前模型的查询对象 + * @access public + * @param bool $buildNewQuery 创建新的查询对象 + * @return Query + */ + public function getQuery($buildNewQuery = false) + { + if ($buildNewQuery) { + return $this->buildQuery(); + } elseif (!isset(self::$links[$this->class])) { + // 创建模型查询对象 + self::$links[$this->class] = $this->buildQuery(); + } + + return self::$links[$this->class]; + } + + /** + * 获取当前模型的数据库查询对象 + * @access public + * @param bool $useBaseQuery 是否调用全局查询范围 + * @param bool $buildNewQuery 创建新的查询对象 + * @return Query + */ + public function db($useBaseQuery = true, $buildNewQuery = true) + { + $query = $this->getQuery($buildNewQuery); + + // 全局作用域 + if ($useBaseQuery && method_exists($this, 'base')) { + call_user_func_array([$this, 'base'], [ & $query]); + } + + // 返回当前模型的数据库查询对象 + return $query; + } + + /** + * 初始化模型 + * @access protected + * @return void + */ + protected function initialize() + { + $class = get_class($this); + if (!isset(static::$initialized[$class])) { + static::$initialized[$class] = true; + static::init(); + } + } + + /** + * 初始化处理 + * @access protected + * @return void + */ + protected static function init() + { + } + + /** + * 设置父关联对象 + * @access public + * @param Model $model 模型对象 + * @return $this + */ + public function setParent($model) + { + $this->parent = $model; + return $this; + } + + /** + * 获取父关联对象 + * @access public + * @return Model + */ + public function getParent() + { + return $this->parent; + } + + /** + * 设置数据对象值 + * @access public + * @param mixed $data 数据或者属性名 + * @param mixed $value 值 + * @return $this + */ + public function data($data, $value = null) + { + if (is_string($data)) { + $this->data[$data] = $value; + } else { + // 清空数据 + $this->data = []; + if (is_object($data)) { + $data = get_object_vars($data); + } + if (true === $value) { + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + } else { + $this->data = $data; + } + } + return $this; + } + + /** + * 获取对象原始数据 如果不存在指定字段返回false + * @access public + * @param string $name 字段名 留空获取全部 + * @return mixed + * @throws InvalidArgumentException + */ + public function getData($name = null) + { + if (is_null($name)) { + return $this->data; + } elseif (array_key_exists($name, $this->data)) { + return $this->data[$name]; + } elseif (array_key_exists($name, $this->relation)) { + return $this->relation[$name]; + } else { + throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name); + } + } + + /** + * 是否需要自动写入时间字段 + * @access public + * @param bool $auto + * @return $this + */ + public function isAutoWriteTimestamp($auto) + { + $this->autoWriteTimestamp = $auto; + return $this; + } + + /** + * 更新是否强制写入数据 而不做比较 + * @access public + * @param bool $force + * @return $this + */ + public function force($force = true) + { + $this->force = $force; + return $this; + } + + /** + * 修改器 设置数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 属性值 + * @param array $data 数据 + * @return $this + */ + public function setAttr($name, $value, $data = []) + { + if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { + // 自动写入的时间戳字段 + $value = $this->autoWriteTimestamp($name); + } else { + // 检测修改器 + $method = 'set' . Loader::parseName($name, 1) . 'Attr'; + if (method_exists($this, $method)) { + $value = $this->$method($value, array_merge($this->data, $data), $this->relation); + } elseif (isset($this->type[$name])) { + // 类型转换 + $value = $this->writeTransform($value, $this->type[$name]); + } + } + + // 设置数据对象属性 + $this->data[$name] = $value; + return $this; + } + + /** + * 获取当前模型的关联模型数据 + * @access public + * @param string $name 关联方法名 + * @return mixed + */ + public function getRelation($name = null) + { + if (is_null($name)) { + return $this->relation; + } elseif (array_key_exists($name, $this->relation)) { + return $this->relation[$name]; + } else { + return; + } + } + + /** + * 设置关联数据对象值 + * @access public + * @param string $name 属性名 + * @param mixed $value 属性值 + * @return $this + */ + public function setRelation($name, $value) + { + $this->relation[$name] = $value; + return $this; + } + + /** + * 自动写入时间戳 + * @access public + * @param string $name 时间戳字段 + * @return mixed + */ + protected function autoWriteTimestamp($name) + { + if (isset($this->type[$name])) { + $type = $this->type[$name]; + if (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + switch ($type) { + case 'datetime': + case 'date': + $format = !empty($param) ? $param : $this->dateFormat; + $value = $this->formatDateTime(time(), $format); + break; + case 'timestamp': + case 'integer': + default: + $value = time(); + break; + } + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp', + ]) + ) { + $value = $this->formatDateTime(time(), $this->dateFormat); + } else { + $value = $this->formatDateTime(time(), $this->dateFormat, true); + } + return $value; + } + + /** + * 时间日期字段格式化处理 + * @access public + * @param mixed $time 时间日期表达式 + * @param mixed $format 日期格式 + * @param bool $timestamp 是否进行时间戳转换 + * @return mixed + */ + protected function formatDateTime($time, $format, $timestamp = false) + { + if (false !== strpos($format, '\\')) { + $time = new $format($time); + } elseif (!$timestamp && false !== $format) { + $time = date($format, $time); + } + return $time; + } + + /** + * 数据写入 类型转换 + * @access public + * @param mixed $value 值 + * @param string|array $type 要转换的类型 + * @return mixed + */ + protected function writeTransform($value, $type) + { + if (is_null($value)) { + return; + } + + if (is_array($type)) { + list($type, $param) = $type; + } elseif (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + switch ($type) { + case 'integer': + $value = (int) $value; + break; + case 'float': + if (empty($param)) { + $value = (float) $value; + } else { + $value = (float) number_format($value, $param, '.', ''); + } + break; + case 'boolean': + $value = (bool) $value; + break; + case 'timestamp': + if (!is_numeric($value)) { + $value = strtotime($value); + } + break; + case 'datetime': + $format = !empty($param) ? $param : $this->dateFormat; + $value = is_numeric($value) ? $value : strtotime($value); + $value = $this->formatDateTime($value, $format); + break; + case 'object': + if (is_object($value)) { + $value = json_encode($value, JSON_FORCE_OBJECT); + } + break; + case 'array': + $value = (array) $value; + case 'json': + $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE; + $value = json_encode($value, $option); + break; + case 'serialize': + $value = serialize($value); + break; + + } + return $value; + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @return mixed + * @throws InvalidArgumentException + */ + public function getAttr($name) + { + try { + $notFound = false; + $value = $this->getData($name); + } catch (InvalidArgumentException $e) { + $notFound = true; + $value = null; + } + + // 检测属性获取器 + $method = 'get' . Loader::parseName($name, 1) . 'Attr'; + if (method_exists($this, $method)) { + $value = $this->$method($value, $this->data, $this->relation); + } elseif (isset($this->type[$name])) { + // 类型转换 + $value = $this->readTransform($value, $this->type[$name]); + } elseif (in_array($name, [$this->createTime, $this->updateTime])) { + if (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), [ + 'datetime', + 'date', + 'timestamp', + ]) + ) { + $value = $this->formatDateTime(strtotime($value), $this->dateFormat); + } else { + $value = $this->formatDateTime($value, $this->dateFormat); + } + } elseif ($notFound) { + $relation = Loader::parseName($name, 1, false); + if (method_exists($this, $relation)) { + $modelRelation = $this->$relation(); + // 不存在该字段 获取关联数据 + $value = $this->getRelationData($modelRelation); + // 保存关联对象值 + $this->relation[$name] = $value; + } else { + throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name); + } + } + return $value; + } + + /** + * 获取关联模型数据 + * @access public + * @param Relation $modelRelation 模型关联对象 + * @return mixed + * @throws BadMethodCallException + */ + protected function getRelationData(Relation $modelRelation) + { + if ($this->parent && !$modelRelation->isSelfRelation() && get_class($modelRelation->getModel()) == get_class($this->parent)) { + $value = $this->parent; + } else { + // 首先获取关联数据 + if (method_exists($modelRelation, 'getRelation')) { + $value = $modelRelation->getRelation(); + } else { + throw new BadMethodCallException('method not exists:' . get_class($modelRelation) . '-> getRelation'); + } + } + return $value; + } + + /** + * 数据读取 类型转换 + * @access public + * @param mixed $value 值 + * @param string|array $type 要转换的类型 + * @return mixed + */ + protected function readTransform($value, $type) + { + if (is_null($value)) { + return; + } + + if (is_array($type)) { + list($type, $param) = $type; + } elseif (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + switch ($type) { + case 'integer': + $value = (int) $value; + break; + case 'float': + if (empty($param)) { + $value = (float) $value; + } else { + $value = (float) number_format($value, $param, '.', ''); + } + break; + case 'boolean': + $value = (bool) $value; + break; + case 'timestamp': + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = $this->formatDateTime($value, $format); + } + break; + case 'datetime': + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = $this->formatDateTime(strtotime($value), $format); + } + break; + case 'json': + $value = json_decode($value, true); + break; + case 'array': + $value = empty($value) ? [] : json_decode($value, true); + break; + case 'object': + $value = empty($value) ? new \stdClass() : json_decode($value); + break; + case 'serialize': + try { + $value = unserialize($value); + } catch (\Exception $e) { + $value = null; + } + break; + default: + if (false !== strpos($type, '\\')) { + // 对象类型 + $value = new $type($value); + } + } + return $value; + } + + /** + * 设置需要追加的输出属性 + * @access public + * @param array $append 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function append($append = [], $override = false) + { + $this->append = $override ? $append : array_merge($this->append, $append); + return $this; + } + + /** + * 设置附加关联对象的属性 + * @access public + * @param string $relation 关联方法 + * @param string|array $append 追加属性名 + * @return $this + * @throws Exception + */ + public function appendRelationAttr($relation, $append) + { + if (is_string($append)) { + $append = explode(',', $append); + } + + $relation = Loader::parseName($relation, 1, false); + + // 获取关联数据 + if (isset($this->relation[$relation])) { + $model = $this->relation[$relation]; + } else { + $model = $this->getRelationData($this->$relation()); + } + + if ($model instanceof Model) { + foreach ($append as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if (isset($this->data[$key])) { + throw new Exception('bind attr has exists:' . $key); + } else { + $this->data[$key] = $model->getAttr($attr); + } + } + } + return $this; + } + + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function hidden($hidden = [], $override = false) + { + $this->hidden = $override ? $hidden : array_merge($this->hidden, $hidden); + return $this; + } + + /** + * 设置需要输出的属性 + * @access public + * @param array $visible + * @param bool $override 是否覆盖 + * @return $this + */ + public function visible($visible = [], $override = false) + { + $this->visible = $override ? $visible : array_merge($this->visible, $visible); + return $this; + } + + /** + * 解析隐藏及显示属性 + * @access protected + * @param array $attrs 属性 + * @param array $result 结果集 + * @param bool $visible + * @return array + */ + protected function parseAttr($attrs, &$result, $visible = true) + { + $array = []; + foreach ($attrs as $key => $val) { + if (is_array($val)) { + if ($visible) { + $array[] = $key; + } + $result[$key] = $val; + } elseif (strpos($val, '.')) { + list($key, $name) = explode('.', $val); + if ($visible) { + $array[] = $key; + } + $result[$key][] = $name; + } else { + $array[] = $val; + } + } + return $array; + } + + /** + * 转换子模型对象 + * @access protected + * @param Model|ModelCollection $model + * @param $visible + * @param $hidden + * @param $key + * @return array + */ + protected function subToArray($model, $visible, $hidden, $key) + { + // 关联模型对象 + if (isset($visible[$key])) { + $model->visible($visible[$key]); + } elseif (isset($hidden[$key])) { + $model->hidden($hidden[$key]); + } + return $model->toArray(); + } + + /** + * 转换当前模型对象为数组 + * @access public + * @return array + */ + public function toArray() + { + $item = []; + $visible = []; + $hidden = []; + + $data = array_merge($this->data, $this->relation); + + // 过滤属性 + if (!empty($this->visible)) { + $array = $this->parseAttr($this->visible, $visible); + $data = array_intersect_key($data, array_flip($array)); + } elseif (!empty($this->hidden)) { + $array = $this->parseAttr($this->hidden, $hidden, false); + $data = array_diff_key($data, array_flip($array)); + } + + foreach ($data as $key => $val) { + if ($val instanceof Model || $val instanceof ModelCollection) { + // 关联模型对象 + $item[$key] = $this->subToArray($val, $visible, $hidden, $key); + } elseif (is_array($val) && reset($val) instanceof Model) { + // 关联模型数据集 + $arr = []; + foreach ($val as $k => $value) { + $arr[$k] = $this->subToArray($value, $visible, $hidden, $key); + } + $item[$key] = $arr; + } else { + // 模型属性 + $item[$key] = $this->getAttr($key); + } + } + // 追加属性(必须定义获取器) + if (!empty($this->append)) { + foreach ($this->append as $key => $name) { + if (is_array($name)) { + // 追加关联对象属性 + $relation = $this->getAttr($key); + $item[$key] = $relation->append($name)->toArray(); + } elseif (strpos($name, '.')) { + list($key, $attr) = explode('.', $name); + // 追加关联对象属性 + $relation = $this->getAttr($key); + $item[$key] = $relation->append([$attr])->toArray(); + } else { + $relation = Loader::parseName($name, 1, false); + if (method_exists($this, $relation)) { + $modelRelation = $this->$relation(); + $value = $this->getRelationData($modelRelation); + + if (method_exists($modelRelation, 'getBindAttr')) { + $bindAttr = $modelRelation->getBindAttr(); + if ($bindAttr) { + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if (isset($this->data[$key])) { + throw new Exception('bind attr has exists:' . $key); + } else { + $item[$key] = $value ? $value->getAttr($attr) : null; + } + } + continue; + } + } + $item[$name] = $value; + } else { + $item[$name] = $this->getAttr($name); + } + } + } + } + return !empty($item) ? $item : []; + } + + /** + * 转换当前模型对象为JSON字符串 + * @access public + * @param integer $options json参数 + * @return string + */ + public function toJson($options = JSON_UNESCAPED_UNICODE) + { + return json_encode($this->toArray(), $options); + } + + /** + * 移除当前模型的关联属性 + * @access public + * @return $this + */ + public function removeRelation() + { + $this->relation = []; + return $this; + } + + /** + * 转换当前模型数据集为数据集对象 + * @access public + * @param array|\think\Collection $collection 数据集 + * @return \think\Collection + */ + public function toCollection($collection) + { + if ($this->resultSetType) { + if ('collection' == $this->resultSetType) { + $collection = new ModelCollection($collection); + } elseif (false !== strpos($this->resultSetType, '\\')) { + $class = $this->resultSetType; + $collection = new $class($collection); + } + } + return $collection; + } + + /** + * 关联数据一起更新 + * @access public + * @param mixed $relation 关联 + * @return $this + */ + public function together($relation) + { + if (is_string($relation)) { + $relation = explode(',', $relation); + } + $this->relationWrite = $relation; + return $this; + } + + /** + * 获取模型对象的主键 + * @access public + * @param string $name 模型名 + * @return mixed + */ + public function getPk($name = '') + { + if (!empty($name)) { + $table = $this->getQuery()->getTable($name); + return $this->getQuery()->getPk($table); + } elseif (empty($this->pk)) { + $this->pk = $this->getQuery()->getPk(); + } + return $this->pk; + } + + /** + * 判断一个字段名是否为主键字段 + * @access public + * @param string $key 名称 + * @return bool + */ + protected function isPk($key) + { + $pk = $this->getPk(); + if (is_string($pk) && $pk == $key) { + return true; + } elseif (is_array($pk) && in_array($key, $pk)) { + return true; + } + return false; + } + + /** + * 新增数据是否使用Replace + * @access public + * @param bool $replace + * @return $this + */ + public function replace($replace = true) + { + $this->replace = $replace; + return $this; + } + + /** + * 保存当前数据对象 + * @access public + * @param array $data 数据 + * @param array $where 更新条件 + * @param string $sequence 自增序列名 + * @return integer|false + */ + public function save($data = [], $where = [], $sequence = null) + { + if (is_string($data)) { + $sequence = $data; + $data = []; + } + + // 数据自动验证 + if (!empty($data)) { + if (!$this->validateData($data)) { + return false; + } + + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + } + + if (!empty($where)) { + $this->isUpdate = true; + $this->updateWhere = $where; + } + + // 自动关联写入 + if (!empty($this->relationWrite)) { + $relation = []; + foreach ($this->relationWrite as $key => $name) { + if (is_array($name)) { + if (key($name) === 0) { + $relation[$key] = []; + foreach ($name as $val) { + if (isset($this->data[$val])) { + $relation[$key][$val] = $this->data[$val]; + unset($this->data[$val]); + } + } + } else { + $relation[$key] = $name; + } + } elseif (isset($this->relation[$name])) { + $relation[$name] = $this->relation[$name]; + } elseif (isset($this->data[$name])) { + $relation[$name] = $this->data[$name]; + unset($this->data[$name]); + } + } + } + + // 数据自动完成 + $this->autoCompleteData($this->auto); + + // 事件回调 + if (false === $this->trigger('before_write', $this)) { + return false; + } + $pk = $this->getPk(); + if ($this->isUpdate) { + // 自动更新 + $this->autoCompleteData($this->update); + + // 事件回调 + if (false === $this->trigger('before_update', $this)) { + return false; + } + + // 获取有更新的数据 + $data = $this->getChangedData(); + + if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) { + // 关联更新 + if (isset($relation)) { + $this->autoRelationUpdate($relation); + } + return 0; + } elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) { + // 自动写入更新时间 + $data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + $this->data[$this->updateTime] = $data[$this->updateTime]; + } + + if (empty($where) && !empty($this->updateWhere)) { + $where = $this->updateWhere; + } + + // 保留主键数据 + foreach ($this->data as $key => $val) { + if ($this->isPk($key)) { + $data[$key] = $val; + } + } + + $array = []; + + foreach ((array) $pk as $key) { + if (isset($data[$key])) { + $array[$key] = $data[$key]; + unset($data[$key]); + } + } + + if (!empty($array)) { + $where = $array; + } + + // 检测字段 + $allowFields = $this->checkAllowField(array_merge($this->auto, $this->update)); + + // 模型更新 + if (!empty($allowFields)) { + $result = $this->getQuery()->where($where)->strict(false)->field($allowFields)->update($data); + } else { + $result = $this->getQuery()->where($where)->update($data); + } + + // 关联更新 + if (isset($relation)) { + $this->autoRelationUpdate($relation); + } + + // 更新回调 + $this->trigger('after_update', $this); + + } else { + // 自动写入 + $this->autoCompleteData($this->insert); + + // 自动写入创建时间和更新时间 + if ($this->autoWriteTimestamp) { + if ($this->createTime && !isset($this->data[$this->createTime])) { + $this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime); + } + if ($this->updateTime && !isset($this->data[$this->updateTime])) { + $this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime); + } + } + + if (false === $this->trigger('before_insert', $this)) { + return false; + } + + // 检测字段 + $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert)); + if (!empty($allowFields)) { + $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence); + } else { + $result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence); + } + + // 获取自动增长主键 + if ($result && $insertId = $this->getQuery()->getLastInsID($sequence)) { + foreach ((array) $pk as $key) { + if (!isset($this->data[$key]) || '' == $this->data[$key]) { + $this->data[$key] = $insertId; + } + } + } + + // 关联写入 + if (isset($relation)) { + foreach ($relation as $name => $val) { + $method = Loader::parseName($name, 1, false); + $this->$method()->save($val); + } + } + + // 标记为更新 + $this->isUpdate = true; + + // 新增回调 + $this->trigger('after_insert', $this); + } + // 写入回调 + $this->trigger('after_write', $this); + + // 重新记录原始数据 + $this->origin = $this->data; + + return $result; + } + + protected function checkAllowField($auto = []) + { + if (true === $this->field) { + $this->field = $this->getQuery()->getTableInfo('', 'fields'); + $field = $this->field; + } elseif (!empty($this->field)) { + $field = array_merge($this->field, $auto); + if ($this->autoWriteTimestamp) { + array_push($field, $this->createTime, $this->updateTime); + } + } elseif (!empty($this->except)) { + $fields = $this->getQuery()->getTableInfo('', 'fields'); + $field = array_diff($fields, (array) $this->except); + $this->field = $field; + } else { + $field = []; + } + + if ($this->disuse) { + // 废弃字段 + $field = array_diff($field, (array) $this->disuse); + } + return $field; + } + + protected function autoRelationUpdate($relation) + { + foreach ($relation as $name => $val) { + if ($val instanceof Model) { + $val->save(); + } else { + unset($this->data[$name]); + $model = $this->getAttr($name); + if ($model instanceof Model) { + $model->save($val); + } + } + } + } + + /** + * 获取变化的数据 并排除只读数据 + * @access public + * @return array + */ + public function getChangedData() + { + if ($this->force) { + $data = $this->data; + } else { + $data = array_udiff_assoc($this->data, $this->origin, function ($a, $b) { + if ((empty($a) || empty($b)) && $a !== $b) { + return 1; + } + return is_object($a) || $a != $b ? 1 : 0; + }); + } + + if (!empty($this->readonly)) { + // 只读字段不允许更新 + foreach ($this->readonly as $key => $field) { + if (isset($data[$field])) { + unset($data[$field]); + } + } + } + + return $data; + } + + /** + * 字段值(延迟)增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return integer|true + * @throws Exception + */ + public function setInc($field, $step = 1, $lazyTime = 0) + { + // 更新条件 + $where = $this->getWhere(); + + $result = $this->getQuery()->where($where)->setInc($field, $step, $lazyTime); + if (true !== $result) { + $this->data[$field] += $step; + } + + return $result; + } + + /** + * 字段值(延迟)增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return integer|true + * @throws Exception + */ + public function setDec($field, $step = 1, $lazyTime = 0) + { + // 更新条件 + $where = $this->getWhere(); + $result = $this->getQuery()->where($where)->setDec($field, $step, $lazyTime); + if (true !== $result) { + $this->data[$field] -= $step; + } + + return $result; + } + + /** + * 获取更新条件 + * @access protected + * @return mixed + */ + protected function getWhere() + { + // 删除条件 + $pk = $this->getPk(); + + if (is_string($pk) && isset($this->data[$pk])) { + $where = [$pk => $this->data[$pk]]; + } elseif (!empty($this->updateWhere)) { + $where = $this->updateWhere; + } else { + $where = null; + } + return $where; + } + + /** + * 保存多个数据到当前数据对象 + * @access public + * @param array $dataSet 数据 + * @param boolean $replace 是否自动识别更新和写入 + * @return array|false + * @throws \Exception + */ + public function saveAll($dataSet, $replace = true) + { + if ($this->validate) { + // 数据批量验证 + $validate = $this->validate; + foreach ($dataSet as $data) { + if (!$this->validateData($data, $validate)) { + return false; + } + } + } + + $result = []; + $db = $this->getQuery(); + $db->startTrans(); + try { + $pk = $this->getPk(); + if (is_string($pk) && $replace) { + $auto = true; + } + foreach ($dataSet as $key => $data) { + if ($this->isUpdate || (!empty($auto) && isset($data[$pk]))) { + $result[$key] = self::update($data, [], $this->field); + } else { + $result[$key] = self::create($data, $this->field); + } + } + $db->commit(); + return $this->toCollection($result); + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 设置允许写入的字段 + * @access public + * @param string|array $field 允许写入的字段 如果为true只允许写入数据表字段 + * @return $this + */ + public function allowField($field) + { + if (is_string($field)) { + $field = explode(',', $field); + } + $this->field = $field; + return $this; + } + + /** + * 设置排除写入的字段 + * @access public + * @param string|array $field 排除允许写入的字段 + * @return $this + */ + public function except($field) + { + if (is_string($field)) { + $field = explode(',', $field); + } + $this->except = $field; + return $this; + } + + /** + * 设置只读字段 + * @access public + * @param mixed $field 只读字段 + * @return $this + */ + public function readonly($field) + { + if (is_string($field)) { + $field = explode(',', $field); + } + $this->readonly = $field; + return $this; + } + + /** + * 是否为更新数据 + * @access public + * @param bool $update + * @param mixed $where + * @return $this + */ + public function isUpdate($update = true, $where = null) + { + $this->isUpdate = $update; + if (!empty($where)) { + $this->updateWhere = $where; + } + return $this; + } + + /** + * 数据自动完成 + * @access public + * @param array $auto 要自动更新的字段列表 + * @return void + */ + protected function autoCompleteData($auto = []) + { + foreach ($auto as $field => $value) { + if (is_integer($field)) { + $field = $value; + $value = null; + } + + if (!isset($this->data[$field])) { + $default = null; + } else { + $default = $this->data[$field]; + } + + $this->setAttr($field, !is_null($value) ? $value : $default); + } + } + + /** + * 删除当前的记录 + * @access public + * @return integer + */ + public function delete() + { + if (false === $this->trigger('before_delete', $this)) { + return false; + } + + // 删除条件 + $where = $this->getWhere(); + + // 删除当前模型数据 + $result = $this->getQuery()->where($where)->delete(); + + // 关联删除 + if (!empty($this->relationWrite)) { + foreach ($this->relationWrite as $key => $name) { + $name = is_numeric($key) ? $name : $key; + $model = $this->getAttr($name); + if ($model instanceof Model) { + $model->delete(); + } + } + } + + $this->trigger('after_delete', $this); + // 清空原始数据 + $this->origin = []; + + return $result; + } + + /** + * 设置自动完成的字段( 规则通过修改器定义) + * @access public + * @param array $fields 需要自动完成的字段 + * @return $this + */ + public function auto($fields) + { + $this->auto = $fields; + return $this; + } + + /** + * 设置字段验证 + * @access public + * @param array|string|bool $rule 验证规则 true表示自动读取验证器类 + * @param array $msg 提示信息 + * @param bool $batch 批量验证 + * @return $this + */ + public function validate($rule = true, $msg = [], $batch = false) + { + if (is_array($rule)) { + $this->validate = [ + 'rule' => $rule, + 'msg' => $msg, + ]; + } else { + $this->validate = true === $rule ? $this->name : $rule; + } + $this->batchValidate = $batch; + return $this; + } + + /** + * 设置验证失败后是否抛出异常 + * @access public + * @param bool $fail 是否抛出异常 + * @return $this + */ + public function validateFailException($fail = true) + { + $this->failException = $fail; + return $this; + } + + /** + * 自动验证数据 + * @access protected + * @param array $data 验证数据 + * @param mixed $rule 验证规则 + * @param bool $batch 批量验证 + * @return bool + */ + protected function validateData($data, $rule = null, $batch = null) + { + $info = is_null($rule) ? $this->validate : $rule; + + if (!empty($info)) { + if (is_array($info)) { + $validate = Loader::validate(); + $validate->rule($info['rule']); + $validate->message($info['msg']); + } else { + $name = is_string($info) ? $info : $this->name; + if (strpos($name, '.')) { + list($name, $scene) = explode('.', $name); + } + $validate = Loader::validate($name); + if (!empty($scene)) { + $validate->scene($scene); + } + } + $batch = is_null($batch) ? $this->batchValidate : $batch; + + if (!$validate->batch($batch)->check($data)) { + $this->error = $validate->getError(); + if ($this->failException) { + throw new ValidateException($this->error); + } else { + return false; + } + } + $this->validate = null; + } + return true; + } + + /** + * 返回模型的错误信息 + * @access public + * @return string|array + */ + public function getError() + { + return $this->error; + } + + /** + * 注册回调方法 + * @access public + * @param string $event 事件名 + * @param callable $callback 回调方法 + * @param bool $override 是否覆盖 + * @return void + */ + public static function event($event, $callback, $override = false) + { + $class = get_called_class(); + if ($override) { + self::$event[$class][$event] = []; + } + self::$event[$class][$event][] = $callback; + } + + /** + * 触发事件 + * @access protected + * @param string $event 事件名 + * @param mixed $params 传入参数(引用) + * @return bool + */ + protected function trigger($event, &$params) + { + if (isset(self::$event[$this->class][$event])) { + foreach (self::$event[$this->class][$event] as $callback) { + if (is_callable($callback)) { + $result = call_user_func_array($callback, [ & $params]); + if (false === $result) { + return false; + } + } + } + } + return true; + } + + /** + * 写入数据 + * @access public + * @param array $data 数据数组 + * @param array|true $field 允许字段 + * @return $this + */ + public static function create($data = [], $field = null) + { + $model = new static(); + if (!empty($field)) { + $model->allowField($field); + } + $model->isUpdate(false)->save($data, []); + return $model; + } + + /** + * 更新数据 + * @access public + * @param array $data 数据数组 + * @param array $where 更新条件 + * @param array|true $field 允许字段 + * @return $this + */ + public static function update($data = [], $where = [], $field = null) + { + $model = new static(); + if (!empty($field)) { + $model->allowField($field); + } + $result = $model->isUpdate(true)->save($data, $where); + return $model; + } + + /** + * 查找单条记录 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static|null + * @throws exception\DbException + */ + public static function get($data, $with = [], $cache = false) + { + if (is_null($data)) { + return; + } + + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + $query = static::parseQuery($data, $with, $cache); + return $query->find($data); + } + + /** + * 查找所有记录 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return static[]|false + * @throws exception\DbException + */ + public static function all($data = null, $with = [], $cache = false) + { + if (true === $with || is_int($with)) { + $cache = $with; + $with = []; + } + $query = static::parseQuery($data, $with, $cache); + return $query->select($data); + } + + /** + * 分析查询表达式 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param string $with 关联预查询 + * @param bool $cache 是否缓存 + * @return Query + */ + protected static function parseQuery(&$data, $with, $cache) + { + $result = self::with($with)->cache($cache); + if (is_array($data) && key($data) !== 0) { + $result = $result->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $result]); + $data = null; + } elseif ($data instanceof Query) { + $result = $data->with($with)->cache($cache); + $data = null; + } + return $result; + } + + /** + * 删除记录 + * @access public + * @param mixed $data 主键列表 支持闭包查询条件 + * @return integer 成功删除的记录数 + */ + public static function destroy($data) + { + $model = new static(); + $query = $model->db(); + if (empty($data) && 0 !== $data) { + return 0; + } elseif (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $query]); + $data = null; + } + $resultSet = $query->select($data); + $count = 0; + if ($resultSet) { + foreach ($resultSet as $data) { + $result = $data->delete(); + $count += $result; + } + } + return $count; + } + + /** + * 命名范围 + * @access public + * @param string|array|\Closure $name 命名范围名称 逗号分隔 + * @internal mixed ...$params 参数调用 + * @return Query + */ + public static function scope($name) + { + $model = new static(); + $query = $model->db(); + $params = func_get_args(); + array_shift($params); + array_unshift($params, $query); + if ($name instanceof \Closure) { + call_user_func_array($name, $params); + } elseif (is_string($name)) { + $name = explode(',', $name); + } + if (is_array($name)) { + foreach ($name as $scope) { + $method = 'scope' . trim($scope); + if (method_exists($model, $method)) { + call_user_func_array([$model, $method], $params); + } + } + } + return $query; + } + + /** + * 设置是否使用全局查询范围 + * @param bool $use 是否启用全局查询范围 + * @access public + * @return Query + */ + public static function useGlobalScope($use) + { + $model = new static(); + return $model->db($use); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @return Relation|Query + */ + public static function has($relation, $operator = '>=', $count = 1, $id = '*') + { + $relation = (new static())->$relation(); + if (is_array($operator) || $operator instanceof \Closure) { + return $relation->hasWhere($operator); + } + return $relation->has($operator, $count, $id); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $relation 关联方法名 + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Relation|Query + */ + public static function hasWhere($relation, $where = [], $fields = null) + { + return (new static())->$relation()->hasWhere($where, $fields); + } + + /** + * 解析模型的完整命名空间 + * @access public + * @param string $model 模型名(或者完整类名) + * @return string + */ + protected function parseModel($model) + { + if (false === strpos($model, '\\')) { + $path = explode('\\', get_called_class()); + array_pop($path); + array_push($path, Loader::parseName($model, 1)); + $model = implode('\\', $path); + } + return $model; + } + + /** + * 查询当前模型的关联数据 + * @access public + * @param string|array $relations 关联名 + * @return $this + */ + public function relationQuery($relations) + { + if (is_string($relations)) { + $relations = explode(',', $relations); + } + + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = null; + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + } + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + $method = Loader::parseName($relation, 1, false); + $this->data[$relation] = $this->$method()->getRelation($subRelation, $closure); + } + return $this; + } + + /** + * 预载入关联查询 返回数据集 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 关联名 + * @return array + */ + public function eagerlyResultSet(&$resultSet, $relation) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = false; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + $relation = Loader::parseName($relation, 1, false); + $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure); + } + } + + /** + * 预载入关联查询 返回模型对象 + * @access public + * @param Model $result 数据对象 + * @param string $relation 关联名 + * @return Model + */ + public function eagerlyResult(&$result, $relation) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + foreach ($relations as $key => $relation) { + $subRelation = ''; + $closure = false; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } + if (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (strpos($relation, '.')) { + list($relation, $subRelation) = explode('.', $relation, 2); + } + $relation = Loader::parseName($relation, 1, false); + $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param string|array $relation 关联名 + * @return void + */ + public function relationCount(&$result, $relation) + { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + + foreach ($relations as $key => $relation) { + $closure = false; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } elseif (is_string($key)) { + $name = $relation; + $relation = $key; + } + $relation = Loader::parseName($relation, 1, false); + $count = $this->$relation()->relationCount($result, $closure); + if (!isset($name)) { + $name = Loader::parseName($relation) . '_count'; + } + $result->setAttr($name, $count); + } + } + + /** + * 获取模型的默认外键名 + * @access public + * @param string $name 模型名 + * @return string + */ + protected function getForeignKey($name) + { + if (strpos($name, '\\')) { + $name = basename(str_replace('\\', '/', $name)); + } + return Loader::parseName($name) . '_id'; + } + + /** + * HAS ONE 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 + * @param array $alias 别名定义(已经废弃) + * @param string $joinType JOIN类型 + * @return HasOne + */ + public function hasOne($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + return new HasOne($this, $model, $foreignKey, $localKey, $joinType); + } + + /** + * BELONGS TO 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 + * @param array $alias 别名定义(已经废弃) + * @param string $joinType JOIN类型 + * @return BelongsTo + */ + public function belongsTo($model, $foreignKey = '', $localKey = '', $alias = [], $joinType = 'INNER') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $foreignKey = $foreignKey ?: $this->getForeignKey($model); + $localKey = $localKey ?: (new $model)->getPk(); + $trace = debug_backtrace(false, 2); + $relation = Loader::parseName($trace[1]['function']); + return new BelongsTo($this, $model, $foreignKey, $localKey, $joinType, $relation); + } + + /** + * HAS MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 + * @return HasMany + */ + public function hasMany($model, $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + return new HasMany($this, $model, $foreignKey, $localKey); + } + + /** + * HAS MANY 远程关联定义 + * @access public + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 当前模型主键 + * @return HasManyThrough + */ + public function hasManyThrough($model, $through, $foreignKey = '', $throughKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $through = $this->parseModel($through); + $localKey = $localKey ?: $this->getPk(); + $foreignKey = $foreignKey ?: $this->getForeignKey($this->name); + $throughKey = $throughKey ?: $this->getForeignKey($through); + return new HasManyThrough($this, $model, $through, $foreignKey, $throughKey, $localKey); + } + + /** + * BELONGS TO MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型关联键 + * @return BelongsToMany + */ + public function belongsToMany($model, $table = '', $foreignKey = '', $localKey = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + $name = Loader::parseName(basename(str_replace('\\', '/', $model))); + $table = $table ?: Loader::parseName($this->name) . '_' . $name; + $foreignKey = $foreignKey ?: $name . '_id'; + $localKey = $localKey ?: $this->getForeignKey($this->name); + return new BelongsToMany($this, $model, $table, $foreignKey, $localKey); + } + + /** + * MORPH MANY 关联定义 + * @access public + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 + * @return MorphMany + */ + public function morphMany($model, $morph = null, $type = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + if (is_null($morph)) { + $trace = debug_backtrace(false, 2); + $morph = Loader::parseName($trace[1]['function']); + } + $type = $type ?: get_class($this); + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + return new MorphMany($this, $model, $foreignKey, $morphType, $type); + } + + /** + * MORPH One 关联定义 + * @access public + * @param string $model 模型名 + * @param string|array $morph 多态字段信息 + * @param string $type 多态类型 + * @return MorphOne + */ + public function morphOne($model, $morph = null, $type = '') + { + // 记录当前关联信息 + $model = $this->parseModel($model); + if (is_null($morph)) { + $trace = debug_backtrace(false, 2); + $morph = Loader::parseName($trace[1]['function']); + } + $type = $type ?: get_class($this); + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + return new MorphOne($this, $model, $foreignKey, $morphType, $type); + } + + /** + * MORPH TO 关联定义 + * @access public + * @param string|array $morph 多态字段信息 + * @param array $alias 多态别名定义 + * @return MorphTo + */ + public function morphTo($morph = null, $alias = []) + { + $trace = debug_backtrace(false, 2); + $relation = Loader::parseName($trace[1]['function']); + + if (is_null($morph)) { + $morph = $relation; + } + // 记录当前关联信息 + if (is_array($morph)) { + list($morphType, $foreignKey) = $morph; + } else { + $morphType = $morph . '_type'; + $foreignKey = $morph . '_id'; + } + return new MorphTo($this, $morphType, $foreignKey, $alias, $relation); + } + + public function __call($method, $args) + { + $query = $this->db(true, false); + if (method_exists($this, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $query); + call_user_func_array([$this, $method], $args); + return $this; + } else { + return call_user_func_array([$query, $method], $args); + } + } + + public static function __callStatic($method, $args) + { + $model = new static(); + $query = $model->db(); + if (method_exists($model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $query); + + call_user_func_array([$model, $method], $args); + return $query; + } else { + return call_user_func_array([$query, $method], $args); + } + } + + /** + * 修改器 设置数据对象的值 + * @access public + * @param string $name 名称 + * @param mixed $value 值 + * @return void + */ + public function __set($name, $value) + { + $this->setAttr($name, $value); + } + + /** + * 获取器 获取数据对象的值 + * @access public + * @param string $name 名称 + * @return mixed + */ + public function __get($name) + { + return $this->getAttr($name); + } + + /** + * 检测数据对象的值 + * @access public + * @param string $name 名称 + * @return boolean + */ + public function __isset($name) + { + try { + if (array_key_exists($name, $this->data) || array_key_exists($name, $this->relation)) { + return true; + } else { + $this->getAttr($name); + return true; + } + } catch (InvalidArgumentException $e) { + return false; + } + + } + + /** + * 销毁数据对象的值 + * @access public + * @param string $name 名称 + * @return void + */ + public function __unset($name) + { + unset($this->data[$name], $this->relation[$name]); + } + + public function __toString() + { + return $this->toJson(); + } + + // JsonSerializable + public function jsonSerialize() + { + return $this->toArray(); + } + + // ArrayAccess + public function offsetSet($name, $value) + { + $this->setAttr($name, $value); + } + + public function offsetExists($name) + { + return $this->__isset($name); + } + + public function offsetUnset($name) + { + $this->__unset($name); + } + + public function offsetGet($name) + { + return $this->getAttr($name); + } + + /** + * 解序列化后处理 + */ + public function __wakeup() + { + $this->initialize(); + } + + /** + * 模型事件快捷方法 + * @param $callback + * @param bool $override + */ + protected static function beforeInsert($callback, $override = false) + { + self::event('before_insert', $callback, $override); + } + + protected static function afterInsert($callback, $override = false) + { + self::event('after_insert', $callback, $override); + } + + protected static function beforeUpdate($callback, $override = false) + { + self::event('before_update', $callback, $override); + } + + protected static function afterUpdate($callback, $override = false) + { + self::event('after_update', $callback, $override); + } + + protected static function beforeWrite($callback, $override = false) + { + self::event('before_write', $callback, $override); + } + + protected static function afterWrite($callback, $override = false) + { + self::event('after_write', $callback, $override); + } + + protected static function beforeDelete($callback, $override = false) + { + self::event('before_delete', $callback, $override); + } + + protected static function afterDelete($callback, $override = false) + { + self::event('after_delete', $callback, $override); + } + +} diff --git a/source/thinkphp/library/think/Paginator.php b/source/thinkphp/library/think/Paginator.php new file mode 100644 index 0000000..3655567 --- /dev/null +++ b/source/thinkphp/library/think/Paginator.php @@ -0,0 +1,409 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use ArrayAccess; +use ArrayIterator; +use Countable; +use IteratorAggregate; +use JsonSerializable; +use Traversable; + +abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable +{ + /** @var bool 是否为简洁模式 */ + protected $simple = false; + + /** @var Collection 数据集 */ + protected $items; + + /** @var integer 当前页 */ + protected $currentPage; + + /** @var integer 最后一页 */ + protected $lastPage; + + /** @var integer|null 数据总数 */ + protected $total; + + /** @var integer 每页的数量 */ + protected $listRows; + + /** @var bool 是否有下一页 */ + protected $hasMore; + + /** @var array 一些配置 */ + protected $options = [ + 'var_page' => 'page', + 'path' => '/', + 'query' => [], + 'fragment' => '', + ]; + + /** @var mixed simple模式下的下个元素 */ + protected $nextItem; + + public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) + { + $this->options = array_merge($this->options, $options); + + $this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path']; + + $this->simple = $simple; + $this->listRows = $listRows; + + if (!$items instanceof Collection) { + $items = Collection::make($items); + } + + if ($simple) { + $this->currentPage = $this->setCurrentPage($currentPage); + $this->hasMore = count($items) > ($this->listRows); + if ($this->hasMore) { + $this->nextItem = $items->slice($this->listRows, 1); + } + $items = $items->slice(0, $this->listRows); + } else { + $this->total = $total; + $this->lastPage = (int) ceil($total / $listRows); + $this->currentPage = $this->setCurrentPage($currentPage); + $this->hasMore = $this->currentPage < $this->lastPage; + } + $this->items = $items; + } + + /** + * @param $items + * @param $listRows + * @param null $currentPage + * @param bool $simple + * @param null $total + * @param array $options + * @return Paginator + */ + public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = []) + { + return new static($items, $listRows, $currentPage, $total, $simple, $options); + } + + protected function setCurrentPage($currentPage) + { + if (!$this->simple && $currentPage > $this->lastPage) { + return $this->lastPage > 0 ? $this->lastPage : 1; + } + + return $currentPage; + } + + /** + * 获取页码对应的链接 + * + * @param $page + * @return string + */ + protected function url($page) + { + if ($page <= 0) { + $page = 1; + } + + if (strpos($this->options['path'], '[PAGE]') === false) { + $parameters = [$this->options['var_page'] => $page]; + $path = $this->options['path']; + } else { + $parameters = []; + $path = str_replace('[PAGE]', $page, $this->options['path']); + } + if (count($this->options['query']) > 0) { + $parameters = array_merge($this->options['query'], $parameters); + } + $url = $path; + if (!empty($parameters)) { + $url .= '?' . http_build_query($parameters, null, '&'); + } + return $url . $this->buildFragment(); + } + + /** + * 自动获取当前页码 + * @param string $varPage + * @param int $default + * @return int + */ + public static function getCurrentPage($varPage = 'page', $default = 1) + { + $page = (int) Request::instance()->param($varPage); + + if (filter_var($page, FILTER_VALIDATE_INT) !== false && $page >= 1) { + return $page; + } + + return $default; + } + + /** + * 自动获取当前的path + * @return string + */ + public static function getCurrentPath() + { + return Request::instance()->baseUrl(); + } + + public function total() + { + if ($this->simple) { + throw new \DomainException('not support total'); + } + return $this->total; + } + + public function listRows() + { + return $this->listRows; + } + + public function currentPage() + { + return $this->currentPage; + } + + public function lastPage() + { + if ($this->simple) { + throw new \DomainException('not support last'); + } + return $this->lastPage; + } + + /** + * 数据是否足够分页 + * @return boolean + */ + public function hasPages() + { + return !(1 == $this->currentPage && !$this->hasMore); + } + + /** + * 创建一组分页链接 + * + * @param int $start + * @param int $end + * @return array + */ + public function getUrlRange($start, $end) + { + $urls = []; + + for ($page = $start; $page <= $end; $page++) { + $urls[$page] = $this->url($page); + } + + return $urls; + } + + /** + * 设置URL锚点 + * + * @param string|null $fragment + * @return $this + */ + public function fragment($fragment) + { + $this->options['fragment'] = $fragment; + return $this; + } + + /** + * 添加URL参数 + * + * @param array|string $key + * @param string|null $value + * @return $this + */ + public function appends($key, $value = null) + { + if (!is_array($key)) { + $queries = [$key => $value]; + } else { + $queries = $key; + } + + foreach ($queries as $k => $v) { + if ($k !== $this->options['var_page']) { + $this->options['query'][$k] = $v; + } + } + + return $this; + } + + /** + * 构造锚点字符串 + * + * @return string + */ + protected function buildFragment() + { + return $this->options['fragment'] ? '#' . $this->options['fragment'] : ''; + } + + /** + * 渲染分页html + * @return mixed + */ + abstract public function render(); + + public function items() + { + return $this->items->all(); + } + + public function getCollection() + { + return $this->items; + } + + public function isEmpty() + { + return $this->items->isEmpty(); + } + + /** + * 给每个元素执行个回调 + * + * @param callable $callback + * @return $this + */ + public function each(callable $callback) + { + foreach ($this->items as $key => $item) { + $result = $callback($item, $key); + if (false === $result) { + break; + } elseif (!is_object($item)) { + $this->items[$key] = $result; + } + } + + return $this; + } + + /** + * Retrieve an external iterator + * @return Traversable An instance of an object implementing Iterator or + * Traversable + */ + public function getIterator() + { + return new ArrayIterator($this->items->all()); + } + + /** + * Whether a offset exists + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return $this->items->offsetExists($offset); + } + + /** + * Offset to retrieve + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->items->offsetGet($offset); + } + + /** + * Offset to set + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->items->offsetSet($offset, $value); + } + + /** + * Offset to unset + * @param mixed $offset + * @return void + * @since 5.0.0 + */ + public function offsetUnset($offset) + { + $this->items->offsetUnset($offset); + } + + /** + * Count elements of an object + */ + public function count() + { + return $this->items->count(); + } + + public function __toString() + { + return (string) $this->render(); + } + + public function toArray() + { + if ($this->simple) { + return [ + 'per_page' => $this->listRows, + 'current_page' => $this->currentPage, + 'has_more' => $this->hasMore, + 'next_item' => $this->nextItem, + 'data' => $this->items->toArray(), + ]; + } else { + return [ + 'total' => $this->total, + 'per_page' => $this->listRows, + 'current_page' => $this->currentPage, + 'last_page' => $this->lastPage, + 'data' => $this->items->toArray(), + ]; + } + + } + + /** + * Specify data which should be serialized to JSON + */ + public function jsonSerialize() + { + return $this->toArray(); + } + + public function __call($name, $arguments) + { + $collection = $this->getCollection(); + + $result = call_user_func_array([$collection, $name], $arguments); + + if ($result === $collection) { + return $this; + } + + return $result; + } + +} diff --git a/source/thinkphp/library/think/Process.php b/source/thinkphp/library/think/Process.php new file mode 100644 index 0000000..6f3faa3 --- /dev/null +++ b/source/thinkphp/library/think/Process.php @@ -0,0 +1,1205 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\process\exception\Failed as ProcessFailedException; +use think\process\exception\Timeout as ProcessTimeoutException; +use think\process\pipes\Pipes; +use think\process\pipes\Unix as UnixPipes; +use think\process\pipes\Windows as WindowsPipes; +use think\process\Utils; + +class Process +{ + + const ERR = 'err'; + const OUT = 'out'; + + const STATUS_READY = 'ready'; + const STATUS_STARTED = 'started'; + const STATUS_TERMINATED = 'terminated'; + + const STDIN = 0; + const STDOUT = 1; + const STDERR = 2; + + const TIMEOUT_PRECISION = 0.2; + + private $callback; + private $commandline; + private $cwd; + private $env; + private $input; + private $starttime; + private $lastOutputTime; + private $timeout; + private $idleTimeout; + private $options; + private $exitcode; + private $fallbackExitcode; + private $processInformation; + private $outputDisabled = false; + private $stdout; + private $stderr; + private $enhanceWindowsCompatibility = true; + private $enhanceSigchildCompatibility; + private $process; + private $status = self::STATUS_READY; + private $incrementalOutputOffset = 0; + private $incrementalErrorOutputOffset = 0; + private $tty; + private $pty; + + private $useFileHandles = false; + + /** @var Pipes */ + private $processPipes; + + private $latestSignal; + + private static $sigchild; + + /** + * @var array + */ + public static $exitCodes = [ + 0 => 'OK', + 1 => 'General error', + 2 => 'Misuse of shell builtins', + 126 => 'Invoked command cannot execute', + 127 => 'Command not found', + 128 => 'Invalid exit argument', + // signals + 129 => 'Hangup', + 130 => 'Interrupt', + 131 => 'Quit and dump core', + 132 => 'Illegal instruction', + 133 => 'Trace/breakpoint trap', + 134 => 'Process aborted', + 135 => 'Bus error: "access to undefined portion of memory object"', + 136 => 'Floating point exception: "erroneous arithmetic operation"', + 137 => 'Kill (terminate immediately)', + 138 => 'User-defined 1', + 139 => 'Segmentation violation', + 140 => 'User-defined 2', + 141 => 'Write to pipe with no one reading', + 142 => 'Signal raised by alarm', + 143 => 'Termination (request to terminate)', + // 144 - not defined + 145 => 'Child process terminated, stopped (or continued*)', + 146 => 'Continue if stopped', + 147 => 'Stop executing temporarily', + 148 => 'Terminal stop signal', + 149 => 'Background process attempting to read from tty ("in")', + 150 => 'Background process attempting to write to tty ("out")', + 151 => 'Urgent data available on socket', + 152 => 'CPU time limit exceeded', + 153 => 'File size limit exceeded', + 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"', + 155 => 'Profiling timer expired', + // 156 - not defined + 157 => 'Pollable event', + // 158 - not defined + 159 => 'Bad syscall', + ]; + + /** + * 构造方法 + * @param string $commandline 指令 + * @param string|null $cwd 工作目录 + * @param array|null $env 环境变量 + * @param string|null $input 输入 + * @param int|float|null $timeout 超时时间 + * @param array $options proc_open的选项 + * @throws \RuntimeException + * @api + */ + public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = []) + { + if (!function_exists('proc_open')) { + throw new \RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.'); + } + + $this->commandline = $commandline; + $this->cwd = $cwd; + + if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || '\\' === DS)) { + $this->cwd = getcwd(); + } + if (null !== $env) { + $this->setEnv($env); + } + + $this->input = $input; + $this->setTimeout($timeout); + $this->useFileHandles = '\\' === DS; + $this->pty = false; + $this->enhanceWindowsCompatibility = true; + $this->enhanceSigchildCompatibility = '\\' !== DS && $this->isSigchildEnabled(); + $this->options = array_replace([ + 'suppress_errors' => true, + 'binary_pipes' => true, + ], $options); + } + + public function __destruct() + { + $this->stop(); + } + + public function __clone() + { + $this->resetProcessData(); + } + + /** + * 运行指令 + * @param callback|null $callback + * @return int + */ + public function run($callback = null) + { + $this->start($callback); + + return $this->wait(); + } + + /** + * 运行指令 + * @param callable|null $callback + * @return self + * @throws \RuntimeException + * @throws ProcessFailedException + */ + public function mustRun($callback = null) + { + if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); + } + + if (0 !== $this->run($callback)) { + throw new ProcessFailedException($this); + } + + return $this; + } + + /** + * 启动进程并写到 STDIN 输入后返回。 + * @param callable|null $callback + * @throws \RuntimeException + * @throws \RuntimeException + * @throws \LogicException + */ + public function start($callback = null) + { + if ($this->isRunning()) { + throw new \RuntimeException('Process is already running'); + } + if ($this->outputDisabled && null !== $callback) { + throw new \LogicException('Output has been disabled, enable it to allow the use of a callback.'); + } + + $this->resetProcessData(); + $this->starttime = $this->lastOutputTime = microtime(true); + $this->callback = $this->buildCallback($callback); + $descriptors = $this->getDescriptors(); + + $commandline = $this->commandline; + + if ('\\' === DS && $this->enhanceWindowsCompatibility) { + $commandline = 'cmd /V:ON /E:ON /C "(' . $commandline . ')'; + foreach ($this->processPipes->getFiles() as $offset => $filename) { + $commandline .= ' ' . $offset . '>' . Utils::escapeArgument($filename); + } + $commandline .= '"'; + + if (!isset($this->options['bypass_shell'])) { + $this->options['bypass_shell'] = true; + } + } + + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); + + if (!is_resource($this->process)) { + throw new \RuntimeException('Unable to launch a new process.'); + } + $this->status = self::STATUS_STARTED; + + if ($this->tty) { + return; + } + + $this->updateStatus(false); + $this->checkTimeout(); + } + + /** + * 重启进程 + * @param callable|null $callback + * @return Process + * @throws \RuntimeException + * @throws \RuntimeException + */ + public function restart($callback = null) + { + if ($this->isRunning()) { + throw new \RuntimeException('Process is already running'); + } + + $process = clone $this; + $process->start($callback); + + return $process; + } + + /** + * 等待要终止的进程 + * @param callable|null $callback + * @return int + */ + public function wait($callback = null) + { + $this->requireProcessIsStarted(__FUNCTION__); + + $this->updateStatus(false); + if (null !== $callback) { + $this->callback = $this->buildCallback($callback); + } + + do { + $this->checkTimeout(); + $running = '\\' === DS ? $this->isRunning() : $this->processPipes->areOpen(); + $close = '\\' !== DS || !$running; + $this->readPipes(true, $close); + } while ($running); + + while ($this->isRunning()) { + usleep(1000); + } + + if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) { + throw new \RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); + } + + return $this->exitcode; + } + + /** + * 获取PID + * @return int|null + * @throws \RuntimeException + */ + public function getPid() + { + if ($this->isSigchildEnabled()) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->isRunning() ? $this->processInformation['pid'] : null; + } + + /** + * 将一个 POSIX 信号发送到进程中 + * @param int $signal + * @return Process + */ + public function signal($signal) + { + $this->doSignal($signal, true); + + return $this; + } + + /** + * 禁用从底层过程获取输出和错误输出。 + * @return Process + */ + public function disableOutput() + { + if ($this->isRunning()) { + throw new \RuntimeException('Disabling output while the process is running is not possible.'); + } + if (null !== $this->idleTimeout) { + throw new \LogicException('Output can not be disabled while an idle timeout is set.'); + } + + $this->outputDisabled = true; + + return $this; + } + + /** + * 开启从底层过程获取输出和错误输出。 + * @return Process + * @throws \RuntimeException + */ + public function enableOutput() + { + if ($this->isRunning()) { + throw new \RuntimeException('Enabling output while the process is running is not possible.'); + } + + $this->outputDisabled = false; + + return $this; + } + + /** + * 输出是否禁用 + * @return bool + */ + public function isOutputDisabled() + { + return $this->outputDisabled; + } + + /** + * 获取当前的输出管道 + * @return string + * @throws \LogicException + * @throws \LogicException + * @api + */ + public function getOutput() + { + if ($this->outputDisabled) { + throw new \LogicException('Output has been disabled.'); + } + + $this->requireProcessIsStarted(__FUNCTION__); + + $this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true); + + return $this->stdout; + } + + /** + * 以增量方式返回的输出结果。 + * @return string + */ + public function getIncrementalOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $data = $this->getOutput(); + + $latest = substr($data, $this->incrementalOutputOffset); + + if (false === $latest) { + return ''; + } + + $this->incrementalOutputOffset = strlen($data); + + return $latest; + } + + /** + * 清空输出 + * @return Process + */ + public function clearOutput() + { + $this->stdout = ''; + $this->incrementalOutputOffset = 0; + + return $this; + } + + /** + * 返回当前的错误输出的过程 (STDERR)。 + * @return string + */ + public function getErrorOutput() + { + if ($this->outputDisabled) { + throw new \LogicException('Output has been disabled.'); + } + + $this->requireProcessIsStarted(__FUNCTION__); + + $this->readPipes(false, '\\' === DS ? !$this->processInformation['running'] : true); + + return $this->stderr; + } + + /** + * 以增量方式返回 errorOutput + * @return string + */ + public function getIncrementalErrorOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $data = $this->getErrorOutput(); + + $latest = substr($data, $this->incrementalErrorOutputOffset); + + if (false === $latest) { + return ''; + } + + $this->incrementalErrorOutputOffset = strlen($data); + + return $latest; + } + + /** + * 清空 errorOutput + * @return Process + */ + public function clearErrorOutput() + { + $this->stderr = ''; + $this->incrementalErrorOutputOffset = 0; + + return $this; + } + + /** + * 获取退出码 + * @return null|int + */ + public function getExitCode() + { + if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); + } + + $this->updateStatus(false); + + return $this->exitcode; + } + + /** + * 获取退出文本 + * @return null|string + */ + public function getExitCodeText() + { + if (null === $exitcode = $this->getExitCode()) { + return; + } + + return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error'; + } + + /** + * 检查是否成功 + * @return bool + */ + public function isSuccessful() + { + return 0 === $this->getExitCode(); + } + + /** + * 是否未捕获的信号已被终止子进程 + * @return bool + */ + public function hasBeenSignaled() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + if ($this->isSigchildEnabled()) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->processInformation['signaled']; + } + + /** + * 返回导致子进程终止其执行的数。 + * @return int + */ + public function getTermSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + if ($this->isSigchildEnabled()) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->processInformation['termsig']; + } + + /** + * 检查子进程信号是否已停止 + * @return bool + */ + public function hasBeenStopped() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + $this->updateStatus(false); + + return $this->processInformation['stopped']; + } + + /** + * 返回导致子进程停止其执行的数。 + * @return int + */ + public function getStopSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + $this->updateStatus(false); + + return $this->processInformation['stopsig']; + } + + /** + * 检查是否正在运行 + * @return bool + */ + public function isRunning() + { + if (self::STATUS_STARTED !== $this->status) { + return false; + } + + $this->updateStatus(false); + + return $this->processInformation['running']; + } + + /** + * 检查是否已开始 + * @return bool + */ + public function isStarted() + { + return self::STATUS_READY != $this->status; + } + + /** + * 检查是否已终止 + * @return bool + */ + public function isTerminated() + { + $this->updateStatus(false); + + return self::STATUS_TERMINATED == $this->status; + } + + /** + * 获取当前的状态 + * @return string + */ + public function getStatus() + { + $this->updateStatus(false); + + return $this->status; + } + + /** + * 终止进程 + */ + public function stop() + { + if ($this->isRunning()) { + if ('\\' === DS && !$this->isSigchildEnabled()) { + exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode); + if ($exitCode > 0) { + throw new \RuntimeException('Unable to kill the process'); + } + } else { + $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid {$this->getPid()}`); + foreach ($pids as $pid) { + if (is_numeric($pid)) { + posix_kill($pid, 9); + } + } + } + } + + $this->updateStatus(false); + if ($this->processInformation['running']) { + $this->close(); + } + + return $this->exitcode; + } + + /** + * 添加一行输出 + * @param string $line + */ + public function addOutput($line) +{ + $this->lastOutputTime = microtime(true); + $this->stdout .= $line; + } + + /** + * 添加一行错误输出 + * @param string $line + */ + public function addErrorOutput($line) +{ + $this->lastOutputTime = microtime(true); + $this->stderr .= $line; + } + + /** + * 获取被执行的指令 + * @return string + */ + public function getCommandLine() +{ + return $this->commandline; + } + + /** + * 设置指令 + * @param string $commandline + * @return self + */ + public function setCommandLine($commandline) +{ + $this->commandline = $commandline; + + return $this; + } + + /** + * 获取超时时间 + * @return float|null + */ + public function getTimeout() +{ + return $this->timeout; + } + + /** + * 获取idle超时时间 + * @return float|null + */ + public function getIdleTimeout() +{ + return $this->idleTimeout; + } + + /** + * 设置超时时间 + * @param int|float|null $timeout + * @return self + */ + public function setTimeout($timeout) +{ + $this->timeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * 设置idle超时时间 + * @param int|float|null $timeout + * @return self + */ + public function setIdleTimeout($timeout) +{ + if (null !== $timeout && $this->outputDisabled) { + throw new \LogicException('Idle timeout can not be set while the output is disabled.'); + } + + $this->idleTimeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * 设置TTY + * @param bool $tty + * @return self + */ + public function setTty($tty) +{ + if ('\\' === DS && $tty) { + throw new \RuntimeException('TTY mode is not supported on Windows platform.'); + } + if ($tty && (!file_exists('/dev/tty') || !is_readable('/dev/tty'))) { + throw new \RuntimeException('TTY mode requires /dev/tty to be readable.'); + } + + $this->tty = (bool) $tty; + + return $this; + } + + /** + * 检查是否是tty模式 + * @return bool + */ + public function isTty() +{ + return $this->tty; + } + + /** + * 设置pty模式 + * @param bool $bool + * @return self + */ + public function setPty($bool) +{ + $this->pty = (bool) $bool; + + return $this; + } + + /** + * 是否是pty模式 + * @return bool + */ + public function isPty() +{ + return $this->pty; + } + + /** + * 获取工作目录 + * @return string|null + */ + public function getWorkingDirectory() +{ + if (null === $this->cwd) { + return getcwd() ?: null; + } + + return $this->cwd; + } + + /** + * 设置工作目录 + * @param string $cwd + * @return self + */ + public function setWorkingDirectory($cwd) +{ + $this->cwd = $cwd; + + return $this; + } + + /** + * 获取环境变量 + * @return array + */ + public function getEnv() +{ + return $this->env; + } + + /** + * 设置环境变量 + * @param array $env + * @return self + */ + public function setEnv(array $env) +{ + $env = array_filter($env, function ($value) { + return !is_array($value); + }); + + $this->env = []; + foreach ($env as $key => $value) { + $this->env[(binary) $key] = (binary) $value; + } + + return $this; + } + + /** + * 获取输入 + * @return null|string + */ + public function getInput() +{ + return $this->input; + } + + /** + * 设置输入 + * @param mixed $input + * @return self + */ + public function setInput($input) +{ + if ($this->isRunning()) { + throw new \LogicException('Input can not be set while the process is running.'); + } + + $this->input = Utils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input); + + return $this; + } + + /** + * 获取proc_open的选项 + * @return array + */ + public function getOptions() +{ + return $this->options; + } + + /** + * 设置proc_open的选项 + * @param array $options + * @return self + */ + public function setOptions(array $options) +{ + $this->options = $options; + + return $this; + } + + /** + * 是否兼容windows + * @return bool + */ + public function getEnhanceWindowsCompatibility() +{ + return $this->enhanceWindowsCompatibility; + } + + /** + * 设置是否兼容windows + * @param bool $enhance + * @return self + */ + public function setEnhanceWindowsCompatibility($enhance) +{ + $this->enhanceWindowsCompatibility = (bool) $enhance; + + return $this; + } + + /** + * 返回是否 sigchild 兼容模式激活 + * @return bool + */ + public function getEnhanceSigchildCompatibility() +{ + return $this->enhanceSigchildCompatibility; + } + + /** + * 激活 sigchild 兼容性模式。 + * @param bool $enhance + * @return self + */ + public function setEnhanceSigchildCompatibility($enhance) +{ + $this->enhanceSigchildCompatibility = (bool) $enhance; + + return $this; + } + + /** + * 是否超时 + */ + public function checkTimeout() +{ + if (self::STATUS_STARTED !== $this->status) { + return; + } + + if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) { + $this->stop(); + + throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_GENERAL); + } + + if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) { + $this->stop(); + + throw new ProcessTimeoutException($this, ProcessTimeoutException::TYPE_IDLE); + } + } + + /** + * 是否支持pty + * @return bool + */ + public static function isPtySupported() +{ + static $result; + + if (null !== $result) { + return $result; + } + + if ('\\' === DS) { + return $result = false; + } + + $proc = @proc_open('echo 1', [['pty'], ['pty'], ['pty']], $pipes); + if (is_resource($proc)) { + proc_close($proc); + + return $result = true; + } + + return $result = false; + } + + /** + * 创建所需的 proc_open 的描述符 + * @return array + */ + private function getDescriptors() +{ + if ('\\' === DS) { + $this->processPipes = WindowsPipes::create($this, $this->input); + } else { + $this->processPipes = UnixPipes::create($this, $this->input); + } + $descriptors = $this->processPipes->getDescriptors($this->outputDisabled); + + if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + + $descriptors = array_merge($descriptors, [['pipe', 'w']]); + + $this->commandline = '(' . $this->commandline . ') 3>/dev/null; code=$?; echo $code >&3; exit $code'; + } + + return $descriptors; + } + + /** + * 建立 wait () 使用的回调。 + * @param callable|null $callback + * @return callable + */ + protected function buildCallback($callback) +{ + $out = self::OUT; + $callback = function ($type, $data) use ($callback, $out) { + if ($out == $type) { + $this->addOutput($data); + } else { + $this->addErrorOutput($data); + } + + if (null !== $callback) { + call_user_func($callback, $type, $data); + } + }; + + return $callback; + } + + /** + * 更新状态 + * @param bool $blocking + */ + protected function updateStatus($blocking) +{ + if (self::STATUS_STARTED !== $this->status) { + return; + } + + $this->processInformation = proc_get_status($this->process); + $this->captureExitCode(); + + $this->readPipes($blocking, '\\' === DS ? !$this->processInformation['running'] : true); + + if (!$this->processInformation['running']) { + $this->close(); + } + } + + /** + * 是否开启 '--enable-sigchild' + * @return bool + */ + protected function isSigchildEnabled() +{ + if (null !== self::$sigchild) { + return self::$sigchild; + } + + if (!function_exists('phpinfo')) { + return self::$sigchild = false; + } + + ob_start(); + phpinfo(INFO_GENERAL); + + return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); + } + + /** + * 验证是否超时 + * @param int|float|null $timeout + * @return float|null + */ + private function validateTimeout($timeout) +{ + $timeout = (float) $timeout; + + if (0.0 === $timeout) { + $timeout = null; + } elseif ($timeout < 0) { + throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); + } + + return $timeout; + } + + /** + * 读取pipes + * @param bool $blocking + * @param bool $close + */ + private function readPipes($blocking, $close) +{ + $result = $this->processPipes->readAndWrite($blocking, $close); + + $callback = $this->callback; + foreach ($result as $type => $data) { + if (3 == $type) { + $this->fallbackExitcode = (int) $data; + } else { + $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data); + } + } + } + + /** + * 捕获退出码 + */ + private function captureExitCode() +{ + if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) { + $this->exitcode = $this->processInformation['exitcode']; + } + } + + /** + * 关闭资源 + * @return int 退出码 + */ + private function close() +{ + $this->processPipes->close(); + if (is_resource($this->process)) { + $exitcode = proc_close($this->process); + } else { + $exitcode = -1; + } + + $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1); + $this->status = self::STATUS_TERMINATED; + + if (-1 === $this->exitcode && null !== $this->fallbackExitcode) { + $this->exitcode = $this->fallbackExitcode; + } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] + && 0 < $this->processInformation['termsig'] + ) { + $this->exitcode = 128 + $this->processInformation['termsig']; + } + + return $this->exitcode; + } + + /** + * 重置数据 + */ + private function resetProcessData() +{ + $this->starttime = null; + $this->callback = null; + $this->exitcode = null; + $this->fallbackExitcode = null; + $this->processInformation = null; + $this->stdout = null; + $this->stderr = null; + $this->process = null; + $this->latestSignal = null; + $this->status = self::STATUS_READY; + $this->incrementalOutputOffset = 0; + $this->incrementalErrorOutputOffset = 0; + } + + /** + * 将一个 POSIX 信号发送到进程中。 + * @param int $signal + * @param bool $throwException + * @return bool + */ + private function doSignal($signal, $throwException) +{ + if (!$this->isRunning()) { + if ($throwException) { + throw new \LogicException('Can not send signal on a non running process.'); + } + + return false; + } + + if ($this->isSigchildEnabled()) { + if ($throwException) { + throw new \RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + } + + return false; + } + + if (true !== @proc_terminate($this->process, $signal)) { + if ($throwException) { + throw new \RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); + } + + return false; + } + + $this->latestSignal = $signal; + + return true; + } + + /** + * 确保进程已经开启 + * @param string $functionName + */ + private function requireProcessIsStarted($functionName) +{ + if (!$this->isStarted()) { + throw new \LogicException(sprintf('Process must be started before calling %s.', $functionName)); + } + } + + /** + * 确保进程已经终止 + * @param string $functionName + */ + private function requireProcessIsTerminated($functionName) +{ + if (!$this->isTerminated()) { + throw new \LogicException(sprintf('Process must be terminated before calling %s.', $functionName)); + } + } +} diff --git a/source/thinkphp/library/think/Request.php b/source/thinkphp/library/think/Request.php new file mode 100644 index 0000000..5997a76 --- /dev/null +++ b/source/thinkphp/library/think/Request.php @@ -0,0 +1,1690 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Request +{ + /** + * @var object 对象实例 + */ + protected static $instance; + + protected $method; + /** + * @var string 域名(含协议和端口) + */ + protected $domain; + + /** + * @var string URL地址 + */ + protected $url; + + /** + * @var string 基础URL + */ + protected $baseUrl; + + /** + * @var string 当前执行的文件 + */ + protected $baseFile; + + /** + * @var string 访问的ROOT地址 + */ + protected $root; + + /** + * @var string pathinfo + */ + protected $pathinfo; + + /** + * @var string pathinfo(不含后缀) + */ + protected $path; + + /** + * @var array 当前路由信息 + */ + protected $routeInfo = []; + + /** + * @var array 环境变量 + */ + protected $env; + + /** + * @var array 当前调度信息 + */ + protected $dispatch = []; + protected $module; + protected $controller; + protected $action; + // 当前语言集 + protected $langset; + + /** + * @var array 请求参数 + */ + protected $param = []; + protected $get = []; + protected $post = []; + protected $request = []; + protected $route = []; + protected $put; + protected $session = []; + protected $file = []; + protected $cookie = []; + protected $server = []; + protected $header = []; + + /** + * @var array 资源类型 + */ + protected $mimeType = [ + 'xml' => 'application/xml,text/xml,application/x-xml', + 'json' => 'application/json,text/x-json,application/jsonrequest,text/json', + 'js' => 'text/javascript,application/javascript,application/x-javascript', + 'css' => 'text/css', + 'rss' => 'application/rss+xml', + 'yaml' => 'application/x-yaml,text/yaml', + 'atom' => 'application/atom+xml', + 'pdf' => 'application/pdf', + 'text' => 'text/plain', + 'image' => 'image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*', + 'csv' => 'text/csv', + 'html' => 'text/html,application/xhtml+xml,*/*', + ]; + + protected $content; + + // 全局过滤规则 + protected $filter; + // Hook扩展方法 + protected static $hook = []; + // 绑定的属性 + protected $bind = []; + // php://input + protected $input; + // 请求缓存 + protected $cache; + // 缓存是否检查 + protected $isCheckCache; + /** + * 是否合并Param + * @var bool + */ + protected $mergeParam = false; + + /** + * 构造函数 + * @access protected + * @param array $options 参数 + */ + protected function __construct($options = []) + { + foreach ($options as $name => $item) { + if (property_exists($this, $name)) { + $this->$name = $item; + } + } + if (is_null($this->filter)) { + $this->filter = Config::get('default_filter'); + } + + // 保存 php://input + $this->input = file_get_contents('php://input'); + } + + public function __call($method, $args) + { + if (array_key_exists($method, self::$hook)) { + array_unshift($args, $this); + return call_user_func_array(self::$hook[$method], $args); + } else { + throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + } + } + + /** + * Hook 方法注入 + * @access public + * @param string|array $method 方法名 + * @param mixed $callback callable + * @return void + */ + public static function hook($method, $callback = null) + { + if (is_array($method)) { + self::$hook = array_merge(self::$hook, $method); + } else { + self::$hook[$method] = $callback; + } + } + + /** + * 初始化 + * @access public + * @param array $options 参数 + * @return \think\Request + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + return self::$instance; + } + + /** + * 销毁当前请求对象 + * @access public + * @return void + */ + public static function destroy() + { + if (!is_null(self::$instance)) { + self::$instance = null; + } + } + + /** + * 创建一个URL请求 + * @access public + * @param string $uri URL地址 + * @param string $method 请求类型 + * @param array $params 请求参数 + * @param array $cookie + * @param array $files + * @param array $server + * @param string $content + * @return \think\Request + */ + public static function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null) + { + $server['PATH_INFO'] = ''; + $server['REQUEST_METHOD'] = strtoupper($method); + $info = parse_url($uri); + if (isset($info['host'])) { + $server['SERVER_NAME'] = $info['host']; + $server['HTTP_HOST'] = $info['host']; + } + if (isset($info['scheme'])) { + if ('https' === $info['scheme']) { + $server['HTTPS'] = 'on'; + $server['SERVER_PORT'] = 443; + } else { + unset($server['HTTPS']); + $server['SERVER_PORT'] = 80; + } + } + if (isset($info['port'])) { + $server['SERVER_PORT'] = $info['port']; + $server['HTTP_HOST'] = $server['HTTP_HOST'] . ':' . $info['port']; + } + if (isset($info['user'])) { + $server['PHP_AUTH_USER'] = $info['user']; + } + if (isset($info['pass'])) { + $server['PHP_AUTH_PW'] = $info['pass']; + } + if (!isset($info['path'])) { + $info['path'] = '/'; + } + $options = []; + $options[strtolower($method)] = $params; + $queryString = ''; + if (isset($info['query'])) { + parse_str(html_entity_decode($info['query']), $query); + if (!empty($params)) { + $params = array_replace($query, $params); + $queryString = http_build_query($params, '', '&'); + } else { + $params = $query; + $queryString = $info['query']; + } + } elseif (!empty($params)) { + $queryString = http_build_query($params, '', '&'); + } + if ($queryString) { + parse_str($queryString, $get); + $options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get; + } + + $server['REQUEST_URI'] = $info['path'] . ('' !== $queryString ? '?' . $queryString : ''); + $server['QUERY_STRING'] = $queryString; + $options['cookie'] = $cookie; + $options['param'] = $params; + $options['file'] = $files; + $options['server'] = $server; + $options['url'] = $server['REQUEST_URI']; + $options['baseUrl'] = $info['path']; + $options['pathinfo'] = '/' == $info['path'] ? '/' : ltrim($info['path'], '/'); + $options['method'] = $server['REQUEST_METHOD']; + $options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : ''; + $options['content'] = $content; + self::$instance = new self($options); + return self::$instance; + } + + /** + * 设置或获取当前包含协议的域名 + * @access public + * @param string $domain 域名 + * @return string + */ + public function domain($domain = null) + { + if (!is_null($domain)) { + $this->domain = $domain; + return $this; + } elseif (!$this->domain) { + $this->domain = $this->scheme() . '://' . $this->host(); + } + return $this->domain; + } + + /** + * 设置或获取当前完整URL 包括QUERY_STRING + * @access public + * @param string|true $url URL地址 true 带域名获取 + * @return string + */ + public function url($url = null) + { + if (!is_null($url) && true !== $url) { + $this->url = $url; + return $this; + } elseif (!$this->url) { + if (IS_CLI) { + $this->url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; + } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) { + $this->url = $_SERVER['HTTP_X_REWRITE_URL']; + } elseif (isset($_SERVER['REQUEST_URI'])) { + $this->url = $_SERVER['REQUEST_URI']; + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { + $this->url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''); + } else { + $this->url = ''; + } + } + return true === $url ? $this->domain() . $this->url : $this->url; + } + + /** + * 设置或获取当前URL 不含QUERY_STRING + * @access public + * @param string $url URL地址 + * @return string + */ + public function baseUrl($url = null) + { + if (!is_null($url) && true !== $url) { + $this->baseUrl = $url; + return $this; + } elseif (!$this->baseUrl) { + $str = $this->url(); + $this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str; + } + return true === $url ? $this->domain() . $this->baseUrl : $this->baseUrl; + } + + /** + * 设置或获取当前执行的文件 SCRIPT_NAME + * @access public + * @param string $file 当前执行的文件 + * @return string + */ + public function baseFile($file = null) + { + if (!is_null($file) && true !== $file) { + $this->baseFile = $file; + return $this; + } elseif (!$this->baseFile) { + $url = ''; + if (!IS_CLI) { + $script_name = basename($_SERVER['SCRIPT_FILENAME']); + if (basename($_SERVER['SCRIPT_NAME']) === $script_name) { + $url = $_SERVER['SCRIPT_NAME']; + } elseif (basename($_SERVER['PHP_SELF']) === $script_name) { + $url = $_SERVER['PHP_SELF']; + } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $script_name) { + $url = $_SERVER['ORIG_SCRIPT_NAME']; + } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $script_name)) !== false) { + $url = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $script_name; + } elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0) { + $url = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME'])); + } + } + $this->baseFile = $url; + } + return true === $file ? $this->domain() . $this->baseFile : $this->baseFile; + } + + /** + * 设置或获取URL访问根地址 + * @access public + * @param string $url URL地址 + * @return string + */ + public function root($url = null) + { + if (!is_null($url) && true !== $url) { + $this->root = $url; + return $this; + } elseif (!$this->root) { + $file = $this->baseFile(); + if ($file && 0 !== strpos($this->url(), $file)) { + $file = str_replace('\\', '/', dirname($file)); + } + $this->root = rtrim($file, '/'); + } + return true === $url ? $this->domain() . $this->root : $this->root; + } + + /** + * 获取当前请求URL的pathinfo信息(含URL后缀) + * @access public + * @return string + */ + public function pathinfo() + { + if (is_null($this->pathinfo)) { + if (isset($_GET[Config::get('var_pathinfo')])) { + // 判断URL里面是否有兼容模式参数 + $_SERVER['PATH_INFO'] = $_GET[Config::get('var_pathinfo')]; + unset($_GET[Config::get('var_pathinfo')]); + } elseif (IS_CLI) { + // CLI模式下 index.php module/controller/action/params/... + $_SERVER['PATH_INFO'] = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : ''; + } + + // 分析PATHINFO信息 + if (!isset($_SERVER['PATH_INFO'])) { + foreach (Config::get('pathinfo_fetch') as $type) { + if (!empty($_SERVER[$type])) { + $_SERVER['PATH_INFO'] = (0 === strpos($_SERVER[$type], $_SERVER['SCRIPT_NAME'])) ? + substr($_SERVER[$type], strlen($_SERVER['SCRIPT_NAME'])) : $_SERVER[$type]; + break; + } + } + } + $this->pathinfo = empty($_SERVER['PATH_INFO']) ? '/' : ltrim($_SERVER['PATH_INFO'], '/'); + } + return $this->pathinfo; + } + + /** + * 获取当前请求URL的pathinfo信息(不含URL后缀) + * @access public + * @return string + */ + public function path() + { + if (is_null($this->path)) { + $suffix = Config::get('url_html_suffix'); + $pathinfo = $this->pathinfo(); + if (false === $suffix) { + // 禁止伪静态访问 + $this->path = $pathinfo; + } elseif ($suffix) { + // 去除正常的URL后缀 + $this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo); + } else { + // 允许任何后缀访问 + $this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo); + } + } + return $this->path; + } + + /** + * 当前URL的访问后缀 + * @access public + * @return string + */ + public function ext() + { + return pathinfo($this->pathinfo(), PATHINFO_EXTENSION); + } + + /** + * 获取当前请求的时间 + * @access public + * @param bool $float 是否使用浮点类型 + * @return integer|float + */ + public function time($float = false) + { + return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME']; + } + + /** + * 当前请求的资源类型 + * @access public + * @return false|string + */ + public function type() + { + $accept = $this->server('HTTP_ACCEPT'); + if (empty($accept)) { + return false; + } + + foreach ($this->mimeType as $key => $val) { + $array = explode(',', $val); + foreach ($array as $k => $v) { + if (stristr($accept, $v)) { + return $key; + } + } + } + return false; + } + + /** + * 设置资源类型 + * @access public + * @param string|array $type 资源类型名 + * @param string $val 资源类型 + * @return void + */ + public function mimeType($type, $val = '') + { + if (is_array($type)) { + $this->mimeType = array_merge($this->mimeType, $type); + } else { + $this->mimeType[$type] = $val; + } + } + + /** + * 当前的请求类型 + * @access public + * @param bool $method true 获取原始请求类型 + * @return string + */ + public function method($method = false) + { + if (true === $method) { + // 获取原始请求类型 + return $this->server('REQUEST_METHOD') ?: 'GET'; + } elseif (!$this->method) { + if (isset($_POST[Config::get('var_method')])) { + $method = strtoupper($_POST[Config::get('var_method')]); + if (in_array($method, ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'])) { + $this->method = $method; + $this->{$this->method}($_POST); + } else { + $this->method = 'POST'; + } + unset($_POST[Config::get('var_method')]); + } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { + $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); + } else { + $this->method = $this->server('REQUEST_METHOD') ?: 'GET'; + } + } + return $this->method; + } + + /** + * 是否为GET请求 + * @access public + * @return bool + */ + public function isGet() + { + return $this->method() == 'GET'; + } + + /** + * 是否为POST请求 + * @access public + * @return bool + */ + public function isPost() + { + return $this->method() == 'POST'; + } + + /** + * 是否为PUT请求 + * @access public + * @return bool + */ + public function isPut() + { + return $this->method() == 'PUT'; + } + + /** + * 是否为DELTE请求 + * @access public + * @return bool + */ + public function isDelete() + { + return $this->method() == 'DELETE'; + } + + /** + * 是否为HEAD请求 + * @access public + * @return bool + */ + public function isHead() + { + return $this->method() == 'HEAD'; + } + + /** + * 是否为PATCH请求 + * @access public + * @return bool + */ + public function isPatch() + { + return $this->method() == 'PATCH'; + } + + /** + * 是否为OPTIONS请求 + * @access public + * @return bool + */ + public function isOptions() + { + return $this->method() == 'OPTIONS'; + } + + /** + * 是否为cli + * @access public + * @return bool + */ + public function isCli() + { + return PHP_SAPI == 'cli'; + } + + /** + * 是否为cgi + * @access public + * @return bool + */ + public function isCgi() + { + return strpos(PHP_SAPI, 'cgi') === 0; + } + + /** + * 获取当前请求的参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function param($name = '', $default = null, $filter = '') + { + if (empty($this->mergeParam)) { + $method = $this->method(true); + // 自动获取请求变量 + switch ($method) { + case 'POST': + $vars = $this->post(false); + break; + case 'PUT': + case 'DELETE': + case 'PATCH': + $vars = $this->put(false); + break; + default: + $vars = []; + } + // 当前请求参数和URL地址中的参数合并 + $this->param = array_merge($this->param, $this->get(false), $vars, $this->route(false)); + $this->mergeParam = true; + } + if (true === $name) { + // 获取包含文件上传信息的数组 + $file = $this->file(); + $data = is_array($file) ? array_merge($this->param, $file) : $this->param; + return $this->input($data, '', $default, $filter); + } + return $this->input($this->param, $name, $default, $filter); + } + + /** + * 设置获取路由参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function route($name = '', $default = null, $filter = '') + { + if (is_array($name)) { + $this->param = []; + $this->mergeParam = false; + return $this->route = array_merge($this->route, $name); + } + return $this->input($this->route, $name, $default, $filter); + } + + /** + * 设置获取GET参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function get($name = '', $default = null, $filter = '') + { + if (empty($this->get)) { + $this->get = $_GET; + } + if (is_array($name)) { + $this->param = []; + $this->mergeParam = false; + return $this->get = array_merge($this->get, $name); + } + return $this->input($this->get, $name, $default, $filter); + } + + /** + * 设置获取POST参数 + * @access public + * @param string $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function post($name = '', $default = null, $filter = '') + { + if (empty($this->post)) { + $content = $this->input; + if (empty($_POST) && false !== strpos($this->contentType(), 'application/json')) { + $this->post = (array) json_decode($content, true); + } else { + $this->post = $_POST; + } + } + if (is_array($name)) { + $this->param = []; + $this->mergeParam = false; + return $this->post = array_merge($this->post, $name); + } + return $this->input($this->post, $name, $default, $filter); + } + + /** + * 设置获取PUT参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function put($name = '', $default = null, $filter = '') + { + if (is_null($this->put)) { + $content = $this->input; + if (false !== strpos($this->contentType(), 'application/json')) { + $this->put = (array) json_decode($content, true); + } else { + parse_str($content, $this->put); + } + } + if (is_array($name)) { + $this->param = []; + $this->mergeParam = false; + return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name); + } + + return $this->input($this->put, $name, $default, $filter); + } + + /** + * 设置获取DELETE参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function delete($name = '', $default = null, $filter = '') + { + return $this->put($name, $default, $filter); + } + + /** + * 设置获取PATCH参数 + * @access public + * @param string|array $name 变量名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function patch($name = '', $default = null, $filter = '') + { + return $this->put($name, $default, $filter); + } + + /** + * 获取request变量 + * @param string $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function request($name = '', $default = null, $filter = '') + { + if (empty($this->request)) { + $this->request = $_REQUEST; + } + if (is_array($name)) { + $this->param = []; + $this->mergeParam = false; + return $this->request = array_merge($this->request, $name); + } + return $this->input($this->request, $name, $default, $filter); + } + + /** + * 获取session数据 + * @access public + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function session($name = '', $default = null, $filter = '') + { + if (empty($this->session)) { + $this->session = Session::get(); + } + if (is_array($name)) { + return $this->session = array_merge($this->session, $name); + } + return $this->input($this->session, $name, $default, $filter); + } + + /** + * 获取cookie参数 + * @access public + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function cookie($name = '', $default = null, $filter = '') + { + if (empty($this->cookie)) { + $this->cookie = Cookie::get(); + } + if (is_array($name)) { + return $this->cookie = array_merge($this->cookie, $name); + } elseif (!empty($name)) { + $data = Cookie::has($name) ? Cookie::get($name) : $default; + } else { + $data = $this->cookie; + } + + // 解析过滤器 + $filter = $this->getFilter($filter, $default); + + if (is_array($data)) { + array_walk_recursive($data, [$this, 'filterValue'], $filter); + reset($data); + } else { + $this->filterValue($data, $name, $filter); + } + return $data; + } + + /** + * 获取server参数 + * @access public + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function server($name = '', $default = null, $filter = '') + { + if (empty($this->server)) { + $this->server = $_SERVER; + } + if (is_array($name)) { + return $this->server = array_merge($this->server, $name); + } + return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter); + } + + /** + * 获取上传的文件信息 + * @access public + * @param string|array $name 名称 + * @return null|array|\think\File + */ + public function file($name = '') + { + if (empty($this->file)) { + $this->file = isset($_FILES) ? $_FILES : []; + } + if (is_array($name)) { + return $this->file = array_merge($this->file, $name); + } + $files = $this->file; + if (!empty($files)) { + // 处理上传文件 + $array = []; + foreach ($files as $key => $file) { + if (is_array($file['name'])) { + $item = []; + $keys = array_keys($file); + $count = count($file['name']); + for ($i = 0; $i < $count; $i++) { + if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) { + continue; + } + $temp['key'] = $key; + foreach ($keys as $_key) { + $temp[$_key] = $file[$_key][$i]; + } + $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp); + } + $array[$key] = $item; + } else { + if ($file instanceof File) { + $array[$key] = $file; + } else { + if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) { + continue; + } + $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file); + } + } + } + if (strpos($name, '.')) { + list($name, $sub) = explode('.', $name); + } + if ('' === $name) { + // 获取全部文件 + return $array; + } elseif (isset($sub) && isset($array[$name][$sub])) { + return $array[$name][$sub]; + } elseif (isset($array[$name])) { + return $array[$name]; + } + } + return; + } + + /** + * 获取环境变量 + * @param string|array $name 数据名称 + * @param string $default 默认值 + * @param string|array $filter 过滤方法 + * @return mixed + */ + public function env($name = '', $default = null, $filter = '') + { + if (empty($this->env)) { + $this->env = $_ENV; + } + if (is_array($name)) { + return $this->env = array_merge($this->env, $name); + } + return $this->input($this->env, false === $name ? false : strtoupper($name), $default, $filter); + } + + /** + * 设置或者获取当前的Header + * @access public + * @param string|array $name header名称 + * @param string $default 默认值 + * @return string + */ + public function header($name = '', $default = null) + { + if (empty($this->header)) { + $header = []; + if (function_exists('apache_request_headers') && $result = apache_request_headers()) { + $header = $result; + } else { + $server = $this->server ?: $_SERVER; + foreach ($server as $key => $val) { + if (0 === strpos($key, 'HTTP_')) { + $key = str_replace('_', '-', strtolower(substr($key, 5))); + $header[$key] = $val; + } + } + if (isset($server['CONTENT_TYPE'])) { + $header['content-type'] = $server['CONTENT_TYPE']; + } + if (isset($server['CONTENT_LENGTH'])) { + $header['content-length'] = $server['CONTENT_LENGTH']; + } + } + $this->header = array_change_key_case($header); + } + if (is_array($name)) { + return $this->header = array_merge($this->header, $name); + } + if ('' === $name) { + return $this->header; + } + $name = str_replace('_', '-', strtolower($name)); + return isset($this->header[$name]) ? $this->header[$name] : $default; + } + + /** + * 获取变量 支持过滤和默认值 + * @param array $data 数据源 + * @param string|false $name 字段名 + * @param mixed $default 默认值 + * @param string|array $filter 过滤函数 + * @return mixed + */ + public function input($data = [], $name = '', $default = null, $filter = '') + { + if (false === $name) { + // 获取原始数据 + return $data; + } + $name = (string) $name; + if ('' != $name) { + // 解析name + if (strpos($name, '/')) { + list($name, $type) = explode('/', $name); + } else { + $type = 's'; + } + // 按.拆分成多维数组进行判断 + foreach (explode('.', $name) as $val) { + if (isset($data[$val])) { + $data = $data[$val]; + } else { + // 无输入数据,返回默认值 + return $default; + } + } + if (is_object($data)) { + return $data; + } + } + + // 解析过滤器 + $filter = $this->getFilter($filter, $default); + + if (is_array($data)) { + array_walk_recursive($data, [$this, 'filterValue'], $filter); + reset($data); + } else { + $this->filterValue($data, $name, $filter); + } + + if (isset($type) && $data !== $default) { + // 强制类型转换 + $this->typeCast($data, $type); + } + return $data; + } + + /** + * 设置或获取当前的过滤规则 + * @param mixed $filter 过滤规则 + * @return mixed + */ + public function filter($filter = null) + { + if (is_null($filter)) { + return $this->filter; + } else { + $this->filter = $filter; + } + } + + protected function getFilter($filter, $default) + { + if (is_null($filter)) { + $filter = []; + } else { + $filter = $filter ?: $this->filter; + if (is_string($filter) && false === strpos($filter, '/')) { + $filter = explode(',', $filter); + } else { + $filter = (array) $filter; + } + } + + $filter[] = $default; + return $filter; + } + + /** + * 递归过滤给定的值 + * @param mixed $value 键值 + * @param mixed $key 键名 + * @param array $filters 过滤方法+默认值 + * @return mixed + */ + private function filterValue(&$value, $key, $filters) + { + $default = array_pop($filters); + foreach ($filters as $filter) { + if (is_callable($filter)) { + // 调用函数或者方法过滤 + $value = call_user_func($filter, $value); + } elseif (is_scalar($value)) { + if (false !== strpos($filter, '/')) { + // 正则过滤 + if (!preg_match($filter, $value)) { + // 匹配不成功返回默认值 + $value = $default; + break; + } + } elseif (!empty($filter)) { + // filter函数不存在时, 则使用filter_var进行过滤 + // filter为非整形值时, 调用filter_id取得过滤id + $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter)); + if (false === $value) { + $value = $default; + break; + } + } + } + } + return $this->filterExp($value); + } + + /** + * 过滤表单中的表达式 + * @param string $value + * @return void + */ + public function filterExp(&$value) + { + // 过滤查询特殊字符 + if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOT EXISTS|NOTEXISTS|EXISTS|NOT NULL|NOTNULL|NULL|BETWEEN TIME|NOT BETWEEN TIME|NOTBETWEEN TIME|NOTIN|NOT IN|IN)$/i', $value)) { + $value .= ' '; + } + // TODO 其他安全过滤 + } + + /** + * 强制类型转换 + * @param string $data + * @param string $type + * @return mixed + */ + private function typeCast(&$data, $type) + { + switch (strtolower($type)) { + // 数组 + case 'a': + $data = (array) $data; + break; + // 数字 + case 'd': + $data = (int) $data; + break; + // 浮点 + case 'f': + $data = (float) $data; + break; + // 布尔 + case 'b': + $data = (boolean) $data; + break; + // 字符串 + case 's': + default: + if (is_scalar($data)) { + $data = (string) $data; + } else { + throw new \InvalidArgumentException('variable type error:' . gettype($data)); + } + } + } + + /** + * 是否存在某个请求参数 + * @access public + * @param string $name 变量名 + * @param string $type 变量类型 + * @param bool $checkEmpty 是否检测空值 + * @return mixed + */ + public function has($name, $type = 'param', $checkEmpty = false) + { + if (empty($this->$type)) { + $param = $this->$type(); + } else { + $param = $this->$type; + } + // 按.拆分成多维数组进行判断 + foreach (explode('.', $name) as $val) { + if (isset($param[$val])) { + $param = $param[$val]; + } else { + return false; + } + } + return ($checkEmpty && '' === $param) ? false : true; + } + + /** + * 获取指定的参数 + * @access public + * @param string|array $name 变量名 + * @param string $type 变量类型 + * @return mixed + */ + public function only($name, $type = 'param') + { + $param = $this->$type(); + if (is_string($name)) { + $name = explode(',', $name); + } + $item = []; + foreach ($name as $key) { + if (isset($param[$key])) { + $item[$key] = $param[$key]; + } + } + return $item; + } + + /** + * 排除指定参数获取 + * @access public + * @param string|array $name 变量名 + * @param string $type 变量类型 + * @return mixed + */ + public function except($name, $type = 'param') + { + $param = $this->$type(); + if (is_string($name)) { + $name = explode(',', $name); + } + foreach ($name as $key) { + if (isset($param[$key])) { + unset($param[$key]); + } + } + return $param; + } + + /** + * 当前是否ssl + * @access public + * @return bool + */ + public function isSsl() + { + $server = array_merge($_SERVER, $this->server); + if (isset($server['HTTPS']) && ('1' == $server['HTTPS'] || 'on' == strtolower($server['HTTPS']))) { + return true; + } elseif (isset($server['REQUEST_SCHEME']) && 'https' == $server['REQUEST_SCHEME']) { + return true; + } elseif (isset($server['SERVER_PORT']) && ('443' == $server['SERVER_PORT'])) { + return true; + } elseif (isset($server['HTTP_X_FORWARDED_PROTO']) && 'https' == $server['HTTP_X_FORWARDED_PROTO']) { + return true; + } elseif (Config::get('https_agent_name') && isset($server[Config::get('https_agent_name')])) { + return true; + } + return false; + } + + /** + * 当前是否Ajax请求 + * @access public + * @param bool $ajax true 获取原始ajax请求 + * @return bool + */ + public function isAjax($ajax = false) + { + $value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower'); + $result = ('xmlhttprequest' == $value) ? true : false; + if (true === $ajax) { + return $result; + } else { + $result = $this->param(Config::get('var_ajax')) ? true : $result; + $this->mergeParam = false; + return $result; + } + } + + /** + * 当前是否Pjax请求 + * @access public + * @param bool $pjax true 获取原始pjax请求 + * @return bool + */ + public function isPjax($pjax = false) + { + $result = !is_null($this->server('HTTP_X_PJAX')) ? true : false; + if (true === $pjax) { + return $result; + } else { + $result = $this->param(Config::get('var_pjax')) ? true : $result; + $this->mergeParam = false; + return $result; + } + } + + /** + * 获取客户端IP地址 + * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 + * @param boolean $adv 是否进行高级模式获取(有可能被伪装) + * @return mixed + */ + public function ip($type = 0, $adv = true) + { + $type = $type ? 1 : 0; + static $ip = null; + if (null !== $ip) { + return $ip[$type]; + } + + $httpAgentIp = Config::get('http_agent_ip'); + + if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) { + $ip = $_SERVER[$httpAgentIp]; + } elseif ($adv) { + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + $pos = array_search('unknown', $arr); + if (false !== $pos) { + unset($arr[$pos]); + } + $ip = trim(current($arr)); + } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) { + $ip = $_SERVER['HTTP_CLIENT_IP']; + } elseif (isset($_SERVER['REMOTE_ADDR'])) { + $ip = $_SERVER['REMOTE_ADDR']; + } + } elseif (isset($_SERVER['REMOTE_ADDR'])) { + $ip = $_SERVER['REMOTE_ADDR']; + } + // IP地址合法验证 + $long = sprintf("%u", ip2long($ip)); + $ip = $long ? [$ip, $long] : ['0.0.0.0', 0]; + return $ip[$type]; + } + + /** + * 检测是否使用手机访问 + * @access public + * @return bool + */ + public function isMobile() + { + if (isset($_SERVER['HTTP_VIA']) && stristr($_SERVER['HTTP_VIA'], "wap")) { + return true; + } elseif (isset($_SERVER['HTTP_ACCEPT']) && strpos(strtoupper($_SERVER['HTTP_ACCEPT']), "VND.WAP.WML")) { + return true; + } elseif (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) { + return true; + } elseif (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } else { + return false; + } + } + + /** + * 当前URL地址中的scheme参数 + * @access public + * @return string + */ + public function scheme() + { + return $this->isSsl() ? 'https' : 'http'; + } + + /** + * 当前请求URL地址中的query参数 + * @access public + * @return string + */ + public function query() + { + return $this->server('QUERY_STRING'); + } + + /** + * 当前请求的host + * @access public + * @param bool $strict true 仅仅获取HOST + * @return string + */ + public function host($strict = false) + { + if (isset($_SERVER['HTTP_X_REAL_HOST'])) { + $host = $_SERVER['HTTP_X_REAL_HOST']; + } else { + $host = $this->server('HTTP_HOST'); + } + + return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host; + } + + /** + * 当前请求URL地址中的port参数 + * @access public + * @return integer + */ + public function port() + { + return $this->server('SERVER_PORT'); + } + + /** + * 当前请求 SERVER_PROTOCOL + * @access public + * @return integer + */ + public function protocol() + { + return $this->server('SERVER_PROTOCOL'); + } + + /** + * 当前请求 REMOTE_PORT + * @access public + * @return integer + */ + public function remotePort() + { + return $this->server('REMOTE_PORT'); + } + + /** + * 当前请求 HTTP_CONTENT_TYPE + * @access public + * @return string + */ + public function contentType() + { + $contentType = $this->server('CONTENT_TYPE'); + if ($contentType) { + if (strpos($contentType, ';')) { + list($type) = explode(';', $contentType); + } else { + $type = $contentType; + } + return trim($type); + } + return ''; + } + + /** + * 获取当前请求的路由信息 + * @access public + * @param array $route 路由名称 + * @return array + */ + public function routeInfo($route = []) + { + if (!empty($route)) { + $this->routeInfo = $route; + } else { + return $this->routeInfo; + } + } + + /** + * 设置或者获取当前请求的调度信息 + * @access public + * @param array $dispatch 调度信息 + * @return array + */ + public function dispatch($dispatch = null) + { + if (!is_null($dispatch)) { + $this->dispatch = $dispatch; + } + return $this->dispatch; + } + + /** + * 设置或者获取当前的模块名 + * @access public + * @param string $module 模块名 + * @return string|Request + */ + public function module($module = null) + { + if (!is_null($module)) { + $this->module = $module; + return $this; + } else { + return $this->module ?: ''; + } + } + + /** + * 设置或者获取当前的控制器名 + * @access public + * @param string $controller 控制器名 + * @return string|Request + */ + public function controller($controller = null) + { + if (!is_null($controller)) { + $this->controller = $controller; + return $this; + } else { + return $this->controller ?: ''; + } + } + + /** + * 设置或者获取当前的操作名 + * @access public + * @param string $action 操作名 + * @return string|Request + */ + public function action($action = null) + { + if (!is_null($action) && !is_bool($action)) { + $this->action = $action; + return $this; + } else { + $name = $this->action ?: ''; + return true === $action ? $name : strtolower($name); + } + } + + /** + * 设置或者获取当前的语言 + * @access public + * @param string $lang 语言名 + * @return string|Request + */ + public function langset($lang = null) + { + if (!is_null($lang)) { + $this->langset = $lang; + return $this; + } else { + return $this->langset ?: ''; + } + } + + /** + * 设置或者获取当前请求的content + * @access public + * @return string + */ + public function getContent() + { + if (is_null($this->content)) { + $this->content = $this->input; + } + return $this->content; + } + + /** + * 获取当前请求的php://input + * @access public + * @return string + */ + public function getInput() + { + return $this->input; + } + + /** + * 生成请求令牌 + * @access public + * @param string $name 令牌名称 + * @param mixed $type 令牌生成方法 + * @return string + */ + public function token($name = '__token__', $type = 'md5') + { + $type = is_callable($type) ? $type : 'md5'; + $token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']); + if ($this->isAjax()) { + header($name . ': ' . $token); + } + Session::set($name, $token); + return $token; + } + + /** + * 设置当前地址的请求缓存 + * @access public + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id + * @param mixed $expire 缓存有效期 + * @param array $except 缓存排除 + * @param string $tag 缓存标签 + * @return void + */ + public function cache($key, $expire = null, $except = [], $tag = null) + { + if (!is_array($except)) { + $tag = $except; + $except = []; + } + + if (false !== $key && $this->isGet() && !$this->isCheckCache) { + // 标记请求缓存检查 + $this->isCheckCache = true; + if (false === $expire) { + // 关闭当前缓存 + return; + } + if ($key instanceof \Closure) { + $key = call_user_func_array($key, [$this]); + } elseif (true === $key) { + foreach ($except as $rule) { + if (0 === stripos($this->url(), $rule)) { + return; + } + } + // 自动缓存功能 + $key = '__URL__'; + } elseif (strpos($key, '|')) { + list($key, $fun) = explode('|', $key); + } + // 特殊规则替换 + if (false !== strpos($key, '__')) { + $key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url(true))], $key); + } + + if (false !== strpos($key, ':')) { + $param = $this->param(); + foreach ($param as $item => $val) { + if (is_string($val) && false !== strpos($key, ':' . $item)) { + $key = str_replace(':' . $item, $val, $key); + } + } + } elseif (strpos($key, ']')) { + if ('[' . $this->ext() . ']' == $key) { + // 缓存某个后缀的请求 + $key = md5($this->url()); + } else { + return; + } + } + if (isset($fun)) { + $key = $fun($key); + } + + if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) { + // 读取缓存 + $response = Response::create()->code(304); + throw new \think\exception\HttpResponseException($response); + } elseif (Cache::has($key)) { + list($content, $header) = Cache::get($key); + $response = Response::create($content)->header($header); + throw new \think\exception\HttpResponseException($response); + } else { + $this->cache = [$key, $expire, $tag]; + } + } + } + + /** + * 读取请求缓存设置 + * @access public + * @return array + */ + public function getCache() + { + return $this->cache; + } + + /** + * 设置当前请求绑定的对象实例 + * @access public + * @param string|array $name 绑定的对象标识 + * @param mixed $obj 绑定的对象实例 + * @return mixed + */ + public function bind($name, $obj = null) + { + if (is_array($name)) { + $this->bind = array_merge($this->bind, $name); + } else { + $this->bind[$name] = $obj; + } + } + + public function __set($name, $value) + { + $this->bind[$name] = $value; + } + + public function __get($name) + { + return isset($this->bind[$name]) ? $this->bind[$name] : null; + } + + public function __isset($name) + { + return isset($this->bind[$name]); + } +} diff --git a/source/thinkphp/library/think/Response.php b/source/thinkphp/library/think/Response.php new file mode 100644 index 0000000..c5c1520 --- /dev/null +++ b/source/thinkphp/library/think/Response.php @@ -0,0 +1,332 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\response\Json as JsonResponse; +use think\response\Jsonp as JsonpResponse; +use think\response\Redirect as RedirectResponse; +use think\response\View as ViewResponse; +use think\response\Xml as XmlResponse; + +class Response +{ + // 原始数据 + protected $data; + + // 当前的contentType + protected $contentType = 'text/html'; + + // 字符集 + protected $charset = 'utf-8'; + + //状态 + protected $code = 200; + + // 输出参数 + protected $options = []; + // header参数 + protected $header = []; + + protected $content = null; + + /** + * 构造函数 + * @access public + * @param mixed $data 输出数据 + * @param int $code + * @param array $header + * @param array $options 输出参数 + */ + public function __construct($data = '', $code = 200, array $header = [], $options = []) + { + $this->data($data); + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->contentType($this->contentType, $this->charset); + $this->header = array_merge($this->header, $header); + $this->code = $code; + } + + /** + * 创建Response对象 + * @access public + * @param mixed $data 输出数据 + * @param string $type 输出类型 + * @param int $code + * @param array $header + * @param array $options 输出参数 + * @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse + */ + public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) + { + $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type)); + if (class_exists($class)) { + $response = new $class($data, $code, $header, $options); + } else { + $response = new static($data, $code, $header, $options); + } + + return $response; + } + + /** + * 发送数据到客户端 + * @access public + * @return mixed + * @throws \InvalidArgumentException + */ + public function send() + { + // 监听response_send + Hook::listen('response_send', $this); + + // 处理输出数据 + $data = $this->getContent(); + + // Trace调试注入 + if (Env::get('app_trace', Config::get('app_trace'))) { + Debug::inject($this, $data); + } + + if (200 == $this->code) { + $cache = Request::instance()->getCache(); + if ($cache) { + $this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate'; + $this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT'; + $this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'; + Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]); + } + } + + if (!headers_sent() && !empty($this->header)) { + // 发送状态码 + http_response_code($this->code); + // 发送头部信息 + foreach ($this->header as $name => $val) { + if (is_null($val)) { + header($name); + } else { + header($name . ':' . $val); + } + } + } + + echo $data; + + if (function_exists('fastcgi_finish_request')) { + // 提高页面响应 + fastcgi_finish_request(); + } + + // 监听response_end + Hook::listen('response_end', $this); + + // 清空当次请求有效的数据 + if (!($this instanceof RedirectResponse)) { + Session::flush(); + } + } + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + */ + protected function output($data) + { + return $data; + } + + /** + * 输出的参数 + * @access public + * @param mixed $options 输出参数 + * @return $this + */ + public function options($options = []) + { + $this->options = array_merge($this->options, $options); + return $this; + } + + /** + * 输出数据设置 + * @access public + * @param mixed $data 输出数据 + * @return $this + */ + public function data($data) + { + $this->data = $data; + return $this; + } + + /** + * 设置响应头 + * @access public + * @param string|array $name 参数名 + * @param string $value 参数值 + * @return $this + */ + public function header($name, $value = null) + { + if (is_array($name)) { + $this->header = array_merge($this->header, $name); + } else { + $this->header[$name] = $value; + } + return $this; + } + + /** + * 设置页面输出内容 + * @param $content + * @return $this + */ + public function content($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([ + $content, + '__toString', + ]) + ) { + throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content))); + } + + $this->content = (string) $content; + + return $this; + } + + /** + * 发送HTTP状态 + * @param integer $code 状态码 + * @return $this + */ + public function code($code) + { + $this->code = $code; + return $this; + } + + /** + * LastModified + * @param string $time + * @return $this + */ + public function lastModified($time) + { + $this->header['Last-Modified'] = $time; + return $this; + } + + /** + * Expires + * @param string $time + * @return $this + */ + public function expires($time) + { + $this->header['Expires'] = $time; + return $this; + } + + /** + * ETag + * @param string $eTag + * @return $this + */ + public function eTag($eTag) + { + $this->header['ETag'] = $eTag; + return $this; + } + + /** + * 页面缓存控制 + * @param string $cache 状态码 + * @return $this + */ + public function cacheControl($cache) + { + $this->header['Cache-control'] = $cache; + return $this; + } + + /** + * 页面输出类型 + * @param string $contentType 输出类型 + * @param string $charset 输出编码 + * @return $this + */ + public function contentType($contentType, $charset = 'utf-8') + { + $this->header['Content-Type'] = $contentType . '; charset=' . $charset; + return $this; + } + + /** + * 获取头部信息 + * @param string $name 头部名称 + * @return mixed + */ + public function getHeader($name = '') + { + if (!empty($name)) { + return isset($this->header[$name]) ? $this->header[$name] : null; + } else { + return $this->header; + } + } + + /** + * 获取原始数据 + * @return mixed + */ + public function getData() + { + return $this->data; + } + + /** + * 获取输出数据 + * @return mixed + */ + public function getContent() + { + if (null == $this->content) { + $content = $this->output($this->data); + + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([ + $content, + '__toString', + ]) + ) { + throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content))); + } + + $this->content = (string) $content; + } + return $this->content; + } + + /** + * 获取状态码 + * @return integer + */ + public function getCode() + { + return $this->code; + } +} diff --git a/source/thinkphp/library/think/Route.php b/source/thinkphp/library/think/Route.php new file mode 100644 index 0000000..ab53aa2 --- /dev/null +++ b/source/thinkphp/library/think/Route.php @@ -0,0 +1,1645 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\HttpException; + +class Route +{ + // 路由规则 + private static $rules = [ + 'get' => [], + 'post' => [], + 'put' => [], + 'delete' => [], + 'patch' => [], + 'head' => [], + 'options' => [], + '*' => [], + 'alias' => [], + 'domain' => [], + 'pattern' => [], + 'name' => [], + ]; + + // REST路由操作方法定义 + private static $rest = [ + 'index' => ['get', '', 'index'], + 'create' => ['get', '/create', 'create'], + 'edit' => ['get', '/:id/edit', 'edit'], + 'read' => ['get', '/:id', 'read'], + 'save' => ['post', '', 'save'], + 'update' => ['put', '/:id', 'update'], + 'delete' => ['delete', '/:id', 'delete'], + ]; + + // 不同请求类型的方法前缀 + private static $methodPrefix = [ + 'get' => 'get', + 'post' => 'post', + 'put' => 'put', + 'delete' => 'delete', + 'patch' => 'patch', + ]; + + // 子域名 + private static $subDomain = ''; + // 域名绑定 + private static $bind = []; + // 当前分组信息 + private static $group = []; + // 当前子域名绑定 + private static $domainBind; + private static $domainRule; + // 当前域名 + private static $domain; + // 当前路由执行过程中的参数 + private static $option = []; + + /** + * 注册变量规则 + * @access public + * @param string|array $name 变量名 + * @param string $rule 变量规则 + * @return void + */ + public static function pattern($name = null, $rule = '') + { + if (is_array($name)) { + self::$rules['pattern'] = array_merge(self::$rules['pattern'], $name); + } else { + self::$rules['pattern'][$name] = $rule; + } + } + + /** + * 注册子域名部署规则 + * @access public + * @param string|array $domain 子域名 + * @param mixed $rule 路由规则 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function domain($domain, $rule = '', $option = [], $pattern = []) + { + if (is_array($domain)) { + foreach ($domain as $key => $item) { + self::domain($key, $item, $option, $pattern); + } + } elseif ($rule instanceof \Closure) { + // 执行闭包 + self::setDomain($domain); + call_user_func_array($rule, []); + self::setDomain(null); + } elseif (is_array($rule)) { + self::setDomain($domain); + self::group('', function () use ($rule) { + // 动态注册域名的路由规则 + self::registerRules($rule); + }, $option, $pattern); + self::setDomain(null); + } else { + self::$rules['domain'][$domain]['[bind]'] = [$rule, $option, $pattern]; + } + } + + private static function setDomain($domain) + { + self::$domain = $domain; + } + + /** + * 设置路由绑定 + * @access public + * @param mixed $bind 绑定信息 + * @param string $type 绑定类型 默认为module 支持 namespace class controller + * @return mixed + */ + public static function bind($bind, $type = 'module') + { + self::$bind = ['type' => $type, $type => $bind]; + } + + /** + * 设置或者获取路由标识 + * @access public + * @param string|array $name 路由命名标识 数组表示批量设置 + * @param array $value 路由地址及变量信息 + * @return array + */ + public static function name($name = '', $value = null) + { + if (is_array($name)) { + return self::$rules['name'] = $name; + } elseif ('' === $name) { + return self::$rules['name']; + } elseif (!is_null($value)) { + self::$rules['name'][strtolower($name)][] = $value; + } else { + $name = strtolower($name); + return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null; + } + } + + /** + * 读取路由绑定 + * @access public + * @param string $type 绑定类型 + * @return mixed + */ + public static function getBind($type) + { + return isset(self::$bind[$type]) ? self::$bind[$type] : null; + } + + /** + * 导入配置文件的路由规则 + * @access public + * @param array $rule 路由规则 + * @param string $type 请求类型 + * @return void + */ + public static function import(array $rule, $type = '*') + { + // 检查域名部署 + if (isset($rule['__domain__'])) { + self::domain($rule['__domain__']); + unset($rule['__domain__']); + } + + // 检查变量规则 + if (isset($rule['__pattern__'])) { + self::pattern($rule['__pattern__']); + unset($rule['__pattern__']); + } + + // 检查路由别名 + if (isset($rule['__alias__'])) { + self::alias($rule['__alias__']); + unset($rule['__alias__']); + } + + // 检查资源路由 + if (isset($rule['__rest__'])) { + self::resource($rule['__rest__']); + unset($rule['__rest__']); + } + + self::registerRules($rule, strtolower($type)); + } + + // 批量注册路由 + protected static function registerRules($rules, $type = '*') + { + foreach ($rules as $key => $val) { + if (is_numeric($key)) { + $key = array_shift($val); + } + if (empty($val)) { + continue; + } + if (is_string($key) && 0 === strpos($key, '[')) { + $key = substr($key, 1, -1); + self::group($key, $val); + } elseif (is_array($val)) { + self::setRule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []); + } else { + self::setRule($key, $val, $type); + } + } + } + + /** + * 注册路由规则 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param string $type 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) + { + $group = self::getGroup('name'); + + if (!is_null($group)) { + // 路由分组 + $option = array_merge(self::getGroup('option'), $option); + $pattern = array_merge(self::getGroup('pattern'), $pattern); + } + + $type = strtolower($type); + + if (strpos($type, '|')) { + $option['method'] = $type; + $type = '*'; + } + if (is_array($rule) && empty($route)) { + foreach ($rule as $key => $val) { + if (is_numeric($key)) { + $key = array_shift($val); + } + if (is_array($val)) { + $route = $val[0]; + $option1 = array_merge($option, $val[1]); + $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); + } else { + $option1 = null; + $pattern1 = null; + $route = $val; + } + self::setRule($key, $route, $type, !is_null($option1) ? $option1 : $option, !is_null($pattern1) ? $pattern1 : $pattern, $group); + } + } else { + self::setRule($rule, $route, $type, $option, $pattern, $group); + } + + } + + /** + * 设置路由规则 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param string $type 请求类型 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @param string $group 所属分组 + * @return void + */ + protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '') + { + if (is_array($rule)) { + $name = $rule[0]; + $rule = $rule[1]; + } elseif (is_string($route)) { + $name = $route; + } + if (!isset($option['complete_match'])) { + if (Config::get('route_complete_match')) { + $option['complete_match'] = true; + } elseif ('$' == substr($rule, -1, 1)) { + // 是否完整匹配 + $option['complete_match'] = true; + } + } elseif (empty($option['complete_match']) && '$' == substr($rule, -1, 1)) { + // 是否完整匹配 + $option['complete_match'] = true; + } + + if ('$' == substr($rule, -1, 1)) { + $rule = substr($rule, 0, -1); + } + + if ('/' != $rule || $group) { + $rule = trim($rule, '/'); + } + $vars = self::parseVar($rule); + if (isset($name)) { + $key = $group ? $group . ($rule ? '/' . $rule : '') : $rule; + $suffix = isset($option['ext']) ? $option['ext'] : null; + self::name($name, [$key, $vars, self::$domain, $suffix]); + } + if (isset($option['modular'])) { + $route = $option['modular'] . '/' . $route; + } + if ($group) { + if ('*' != $type) { + $option['method'] = $type; + } + if (self::$domain) { + self::$rules['domain'][self::$domain]['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; + } else { + self::$rules['*'][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; + } + } else { + if ('*' != $type && isset(self::$rules['*'][$rule])) { + unset(self::$rules['*'][$rule]); + } + if (self::$domain) { + self::$rules['domain'][self::$domain][$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; + } else { + self::$rules[$type][$rule] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; + } + if ('*' == $type) { + // 注册路由快捷方式 + foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { + if (self::$domain && !isset(self::$rules['domain'][self::$domain][$method][$rule])) { + self::$rules['domain'][self::$domain][$method][$rule] = true; + } elseif (!self::$domain && !isset(self::$rules[$method][$rule])) { + self::$rules[$method][$rule] = true; + } + } + } + } + } + + /** + * 设置当前执行的参数信息 + * @access public + * @param array $options 参数信息 + * @return mixed + */ + protected static function setOption($options = []) + { + self::$option[] = $options; + } + + /** + * 获取当前执行的所有参数信息 + * @access public + * @return array + */ + public static function getOption() + { + return self::$option; + } + + /** + * 获取当前的分组信息 + * @access public + * @param string $type 分组信息名称 name option pattern + * @return mixed + */ + public static function getGroup($type) + { + if (isset(self::$group[$type])) { + return self::$group[$type]; + } else { + return 'name' == $type ? null : []; + } + } + + /** + * 设置当前的路由分组 + * @access public + * @param string $name 分组名称 + * @param array $option 分组路由参数 + * @param array $pattern 分组变量规则 + * @return void + */ + public static function setGroup($name, $option = [], $pattern = []) + { + self::$group['name'] = $name; + self::$group['option'] = $option ?: []; + self::$group['pattern'] = $pattern ?: []; + } + + /** + * 注册路由分组 + * @access public + * @param string|array $name 分组名称或者参数 + * @param array|\Closure $routes 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function group($name, $routes, $option = [], $pattern = []) + { + if (is_array($name)) { + $option = $name; + $name = isset($option['name']) ? $option['name'] : ''; + } + // 分组 + $currentGroup = self::getGroup('name'); + if ($currentGroup) { + $name = $currentGroup . ($name ? '/' . ltrim($name, '/') : ''); + } + if (!empty($name)) { + if ($routes instanceof \Closure) { + $currentOption = self::getGroup('option'); + $currentPattern = self::getGroup('pattern'); + self::setGroup($name, array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); + call_user_func_array($routes, []); + self::setGroup($currentGroup, $currentOption, $currentPattern); + if ($currentGroup != $name) { + self::$rules['*'][$name]['route'] = ''; + self::$rules['*'][$name]['var'] = self::parseVar($name); + self::$rules['*'][$name]['option'] = $option; + self::$rules['*'][$name]['pattern'] = $pattern; + } + } else { + $item = []; + $completeMatch = Config::get('route_complete_match'); + foreach ($routes as $key => $val) { + if (is_numeric($key)) { + $key = array_shift($val); + } + if (is_array($val)) { + $route = $val[0]; + $option1 = array_merge($option, isset($val[1]) ? $val[1] : []); + $pattern1 = array_merge($pattern, isset($val[2]) ? $val[2] : []); + } else { + $route = $val; + } + + $options = isset($option1) ? $option1 : $option; + $patterns = isset($pattern1) ? $pattern1 : $pattern; + if ('$' == substr($key, -1, 1)) { + // 是否完整匹配 + $options['complete_match'] = true; + $key = substr($key, 0, -1); + } elseif ($completeMatch) { + $options['complete_match'] = true; + } + $key = trim($key, '/'); + $vars = self::parseVar($key); + $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns]; + // 设置路由标识 + $suffix = isset($options['ext']) ? $options['ext'] : null; + self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain, $suffix]); + } + self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern]; + } + + foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) { + if (!isset(self::$rules[$method][$name])) { + self::$rules[$method][$name] = true; + } elseif (is_array(self::$rules[$method][$name])) { + self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]); + } + } + + } elseif ($routes instanceof \Closure) { + // 闭包注册 + $currentOption = self::getGroup('option'); + $currentPattern = self::getGroup('pattern'); + self::setGroup('', array_merge($currentOption, $option), array_merge($currentPattern, $pattern)); + call_user_func_array($routes, []); + self::setGroup($currentGroup, $currentOption, $currentPattern); + } else { + // 批量注册路由 + self::rule($routes, '', '*', $option, $pattern); + } + } + + /** + * 注册路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function any($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, '*', $option, $pattern); + } + + /** + * 注册GET路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function get($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, 'GET', $option, $pattern); + } + + /** + * 注册POST路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function post($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, 'POST', $option, $pattern); + } + + /** + * 注册PUT路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function put($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, 'PUT', $option, $pattern); + } + + /** + * 注册DELETE路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function delete($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, 'DELETE', $option, $pattern); + } + + /** + * 注册PATCH路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function patch($rule, $route = '', $option = [], $pattern = []) + { + self::rule($rule, $route, 'PATCH', $option, $pattern); + } + + /** + * 注册资源路由 + * @access public + * @param string|array $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function resource($rule, $route = '', $option = [], $pattern = []) + { + if (is_array($rule)) { + foreach ($rule as $key => $val) { + if (is_array($val)) { + list($val, $option, $pattern) = array_pad($val, 3, []); + } + self::resource($key, $val, $option, $pattern); + } + } else { + if (strpos($rule, '.')) { + // 注册嵌套资源路由 + $array = explode('.', $rule); + $last = array_pop($array); + $item = []; + foreach ($array as $val) { + $item[] = $val . '/:' . (isset($option['var'][$val]) ? $option['var'][$val] : $val . '_id'); + } + $rule = implode('/', $item) . '/' . $last; + } + // 注册资源路由 + foreach (self::$rest as $key => $val) { + if ((isset($option['only']) && !in_array($key, $option['only'])) + || (isset($option['except']) && in_array($key, $option['except']))) { + continue; + } + if (isset($last) && strpos($val[1], ':id') && isset($option['var'][$last])) { + $val[1] = str_replace(':id', ':' . $option['var'][$last], $val[1]); + } elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) { + $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]); + } + $item = ltrim($rule . $val[1], '/'); + $option['rest'] = $key; + self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern); + } + } + } + + /** + * 注册控制器路由 操作方法对应不同的请求后缀 + * @access public + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @param array $pattern 变量规则 + * @return void + */ + public static function controller($rule, $route = '', $option = [], $pattern = []) + { + foreach (self::$methodPrefix as $type => $val) { + self::$type($rule . '/:action', $route . '/' . $val . ':action', $option, $pattern); + } + } + + /** + * 注册别名路由 + * @access public + * @param string|array $rule 路由别名 + * @param string $route 路由地址 + * @param array $option 路由参数 + * @return void + */ + public static function alias($rule = null, $route = '', $option = []) + { + if (is_array($rule)) { + self::$rules['alias'] = array_merge(self::$rules['alias'], $rule); + } else { + self::$rules['alias'][$rule] = $option ? [$route, $option] : $route; + } + } + + /** + * 设置不同请求类型下面的方法前缀 + * @access public + * @param string $method 请求类型 + * @param string $prefix 类型前缀 + * @return void + */ + public static function setMethodPrefix($method, $prefix = '') + { + if (is_array($method)) { + self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method)); + } else { + self::$methodPrefix[strtolower($method)] = $prefix; + } + } + + /** + * rest方法定义和修改 + * @access public + * @param string|array $name 方法名称 + * @param array|bool $resource 资源 + * @return void + */ + public static function rest($name, $resource = []) + { + if (is_array($name)) { + self::$rest = $resource ? $name : array_merge(self::$rest, $name); + } else { + self::$rest[$name] = $resource; + } + } + + /** + * 注册未匹配路由规则后的处理 + * @access public + * @param string $route 路由地址 + * @param string $method 请求类型 + * @param array $option 路由参数 + * @return void + */ + public static function miss($route, $method = '*', $option = []) + { + self::rule('__miss__', $route, $method, $option, []); + } + + /** + * 注册一个自动解析的URL路由 + * @access public + * @param string $route 路由地址 + * @return void + */ + public static function auto($route) + { + self::rule('__auto__', $route, '*', [], []); + } + + /** + * 获取或者批量设置路由定义 + * @access public + * @param mixed $rules 请求类型或者路由定义数组 + * @return array + */ + public static function rules($rules = '') + { + if (is_array($rules)) { + self::$rules = $rules; + } elseif ($rules) { + return true === $rules ? self::$rules : self::$rules[strtolower($rules)]; + } else { + $rules = self::$rules; + unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']); + return $rules; + } + } + + /** + * 检测子域名部署 + * @access public + * @param Request $request Request请求对象 + * @param array $currentRules 当前路由规则 + * @param string $method 请求类型 + * @return void + */ + public static function checkDomain($request, &$currentRules, $method = 'get') + { + // 域名规则 + $rules = self::$rules['domain']; + // 开启子域名部署 支持二级和三级域名 + if (!empty($rules)) { + $host = $request->host(true); + if (isset($rules[$host])) { + // 完整域名或者IP配置 + $item = $rules[$host]; + } else { + $rootDomain = Config::get('url_domain_root'); + if ($rootDomain) { + // 配置域名根 例如 thinkphp.cn 163.com.cn 如果是国家级域名 com.cn net.cn 之类的域名需要配置 + $domain = explode('.', rtrim(stristr($host, $rootDomain, true), '.')); + } else { + $domain = explode('.', $host, -2); + } + // 子域名配置 + if (!empty($domain)) { + // 当前子域名 + $subDomain = implode('.', $domain); + self::$subDomain = $subDomain; + $domain2 = array_pop($domain); + if ($domain) { + // 存在三级域名 + $domain3 = array_pop($domain); + } + if ($subDomain && isset($rules[$subDomain])) { + // 子域名配置 + $item = $rules[$subDomain]; + } elseif (isset($rules['*.' . $domain2]) && !empty($domain3)) { + // 泛三级域名 + $item = $rules['*.' . $domain2]; + $panDomain = $domain3; + } elseif (isset($rules['*']) && !empty($domain2)) { + // 泛二级域名 + if ('www' != $domain2) { + $item = $rules['*']; + $panDomain = $domain2; + } + } + } + } + if (!empty($item)) { + if (isset($panDomain)) { + // 保存当前泛域名 + $request->route(['__domain__' => $panDomain]); + } + if (isset($item['[bind]'])) { + // 解析子域名部署规则 + list($rule, $option, $pattern) = $item['[bind]']; + if (!empty($option['https']) && !$request->isSsl()) { + // https检测 + throw new HttpException(404, 'must use https request:' . $host); + } + + if (strpos($rule, '?')) { + // 传入其它参数 + $array = parse_url($rule); + $result = $array['path']; + parse_str($array['query'], $params); + if (isset($panDomain)) { + $pos = array_search('*', $params); + if (false !== $pos) { + // 泛域名作为参数 + $params[$pos] = $panDomain; + } + } + $_GET = array_merge($_GET, $params); + } else { + $result = $rule; + } + + if (0 === strpos($result, '\\')) { + // 绑定到命名空间 例如 \app\index\behavior + self::$bind = ['type' => 'namespace', 'namespace' => $result]; + } elseif (0 === strpos($result, '@')) { + // 绑定到类 例如 @app\index\controller\User + self::$bind = ['type' => 'class', 'class' => substr($result, 1)]; + } else { + // 绑定到模块/控制器 例如 index/user + self::$bind = ['type' => 'module', 'module' => $result]; + } + self::$domainBind = true; + } else { + self::$domainRule = $item; + $currentRules = isset($item[$method]) ? $item[$method] : $item['*']; + } + } + } + } + + /** + * 检测URL路由 + * @access public + * @param Request $request Request请求对象 + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @param bool $checkDomain 是否检测域名规则 + * @return false|array + */ + public static function check($request, $url, $depr = '/', $checkDomain = false) + { + //检查解析缓存 + if (!App::$debug && Config::get('route_check_cache')) { + $key = self::getCheckCacheKey($request); + if (Cache::has($key)) { + list($rule, $route, $pathinfo, $option, $matches) = Cache::get($key); + return self::parseRule($rule, $route, $pathinfo, $option, $matches, true); + } + } + + // 分隔符替换 确保路由定义使用统一的分隔符 + $url = str_replace($depr, '|', $url); + + if (isset(self::$rules['alias'][$url]) || isset(self::$rules['alias'][strstr($url, '|', true)])) { + // 检测路由别名 + $result = self::checkRouteAlias($request, $url, $depr); + if (false !== $result) { + return $result; + } + } + $method = strtolower($request->method()); + // 获取当前请求类型的路由规则 + $rules = isset(self::$rules[$method]) ? self::$rules[$method] : []; + // 检测域名部署 + if ($checkDomain) { + self::checkDomain($request, $rules, $method); + } + // 检测URL绑定 + $return = self::checkUrlBind($url, $rules, $depr); + if (false !== $return) { + return $return; + } + if ('|' != $url) { + $url = rtrim($url, '|'); + } + $item = str_replace('|', '/', $url); + if (isset($rules[$item])) { + // 静态路由规则检测 + $rule = $rules[$item]; + if (true === $rule) { + $rule = self::getRouteExpress($item); + } + if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) { + self::setOption($rule['option']); + return self::parseRule($item, $rule['route'], $url, $rule['option']); + } + } + + // 路由规则检测 + if (!empty($rules)) { + return self::checkRoute($request, $rules, $url, $depr); + } + return false; + } + + private static function getRouteExpress($key) + { + return self::$domainRule ? self::$domainRule['*'][$key] : self::$rules['*'][$key]; + } + + /** + * 检测路由规则 + * @access private + * @param Request $request + * @param array $rules 路由规则 + * @param string $url URL地址 + * @param string $depr URL分割符 + * @param string $group 路由分组名 + * @param array $options 路由参数(分组) + * @return mixed + */ + private static function checkRoute($request, $rules, $url, $depr = '/', $group = '', $options = []) + { + foreach ($rules as $key => $item) { + if (true === $item) { + $item = self::getRouteExpress($key); + } + if (!isset($item['rule'])) { + continue; + } + $rule = $item['rule']; + $route = $item['route']; + $vars = $item['var']; + $option = $item['option']; + $pattern = $item['pattern']; + + // 检查参数有效性 + if (!self::checkOption($option, $request)) { + continue; + } + + if (isset($option['ext'])) { + // 路由ext参数 优先于系统配置的URL伪静态后缀参数 + $url = preg_replace('/\.' . $request->ext() . '$/i', '', $url); + } + + if (is_array($rule)) { + // 分组路由 + $pos = strpos(str_replace('<', ':', $key), ':'); + if (false !== $pos) { + $str = substr($key, 0, $pos); + } else { + $str = $key; + } + if (is_string($str) && $str && 0 !== stripos(str_replace('|', '/', $url), $str)) { + continue; + } + self::setOption($option); + $result = self::checkRoute($request, $rule, $url, $depr, $key, $option); + if (false !== $result) { + return $result; + } + } elseif ($route) { + if ('__miss__' == $rule || '__auto__' == $rule) { + // 指定特殊路由 + $var = trim($rule, '__'); + ${$var} = $item; + continue; + } + if ($group) { + $rule = $group . ($rule ? '/' . ltrim($rule, '/') : ''); + } + + self::setOption($option); + if (isset($options['bind_model']) && isset($option['bind_model'])) { + $option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']); + } + $result = self::checkRule($rule, $route, $url, $pattern, $option, $depr); + if (false !== $result) { + return $result; + } + } + } + if (isset($auto)) { + // 自动解析URL地址 + return self::parseUrl($auto['route'] . '/' . $url, $depr); + } elseif (isset($miss)) { + // 未匹配所有路由的路由规则处理 + return self::parseRule('', $miss['route'], $url, $miss['option']); + } + return false; + } + + /** + * 检测路由别名 + * @access private + * @param Request $request + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @return mixed + */ + private static function checkRouteAlias($request, $url, $depr) + { + $array = explode('|', $url); + $alias = array_shift($array); + $item = self::$rules['alias'][$alias]; + + if (is_array($item)) { + list($rule, $option) = $item; + $action = $array[0]; + if (isset($option['allow']) && !in_array($action, explode(',', $option['allow']))) { + // 允许操作 + return false; + } elseif (isset($option['except']) && in_array($action, explode(',', $option['except']))) { + // 排除操作 + return false; + } + if (isset($option['method'][$action])) { + $option['method'] = $option['method'][$action]; + } + } else { + $rule = $item; + } + $bind = implode('|', $array); + // 参数有效性检查 + if (isset($option) && !self::checkOption($option, $request)) { + // 路由不匹配 + return false; + } elseif (0 === strpos($rule, '\\')) { + // 路由到类 + return self::bindToClass($bind, substr($rule, 1), $depr); + } elseif (0 === strpos($rule, '@')) { + // 路由到控制器类 + return self::bindToController($bind, substr($rule, 1), $depr); + } else { + // 路由到模块/控制器 + return self::bindToModule($bind, $rule, $depr); + } + } + + /** + * 检测URL绑定 + * @access private + * @param string $url URL地址 + * @param array $rules 路由规则 + * @param string $depr URL分隔符 + * @return mixed + */ + private static function checkUrlBind(&$url, &$rules, $depr = '/') + { + if (!empty(self::$bind)) { + $type = self::$bind['type']; + $bind = self::$bind[$type]; + // 记录绑定信息 + App::$debug && Log::record('[ BIND ] ' . var_export($bind, true), 'info'); + // 如果有URL绑定 则进行绑定检测 + switch ($type) { + case 'class': + // 绑定到类 + return self::bindToClass($url, $bind, $depr); + case 'controller': + // 绑定到控制器类 + return self::bindToController($url, $bind, $depr); + case 'namespace': + // 绑定到命名空间 + return self::bindToNamespace($url, $bind, $depr); + } + } + return false; + } + + /** + * 绑定到类 + * @access public + * @param string $url URL地址 + * @param string $class 类名(带命名空间) + * @param string $depr URL分隔符 + * @return array + */ + public static function bindToClass($url, $class, $depr = '/') + { + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); + if (!empty($array[1])) { + self::parseUrlParams($array[1]); + } + return ['type' => 'method', 'method' => [$class, $action], 'var' => []]; + } + + /** + * 绑定到命名空间 + * @access public + * @param string $url URL地址 + * @param string $namespace 命名空间 + * @param string $depr URL分隔符 + * @return array + */ + public static function bindToNamespace($url, $namespace, $depr = '/') + { + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 3); + $class = !empty($array[0]) ? $array[0] : Config::get('default_controller'); + $method = !empty($array[1]) ? $array[1] : Config::get('default_action'); + if (!empty($array[2])) { + self::parseUrlParams($array[2]); + } + return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method], 'var' => []]; + } + + /** + * 绑定到控制器类 + * @access public + * @param string $url URL地址 + * @param string $controller 控制器名 (支持带模块名 index/user ) + * @param string $depr URL分隔符 + * @return array + */ + public static function bindToController($url, $controller, $depr = '/') + { + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); + if (!empty($array[1])) { + self::parseUrlParams($array[1]); + } + return ['type' => 'controller', 'controller' => $controller . '/' . $action, 'var' => []]; + } + + /** + * 绑定到模块/控制器 + * @access public + * @param string $url URL地址 + * @param string $controller 控制器类名(带命名空间) + * @param string $depr URL分隔符 + * @return array + */ + public static function bindToModule($url, $controller, $depr = '/') + { + $url = str_replace($depr, '|', $url); + $array = explode('|', $url, 2); + $action = !empty($array[0]) ? $array[0] : Config::get('default_action'); + if (!empty($array[1])) { + self::parseUrlParams($array[1]); + } + return ['type' => 'module', 'module' => $controller . '/' . $action]; + } + + /** + * 路由参数有效性检查 + * @access private + * @param array $option 路由参数 + * @param Request $request Request对象 + * @return bool + */ + private static function checkOption($option, $request) + { + if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method())) + || (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测 + || (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测 + || (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测 + || (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测 + || (isset($option['ext']) && false === stripos('|' . $option['ext'] . '|', '|' . $request->ext() . '|')) // 伪静态后缀检测 + || (isset($option['deny_ext']) && false !== stripos('|' . $option['deny_ext'] . '|', '|' . $request->ext() . '|')) + || (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测 + || (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测 + || (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测 + || (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测 + || (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测 + ) { + return false; + } + return true; + } + + /** + * 检测路由规则 + * @access private + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $url URL地址 + * @param array $pattern 变量规则 + * @param array $option 路由参数 + * @param string $depr URL分隔符(全局) + * @return array|false + */ + private static function checkRule($rule, $route, $url, $pattern, $option, $depr) + { + // 检查完整规则定义 + if (isset($pattern['__url__']) && !preg_match(0 === strpos($pattern['__url__'], '/') ? $pattern['__url__'] : '/^' . $pattern['__url__'] . '/', str_replace('|', $depr, $url))) { + return false; + } + // 检查路由的参数分隔符 + if (isset($option['param_depr'])) { + $url = str_replace(['|', $option['param_depr']], [$depr, '|'], $url); + } + + $len1 = substr_count($url, '|'); + $len2 = substr_count($rule, '/'); + // 多余参数是否合并 + $merge = !empty($option['merge_extra_vars']); + if ($merge && $len1 > $len2) { + $url = str_replace('|', $depr, $url); + $url = implode('|', explode($depr, $url, $len2 + 1)); + } + + if ($len1 >= $len2 || strpos($rule, '[')) { + if (!empty($option['complete_match'])) { + // 完整匹配 + if (!$merge && $len1 != $len2 && (false === strpos($rule, '[') || $len1 > $len2 || $len1 < $len2 - substr_count($rule, '['))) { + return false; + } + } + $pattern = array_merge(self::$rules['pattern'], $pattern); + if (false !== $match = self::match($url, $rule, $pattern)) { + // 匹配到路由规则 + return self::parseRule($rule, $route, $url, $option, $match); + } + } + return false; + } + + /** + * 解析模块的URL地址 [模块/控制器/操作?]参数1=值1&参数2=值2... + * @access public + * @param string $url URL地址 + * @param string $depr URL分隔符 + * @param bool $autoSearch 是否自动深度搜索控制器 + * @return array + */ + public static function parseUrl($url, $depr = '/', $autoSearch = false) + { + + if (isset(self::$bind['module'])) { + $bind = str_replace('/', $depr, self::$bind['module']); + // 如果有模块/控制器绑定 + $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); + } + $url = str_replace($depr, '|', $url); + list($path, $var) = self::parseUrlPath($url); + $route = [null, null, null]; + if (isset($path)) { + // 解析模块 + $module = Config::get('app_multi_module') ? array_shift($path) : null; + if ($autoSearch) { + // 自动搜索控制器 + $dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer'); + $suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; + $item = []; + $find = false; + foreach ($path as $val) { + $item[] = $val; + $file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT; + $file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT; + if (is_file($file)) { + $find = true; + break; + } else { + $dir .= DS . Loader::parseName($val); + } + } + if ($find) { + $controller = implode('.', $item); + $path = array_slice($path, count($item)); + } else { + $controller = array_shift($path); + } + } else { + // 解析控制器 + $controller = !empty($path) ? array_shift($path) : null; + } + // 解析操作 + $action = !empty($path) ? array_shift($path) : null; + // 解析额外参数 + self::parseUrlParams(empty($path) ? '' : implode('|', $path)); + // 封装路由 + $route = [$module, $controller, $action]; + // 检查地址是否被定义过路由 + $name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action); + $name2 = ''; + if (empty($module) || isset($bind) && $module == $bind) { + $name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action); + } + + if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) { + throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url)); + } + } + return ['type' => 'module', 'module' => $route]; + } + + /** + * 解析URL的pathinfo参数和变量 + * @access private + * @param string $url URL地址 + * @return array + */ + private static function parseUrlPath($url) + { + // 分隔符替换 确保路由定义使用统一的分隔符 + $url = str_replace('|', '/', $url); + $url = trim($url, '/'); + $var = []; + if (false !== strpos($url, '?')) { + // [模块/控制器/操作?]参数1=值1&参数2=值2... + $info = parse_url($url); + $path = explode('/', $info['path']); + parse_str($info['query'], $var); + } elseif (strpos($url, '/')) { + // [模块/控制器/操作] + $path = explode('/', $url); + } else { + $path = [$url]; + } + return [$path, $var]; + } + + /** + * 检测URL和规则路由是否匹配 + * @access private + * @param string $url URL地址 + * @param string $rule 路由规则 + * @param array $pattern 变量规则 + * @return array|false + */ + private static function match($url, $rule, $pattern) + { + $m2 = explode('/', $rule); + $m1 = explode('|', $url); + + $var = []; + foreach ($m2 as $key => $val) { + // val中定义了多个变量 + if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { + $value = []; + $replace = []; + foreach ($matches[1] as $name) { + if (strpos($name, '?')) { + $name = substr($name, 0, -1); + $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?'; + } else { + $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')'; + } + $value[] = $name; + } + $val = str_replace($matches[0], $replace, $val); + if (preg_match('/^' . $val . '$/', isset($m1[$key]) ? $m1[$key] : '', $match)) { + array_shift($match); + foreach ($value as $k => $name) { + if (isset($match[$k])) { + $var[$name] = $match[$k]; + } + } + continue; + } else { + return false; + } + } + + if (0 === strpos($val, '[:')) { + // 可选参数 + $val = substr($val, 1, -1); + $optional = true; + } else { + $optional = false; + } + if (0 === strpos($val, ':')) { + // URL变量 + $name = substr($val, 1); + if (!$optional && !isset($m1[$key])) { + return false; + } + if (isset($m1[$key]) && isset($pattern[$name])) { + // 检查变量规则 + if ($pattern[$name] instanceof \Closure) { + $result = call_user_func_array($pattern[$name], [$m1[$key]]); + if (false === $result) { + return false; + } + } elseif (!preg_match(0 === strpos($pattern[$name], '/') ? $pattern[$name] : '/^' . $pattern[$name] . '$/', $m1[$key])) { + return false; + } + } + $var[$name] = isset($m1[$key]) ? $m1[$key] : ''; + } elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) { + return false; + } + } + // 成功匹配后返回URL中的动态变量数组 + return $var; + } + + /** + * 解析规则路由 + * @access private + * @param string $rule 路由规则 + * @param string $route 路由地址 + * @param string $pathinfo URL地址 + * @param array $option 路由参数 + * @param array $matches 匹配的变量 + * @param bool $fromCache 通过缓存解析 + * @return array + */ + private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = [], $fromCache = false) + { + $request = Request::instance(); + + //保存解析缓存 + if (Config::get('route_check_cache') && !$fromCache) { + try { + $key = self::getCheckCacheKey($request); + Cache::tag('route_check')->set($key, [$rule, $route, $pathinfo, $option, $matches]); + } catch (\Exception $e) { + + } + } + + // 解析路由规则 + if ($rule) { + $rule = explode('/', $rule); + // 获取URL地址中的参数 + $paths = explode('|', $pathinfo); + foreach ($rule as $item) { + $fun = ''; + if (0 === strpos($item, '[:')) { + $item = substr($item, 1, -1); + } + if (0 === strpos($item, ':')) { + $var = substr($item, 1); + $matches[$var] = array_shift($paths); + } else { + // 过滤URL中的静态变量 + array_shift($paths); + } + } + } else { + $paths = explode('|', $pathinfo); + } + + // 获取路由地址规则 + if (is_string($route) && isset($option['prefix'])) { + // 路由地址前缀 + $route = $option['prefix'] . $route; + } + // 替换路由地址中的变量 + if (is_string($route) && !empty($matches)) { + foreach ($matches as $key => $val) { + if (false !== strpos($route, ':' . $key)) { + $route = str_replace(':' . $key, $val, $route); + } + } + } + + // 绑定模型数据 + if (isset($option['bind_model'])) { + $bind = []; + foreach ($option['bind_model'] as $key => $val) { + if ($val instanceof \Closure) { + $result = call_user_func_array($val, [$matches]); + } else { + if (is_array($val)) { + $fields = explode('&', $val[1]); + $model = $val[0]; + $exception = isset($val[2]) ? $val[2] : true; + } else { + $fields = ['id']; + $model = $val; + $exception = true; + } + $where = []; + $match = true; + foreach ($fields as $field) { + if (!isset($matches[$field])) { + $match = false; + break; + } else { + $where[$field] = $matches[$field]; + } + } + if ($match) { + $query = strpos($model, '\\') ? $model::where($where) : Loader::model($model)->where($where); + $result = $query->failException($exception)->find(); + } + } + if (!empty($result)) { + $bind[$key] = $result; + } + } + $request->bind($bind); + } + + if (!empty($option['response'])) { + Hook::add('response_send', $option['response']); + } + + // 解析额外参数 + self::parseUrlParams(empty($paths) ? '' : implode('|', $paths), $matches); + // 记录匹配的路由信息 + $request->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]); + + // 检测路由after行为 + if (!empty($option['after_behavior'])) { + if ($option['after_behavior'] instanceof \Closure) { + $result = call_user_func_array($option['after_behavior'], []); + } else { + foreach ((array) $option['after_behavior'] as $behavior) { + $result = Hook::exec($behavior, ''); + if (!is_null($result)) { + break; + } + } + } + // 路由规则重定向 + if ($result instanceof Response) { + return ['type' => 'response', 'response' => $result]; + } elseif (is_array($result)) { + return $result; + } + } + + if ($route instanceof \Closure) { + // 执行闭包 + $result = ['type' => 'function', 'function' => $route]; + } elseif (0 === strpos($route, '/') || strpos($route, '://')) { + // 路由到重定向地址 + $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301]; + } elseif (false !== strpos($route, '\\')) { + // 路由到方法 + list($path, $var) = self::parseUrlPath($route); + $route = str_replace('/', '@', implode('/', $path)); + $method = strpos($route, '@') ? explode('@', $route) : $route; + $result = ['type' => 'method', 'method' => $method, 'var' => $var]; + } elseif (0 === strpos($route, '@')) { + // 路由到控制器 + $route = substr($route, 1); + list($route, $var) = self::parseUrlPath($route); + $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; + $request->action(array_pop($route)); + $request->controller($route ? array_pop($route) : Config::get('default_controller')); + $request->module($route ? array_pop($route) : Config::get('default_module')); + App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); + } else { + // 路由到模块/控制器/操作 + $result = self::parseModule($route, isset($option['convert']) ? $option['convert'] : false); + } + // 开启请求缓存 + if ($request->isGet() && isset($option['cache'])) { + $cache = $option['cache']; + if (is_array($cache)) { + list($key, $expire, $tag) = array_pad($cache, 3, null); + } else { + $key = str_replace('|', '/', $pathinfo); + $expire = $cache; + $tag = null; + } + $request->cache($key, $expire, $tag); + } + return $result; + } + + /** + * 解析URL地址为 模块/控制器/操作 + * @access private + * @param string $url URL地址 + * @param bool $convert 是否自动转换URL地址 + * @return array + */ + private static function parseModule($url, $convert = false) + { + list($path, $var) = self::parseUrlPath($url); + $action = array_pop($path); + $controller = !empty($path) ? array_pop($path) : null; + $module = Config::get('app_multi_module') && !empty($path) ? array_pop($path) : null; + $method = Request::instance()->method(); + if (Config::get('use_action_prefix') && !empty(self::$methodPrefix[$method])) { + // 操作方法前缀支持 + $action = 0 !== strpos($action, self::$methodPrefix[$method]) ? self::$methodPrefix[$method] . $action : $action; + } + // 设置当前请求的路由变量 + Request::instance()->route($var); + // 路由到模块/控制器/操作 + return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => $convert]; + } + + /** + * 解析URL地址中的参数Request对象 + * @access private + * @param string $url 路由规则 + * @param array $var 变量 + * @return void + */ + private static function parseUrlParams($url, &$var = []) + { + if ($url) { + if (Config::get('url_param_type')) { + $var += explode('|', $url); + } else { + preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) { + $var[$match[1]] = strip_tags($match[2]); + }, $url); + } + } + // 设置当前请求的参数 + Request::instance()->route($var); + } + + // 分析路由规则中的变量 + private static function parseVar($rule) + { + // 提取路由规则中的变量 + $var = []; + foreach (explode('/', $rule) as $val) { + $optional = false; + if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { + foreach ($matches[1] as $name) { + if (strpos($name, '?')) { + $name = substr($name, 0, -1); + $optional = true; + } else { + $optional = false; + } + $var[$name] = $optional ? 2 : 1; + } + } + + if (0 === strpos($val, '[:')) { + // 可选参数 + $optional = true; + $val = substr($val, 1, -1); + } + if (0 === strpos($val, ':')) { + // URL变量 + $name = substr($val, 1); + $var[$name] = $optional ? 2 : 1; + } + } + return $var; + } + + /** + * 获取路由解析缓存的key + * @param Request $request + * @return string + */ + private static function getCheckCacheKey(Request $request) + { + static $key; + + if (empty($key)) { + if ($callback = Config::get('route_check_cache_key')) { + $key = call_user_func($callback, $request); + } else { + $key = "{$request->host(true)}|{$request->method()}|{$request->path()}"; + } + } + + return $key; + } +} diff --git a/source/thinkphp/library/think/Session.php b/source/thinkphp/library/think/Session.php new file mode 100644 index 0000000..61150bc --- /dev/null +++ b/source/thinkphp/library/think/Session.php @@ -0,0 +1,366 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; + +class Session +{ + protected static $prefix = ''; + protected static $init = null; + + /** + * 设置或者获取session作用域(前缀) + * @param string $prefix + * @return string|void + */ + public static function prefix($prefix = '') + { + empty(self::$init) && self::boot(); + if (empty($prefix) && null !== $prefix) { + return self::$prefix; + } else { + self::$prefix = $prefix; + } + } + + /** + * session初始化 + * @param array $config + * @return void + * @throws \think\Exception + */ + public static function init(array $config = []) + { + if (empty($config)) { + $config = Config::get('session'); + } + // 记录初始化信息 + App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info'); + $isDoStart = false; + if (isset($config['use_trans_sid'])) { + ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0); + } + + // 启动session + if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) { + ini_set('session.auto_start', 0); + $isDoStart = true; + } + + if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) { + self::$prefix = $config['prefix']; + } + if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) { + session_id($_REQUEST[$config['var_session_id']]); + } elseif (isset($config['id']) && !empty($config['id'])) { + session_id($config['id']); + } + if (isset($config['name'])) { + session_name($config['name']); + } + if (isset($config['path'])) { + session_save_path($config['path']); + } + if (isset($config['domain'])) { + ini_set('session.cookie_domain', $config['domain']); + } + if (isset($config['expire'])) { + ini_set('session.gc_maxlifetime', $config['expire']); + ini_set('session.cookie_lifetime', $config['expire']); + } + if (isset($config['secure'])) { + ini_set('session.cookie_secure', $config['secure']); + } + if (isset($config['httponly'])) { + ini_set('session.cookie_httponly', $config['httponly']); + } + if (isset($config['use_cookies'])) { + ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0); + } + if (isset($config['cache_limiter'])) { + session_cache_limiter($config['cache_limiter']); + } + if (isset($config['cache_expire'])) { + session_cache_expire($config['cache_expire']); + } + if (!empty($config['type'])) { + // 读取session驱动 + $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']); + + // 检查驱动类 + if (!class_exists($class) || !session_set_save_handler(new $class($config))) { + throw new ClassNotFoundException('error session handler:' . $class, $class); + } + } + if ($isDoStart) { + session_start(); + self::$init = true; + } else { + self::$init = false; + } + } + + /** + * session自动启动或者初始化 + * @return void + */ + public static function boot() + { + if (is_null(self::$init)) { + self::init(); + } elseif (false === self::$init) { + if (PHP_SESSION_ACTIVE != session_status()) { + session_start(); + } + self::$init = true; + } + } + + /** + * session设置 + * @param string $name session名称 + * @param mixed $value session值 + * @param string|null $prefix 作用域(前缀) + * @return void + */ + public static function set($name, $value = '', $prefix = null) + { + empty(self::$init) && self::boot(); + + $prefix = !is_null($prefix) ? $prefix : self::$prefix; + if (strpos($name, '.')) { + // 二维数组赋值 + list($name1, $name2) = explode('.', $name); + if ($prefix) { + $_SESSION[$prefix][$name1][$name2] = $value; + } else { + $_SESSION[$name1][$name2] = $value; + } + } elseif ($prefix) { + $_SESSION[$prefix][$name] = $value; + } else { + $_SESSION[$name] = $value; + } + } + + /** + * session获取 + * @param string $name session名称 + * @param string|null $prefix 作用域(前缀) + * @return mixed + */ + public static function get($name = '', $prefix = null) + { + empty(self::$init) && self::boot(); + $prefix = !is_null($prefix) ? $prefix : self::$prefix; + if ('' == $name) { + // 获取全部的session + $value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION; + } elseif ($prefix) { + // 获取session + if (strpos($name, '.')) { + list($name1, $name2) = explode('.', $name); + $value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null; + } else { + $value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null; + } + } else { + if (strpos($name, '.')) { + list($name1, $name2) = explode('.', $name); + $value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null; + } else { + $value = isset($_SESSION[$name]) ? $_SESSION[$name] : null; + } + } + return $value; + } + + /** + * session获取并删除 + * @param string $name session名称 + * @param string|null $prefix 作用域(前缀) + * @return mixed + */ + public static function pull($name, $prefix = null) + { + $result = self::get($name, $prefix); + if ($result) { + self::delete($name, $prefix); + return $result; + } else { + return; + } + } + + /** + * session设置 下一次请求有效 + * @param string $name session名称 + * @param mixed $value session值 + * @param string|null $prefix 作用域(前缀) + * @return void + */ + public static function flash($name, $value) + { + self::set($name, $value); + if (!self::has('__flash__.__time__')) { + self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']); + } + self::push('__flash__', $name); + } + + /** + * 清空当前请求的session数据 + * @return void + */ + public static function flush() + { + if (self::$init) { + $item = self::get('__flash__'); + + if (!empty($item)) { + $time = $item['__time__']; + if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) { + unset($item['__time__']); + self::delete($item); + self::set('__flash__', []); + } + } + } + } + + /** + * 删除session数据 + * @param string|array $name session名称 + * @param string|null $prefix 作用域(前缀) + * @return void + */ + public static function delete($name, $prefix = null) + { + empty(self::$init) && self::boot(); + $prefix = !is_null($prefix) ? $prefix : self::$prefix; + if (is_array($name)) { + foreach ($name as $key) { + self::delete($key, $prefix); + } + } elseif (strpos($name, '.')) { + list($name1, $name2) = explode('.', $name); + if ($prefix) { + unset($_SESSION[$prefix][$name1][$name2]); + } else { + unset($_SESSION[$name1][$name2]); + } + } else { + if ($prefix) { + unset($_SESSION[$prefix][$name]); + } else { + unset($_SESSION[$name]); + } + } + } + + /** + * 清空session数据 + * @param string|null $prefix 作用域(前缀) + * @return void + */ + public static function clear($prefix = null) + { + empty(self::$init) && self::boot(); + $prefix = !is_null($prefix) ? $prefix : self::$prefix; + if ($prefix) { + unset($_SESSION[$prefix]); + } else { + $_SESSION = []; + } + } + + /** + * 判断session数据 + * @param string $name session名称 + * @param string|null $prefix + * @return bool + */ + public static function has($name, $prefix = null) + { + empty(self::$init) && self::boot(); + $prefix = !is_null($prefix) ? $prefix : self::$prefix; + if (strpos($name, '.')) { + // 支持数组 + list($name1, $name2) = explode('.', $name); + return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]); + } else { + return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]); + } + } + + /** + * 添加数据到一个session数组 + * @param string $key + * @param mixed $value + * @return void + */ + public static function push($key, $value) + { + $array = self::get($key); + if (is_null($array)) { + $array = []; + } + $array[] = $value; + self::set($key, $array); + } + + /** + * 启动session + * @return void + */ + public static function start() + { + session_start(); + self::$init = true; + } + + /** + * 销毁session + * @return void + */ + public static function destroy() + { + if (!empty($_SESSION)) { + $_SESSION = []; + } + session_unset(); + session_destroy(); + self::$init = null; + } + + /** + * 重新生成session_id + * @param bool $delete 是否删除关联会话文件 + * @return void + */ + public static function regenerate($delete = false) + { + session_regenerate_id($delete); + } + + /** + * 暂停session + * @return void + */ + public static function pause() + { + // 暂停session + session_write_close(); + self::$init = false; + } +} diff --git a/source/thinkphp/library/think/Template.php b/source/thinkphp/library/think/Template.php new file mode 100644 index 0000000..9ba0ff3 --- /dev/null +++ b/source/thinkphp/library/think/Template.php @@ -0,0 +1,1139 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\TemplateNotFoundException; +use think\template\TagLib; + +/** + * ThinkPHP分离出来的模板引擎 + * 支持XML标签和普通标签的模板解析 + * 编译型模板引擎 支持动态缓存 + */ +class Template +{ + // 模板变量 + protected $data = []; + // 引擎配置 + protected $config = [ + 'view_path' => '', // 模板路径 + 'view_base' => '', + 'view_suffix' => 'html', // 默认模板文件后缀 + 'view_depr' => DS, + 'cache_suffix' => 'php', // 默认模板缓存后缀 + 'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数 + 'tpl_deny_php' => false, // 默认模板引擎是否禁用PHP原生代码 + 'tpl_begin' => '{', // 模板引擎普通标签开始标记 + 'tpl_end' => '}', // 模板引擎普通标签结束标记 + 'strip_space' => false, // 是否去除模板文件里面的html空格与换行 + 'tpl_cache' => true, // 是否开启模板编译缓存,设为false则每次都会重新编译 + 'compile_type' => 'file', // 模板编译类型 + 'cache_prefix' => '', // 模板缓存前缀标识,可以动态改变 + 'cache_time' => 0, // 模板缓存有效期 0 为永久,(以数字为值,单位:秒) + 'layout_on' => false, // 布局模板开关 + 'layout_name' => 'layout', // 布局模板入口文件 + 'layout_item' => '{__CONTENT__}', // 布局模板的内容替换标识 + 'taglib_begin' => '{', // 标签库标签开始标记 + 'taglib_end' => '}', // 标签库标签结束标记 + 'taglib_load' => true, // 是否使用内置标签库之外的其它标签库,默认自动检测 + 'taglib_build_in' => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序 + 'taglib_pre_load' => '', // 需要额外加载的标签库(须指定标签库名称),多个以逗号分隔 + 'display_cache' => false, // 模板渲染缓存 + 'cache_id' => '', // 模板缓存ID + 'tpl_replace_string' => [], + 'tpl_var_identify' => 'array', // .语法变量识别,array|object|'', 为空时自动识别 + ]; + + private $literal = []; + private $includeFile = []; // 记录所有模板包含的文件路径及更新时间 + protected $storage; + + /** + * 构造函数 + * @access public + * @param array $config + */ + public function __construct(array $config = []) + { + $this->config['cache_path'] = TEMP_PATH; + $this->config = array_merge($this->config, $config); + + $this->config['taglib_begin_origin'] = $this->config['taglib_begin']; + $this->config['taglib_end_origin'] = $this->config['taglib_end']; + + $this->config['taglib_begin'] = preg_quote($this->config['taglib_begin'], '/'); + $this->config['taglib_end'] = preg_quote($this->config['taglib_end'], '/'); + $this->config['tpl_begin'] = preg_quote($this->config['tpl_begin'], '/'); + $this->config['tpl_end'] = preg_quote($this->config['tpl_end'], '/'); + + // 初始化模板编译存储器 + $type = $this->config['compile_type'] ? $this->config['compile_type'] : 'File'; + $class = false !== strpos($type, '\\') ? $type : '\\think\\template\\driver\\' . ucwords($type); + $this->storage = new $class(); + } + + /** + * 模板变量赋值 + * @access public + * @param mixed $name + * @param mixed $value + * @return void + */ + public function assign($name, $value = '') + { + if (is_array($name)) { + $this->data = array_merge($this->data, $name); + } else { + $this->data[$name] = $value; + } + } + + /** + * 模板引擎参数赋值 + * @access public + * @param mixed $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->config[$name] = $value; + } + + /** + * 模板引擎配置项 + * @access public + * @param array|string $config + * @return string|void|array + */ + public function config($config) + { + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } elseif (isset($this->config[$config])) { + return $this->config[$config]; + } else { + return; + } + } + + /** + * 模板变量获取 + * @access public + * @param string $name 变量名 + * @return mixed + */ + public function get($name = '') + { + if ('' == $name) { + return $this->data; + } else { + $data = $this->data; + foreach (explode('.', $name) as $key => $val) { + if (isset($data[$val])) { + $data = $data[$val]; + } else { + $data = null; + break; + } + } + return $data; + } + } + + /** + * 渲染模板文件 + * @access public + * @param string $template 模板文件 + * @param array $vars 模板变量 + * @param array $config 模板参数 + * @return void + */ + public function fetch($template, $vars = [], $config = []) + { + if ($vars) { + $this->data = $vars; + } + if ($config) { + $this->config($config); + } + if (!empty($this->config['cache_id']) && $this->config['display_cache']) { + // 读取渲染缓存 + $cacheContent = Cache::get($this->config['cache_id']); + if (false !== $cacheContent) { + echo $cacheContent; + return; + } + } + $template = $this->parseTemplateFile($template); + if ($template) { + $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($this->config['layout_name'] . $template) . '.' . ltrim($this->config['cache_suffix'], '.'); + if (!$this->checkCache($cacheFile)) { + // 缓存无效 重新模板编译 + $content = file_get_contents($template); + $this->compiler($content, $cacheFile); + } + // 页面缓存 + ob_start(); + ob_implicit_flush(0); + // 读取编译存储 + $this->storage->read($cacheFile, $this->data); + // 获取并清空缓存 + $content = ob_get_clean(); + if (!empty($this->config['cache_id']) && $this->config['display_cache']) { + // 缓存页面输出 + Cache::set($this->config['cache_id'], $content, $this->config['cache_time']); + } + echo $content; + } + } + + /** + * 渲染模板内容 + * @access public + * @param string $content 模板内容 + * @param array $vars 模板变量 + * @param array $config 模板参数 + * @return void + */ + public function display($content, $vars = [], $config = []) + { + if ($vars) { + $this->data = $vars; + } + if ($config) { + $this->config($config); + } + $cacheFile = $this->config['cache_path'] . $this->config['cache_prefix'] . md5($content) . '.' . ltrim($this->config['cache_suffix'], '.'); + if (!$this->checkCache($cacheFile)) { + // 缓存无效 模板编译 + $this->compiler($content, $cacheFile); + } + // 读取编译存储 + $this->storage->read($cacheFile, $this->data); + } + + /** + * 设置布局 + * @access public + * @param mixed $name 布局模板名称 false 则关闭布局 + * @param string $replace 布局模板内容替换标识 + * @return Template + */ + public function layout($name, $replace = '') + { + if (false === $name) { + // 关闭布局 + $this->config['layout_on'] = false; + } else { + // 开启布局 + $this->config['layout_on'] = true; + // 名称必须为字符串 + if (is_string($name)) { + $this->config['layout_name'] = $name; + } + if (!empty($replace)) { + $this->config['layout_item'] = $replace; + } + } + return $this; + } + + /** + * 检查编译缓存是否有效 + * 如果无效则需要重新编译 + * @access private + * @param string $cacheFile 缓存文件名 + * @return boolean + */ + private function checkCache($cacheFile) + { + // 未开启缓存功能 + if (!$this->config['tpl_cache']) { + return false; + } + // 缓存文件不存在 + if (!is_file($cacheFile)) { + return false; + } + // 读取缓存文件失败 + if (!$handle = @fopen($cacheFile, "r")) { + return false; + } + // 读取第一行 + preg_match('/\/\*(.+?)\*\//', fgets($handle), $matches); + if (!isset($matches[1])) { + return false; + } + $includeFile = unserialize($matches[1]); + if (!is_array($includeFile)) { + return false; + } + // 检查模板文件是否有更新 + foreach ($includeFile as $path => $time) { + if (is_file($path) && filemtime($path) > $time) { + // 模板文件如果有更新则缓存需要更新 + return false; + } + } + // 检查编译存储是否有效 + return $this->storage->check($cacheFile, $this->config['cache_time']); + } + + /** + * 检查编译缓存是否存在 + * @access public + * @param string $cacheId 缓存的id + * @return boolean + */ + public function isCache($cacheId) + { + if ($cacheId && $this->config['display_cache']) { + // 缓存页面输出 + return Cache::has($cacheId); + } + return false; + } + + /** + * 编译模板文件内容 + * @access private + * @param string $content 模板内容 + * @param string $cacheFile 缓存文件名 + * @return void + */ + private function compiler(&$content, $cacheFile) + { + // 判断是否启用布局 + if ($this->config['layout_on']) { + if (false !== strpos($content, '{__NOLAYOUT__}')) { + // 可以单独定义不使用布局 + $content = str_replace('{__NOLAYOUT__}', '', $content); + } else { + // 读取布局模板 + $layoutFile = $this->parseTemplateFile($this->config['layout_name']); + if ($layoutFile) { + // 替换布局的主体内容 + $content = str_replace($this->config['layout_item'], $content, file_get_contents($layoutFile)); + } + } + } else { + $content = str_replace('{__NOLAYOUT__}', '', $content); + } + + // 模板解析 + $this->parse($content); + if ($this->config['strip_space']) { + /* 去除html空格与换行 */ + $find = ['~>\s+<~', '~>(\s+\n|\r)~']; + $replace = ['><', '>']; + $content = preg_replace($find, $replace, $content); + } + // 优化生成的php代码 + $content = preg_replace('/\?>\s*<\?php\s(?!echo\b)/s', '', $content); + // 模板过滤输出 + $replace = $this->config['tpl_replace_string']; + $content = str_replace(array_keys($replace), array_values($replace), $content); + // 添加安全代码及模板引用记录 + $content = 'includeFile) . '*/ ?>' . "\n" . $content; + // 编译存储 + $this->storage->write($cacheFile, $content); + $this->includeFile = []; + return; + } + + /** + * 模板解析入口 + * 支持普通标签和TagLib解析 支持自定义标签库 + * @access public + * @param string $content 要解析的模板内容 + * @return void + */ + public function parse(&$content) + { + // 内容为空不解析 + if (empty($content)) { + return; + } + // 替换literal标签内容 + $this->parseLiteral($content); + // 解析继承 + $this->parseExtend($content); + // 解析布局 + $this->parseLayout($content); + // 检查include语法 + $this->parseInclude($content); + // 替换包含文件中literal标签内容 + $this->parseLiteral($content); + // 检查PHP语法 + $this->parsePhp($content); + + // 获取需要引入的标签库列表 + // 标签库只需要定义一次,允许引入多个一次 + // 一般放在文件的最前面 + // 格式: + // 当TAGLIB_LOAD配置为true时才会进行检测 + if ($this->config['taglib_load']) { + $tagLibs = $this->getIncludeTagLib($content); + if (!empty($tagLibs)) { + // 对导入的TagLib进行解析 + foreach ($tagLibs as $tagLibName) { + $this->parseTagLib($tagLibName, $content); + } + } + } + // 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀 + if ($this->config['taglib_pre_load']) { + $tagLibs = explode(',', $this->config['taglib_pre_load']); + foreach ($tagLibs as $tag) { + $this->parseTagLib($tag, $content); + } + } + // 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀 + $tagLibs = explode(',', $this->config['taglib_build_in']); + foreach ($tagLibs as $tag) { + $this->parseTagLib($tag, $content, true); + } + // 解析普通模板标签 {$tagName} + $this->parseTag($content); + + // 还原被替换的Literal标签 + $this->parseLiteral($content, true); + return; + } + + /** + * 检查PHP语法 + * @access private + * @param string $content 要解析的模板内容 + * @return void + * @throws \think\Exception + */ + private function parsePhp(&$content) + { + // 短标签的情况要将' . "\n", $content); + // PHP语法检查 + if ($this->config['tpl_deny_php'] && false !== strpos($content, 'getRegex('layout'), $content, $matches)) { + // 替换Layout标签 + $content = str_replace($matches[0], '', $content); + // 解析Layout标签 + $array = $this->parseAttr($matches[0]); + if (!$this->config['layout_on'] || $this->config['layout_name'] != $array['name']) { + // 读取布局模板 + $layoutFile = $this->parseTemplateFile($array['name']); + if ($layoutFile) { + $replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item']; + // 替换布局的主体内容 + $content = str_replace($replace, $content, file_get_contents($layoutFile)); + } + } + } else { + $content = str_replace('{__NOLAYOUT__}', '', $content); + } + return; + } + + /** + * 解析模板中的include标签 + * @access private + * @param string $content 要解析的模板内容 + * @return void + */ + private function parseInclude(&$content) + { + $regex = $this->getRegex('include'); + $func = function ($template) use (&$func, &$regex, &$content) { + if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $array = $this->parseAttr($match[0]); + $file = $array['file']; + unset($array['file']); + // 分析模板文件名并读取内容 + $parseStr = $this->parseTemplateName($file); + foreach ($array as $k => $v) { + // 以$开头字符串转换成模板变量 + if (0 === strpos($v, '$')) { + $v = $this->get(substr($v, 1)); + } + $parseStr = str_replace('[' . $k . ']', $v, $parseStr); + } + $content = str_replace($match[0], $parseStr, $content); + // 再次对包含文件进行模板分析 + $func($parseStr); + } + unset($matches); + } + }; + // 替换模板中的include标签 + $func($content); + return; + } + + /** + * 解析模板中的extend标签 + * @access private + * @param string $content 要解析的模板内容 + * @return void + */ + private function parseExtend(&$content) + { + $regex = $this->getRegex('extend'); + $array = $blocks = $baseBlocks = []; + $extend = ''; + $func = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) { + if (preg_match($regex, $template, $matches)) { + if (!isset($array[$matches['name']])) { + $array[$matches['name']] = 1; + // 读取继承模板 + $extend = $this->parseTemplateName($matches['name']); + // 递归检查继承 + $func($extend); + // 取得block标签内容 + $blocks = array_merge($blocks, $this->parseBlock($template)); + return; + } + } else { + // 取得顶层模板block标签内容 + $baseBlocks = $this->parseBlock($template, true); + if (empty($extend)) { + // 无extend标签但有block标签的情况 + $extend = $template; + } + } + }; + + $func($content); + if (!empty($extend)) { + if ($baseBlocks) { + $children = []; + foreach ($baseBlocks as $name => $val) { + $replace = $val['content']; + if (!empty($children[$name])) { + // 如果包含有子block标签 + foreach ($children[$name] as $key) { + $replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace); + } + } + if (isset($blocks[$name])) { + // 带有{__block__}表示与所继承模板的相应标签合并,而不是覆盖 + $replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']); + if (!empty($val['parent'])) { + // 如果不是最顶层的block标签 + $parent = $val['parent']; + if (isset($blocks[$parent])) { + $blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']); + } + $blocks[$name]['content'] = $replace; + $children[$parent][] = $name; + continue; + } + } elseif (!empty($val['parent'])) { + // 如果子标签没有被继承则用原值 + $children[$val['parent']][] = $name; + $blocks[$name] = $val; + } + if (!$val['parent']) { + // 替换模板中的顶级block标签 + $extend = str_replace($val['begin'] . $val['content'] . $val['end'], $replace, $extend); + } + } + } + $content = $extend; + unset($blocks, $baseBlocks); + } + return; + } + + /** + * 替换页面中的literal标签 + * @access private + * @param string $content 模板内容 + * @param boolean $restore 是否为还原 + * @return void + */ + private function parseLiteral(&$content, $restore = false) + { + $regex = $this->getRegex($restore ? 'restoreliteral' : 'literal'); + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { + if (!$restore) { + $count = count($this->literal); + // 替换literal标签 + foreach ($matches as $match) { + $this->literal[] = substr($match[0], strlen($match[1]), -strlen($match[2])); + $content = str_replace($match[0], "", $content); + $count++; + } + } else { + // 还原literal标签 + foreach ($matches as $match) { + $content = str_replace($match[0], $this->literal[$match[1]], $content); + } + // 清空literal记录 + $this->literal = []; + } + unset($matches); + } + return; + } + + /** + * 获取模板中的block标签 + * @access private + * @param string $content 模板内容 + * @param boolean $sort 是否排序 + * @return array + */ + private function parseBlock(&$content, $sort = false) + { + $regex = $this->getRegex('block'); + $result = []; + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { + $right = $keys = []; + foreach ($matches as $match) { + if (empty($match['name'][0])) { + if (count($right) > 0) { + $tag = array_pop($right); + $start = $tag['offset'] + strlen($tag['tag']); + $length = $match[0][1] - $start; + $result[$tag['name']] = [ + 'begin' => $tag['tag'], + 'content' => substr($content, $start, $length), + 'end' => $match[0][0], + 'parent' => count($right) ? end($right)['name'] : '', + ]; + $keys[$tag['name']] = $match[0][1]; + } + } else { + // 标签头压入栈 + $right[] = [ + 'name' => $match[2][0], + 'offset' => $match[0][1], + 'tag' => $match[0][0], + ]; + } + } + unset($right, $matches); + if ($sort) { + // 按block标签结束符在模板中的位置排序 + array_multisort($keys, $result); + } + } + return $result; + } + + /** + * 搜索模板页面中包含的TagLib库 + * 并返回列表 + * @access private + * @param string $content 模板内容 + * @return array|null + */ + private function getIncludeTagLib(&$content) + { + // 搜索是否有TagLib标签 + if (preg_match($this->getRegex('taglib'), $content, $matches)) { + // 替换TagLib标签 + $content = str_replace($matches[0], '', $content); + return explode(',', $matches['name']); + } + return; + } + + /** + * TagLib库解析 + * @access public + * @param string $tagLib 要解析的标签库 + * @param string $content 要解析的模板内容 + * @param boolean $hide 是否隐藏标签库前缀 + * @return void + */ + public function parseTagLib($tagLib, &$content, $hide = false) + { + if (false !== strpos($tagLib, '\\')) { + // 支持指定标签库的命名空间 + $className = $tagLib; + $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); + } else { + $className = '\\think\\template\\taglib\\' . ucwords($tagLib); + } + /** @var Taglib $tLib */ + $tLib = new $className($this); + $tLib->parseTag($content, $hide ? '' : $tagLib); + return; + } + + /** + * 分析标签属性 + * @access public + * @param string $str 属性字符串 + * @param string $name 不为空时返回指定的属性名 + * @return array + */ + public function parseAttr($str, $name = null) + { + $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; + $array = []; + if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $array[$match['name']] = $match['value']; + } + unset($matches); + } + if (!empty($name) && isset($array[$name])) { + return $array[$name]; + } else { + return $array; + } + } + + /** + * 模板标签解析 + * 格式: {TagName:args [|content] } + * @access private + * @param string $content 要解析的模板内容 + * @return void + */ + private function parseTag(&$content) + { + $regex = $this->getRegex('tag'); + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $str = stripslashes($match[1]); + $flag = substr($str, 0, 1); + switch ($flag) { + case '$': + // 解析模板变量 格式 {$varName} + // 是否带有?号 + if (false !== $pos = strpos($str, '?')) { + $array = preg_split('/([!=]={1,2}|(?<]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE); + $name = $array[0]; + $this->parseVar($name); + $this->parseVarFunction($name); + + $str = trim(substr($str, $pos + 1)); + $this->parseVar($str); + $first = substr($str, 0, 1); + if (strpos($name, ')')) { + // $name为对象或是自动识别,或者含有函数 + if (isset($array[1])) { + $this->parseVar($array[2]); + $name .= $array[1] . $array[2]; + } + switch ($first) { + case '?': + $str = ''; + break; + case '=': + $str = ''; + break; + default: + $str = ''; + } + } else { + if (isset($array[1])) { + $this->parseVar($array[2]); + $express = $name . $array[1] . $array[2]; + } else { + $express = false; + } + // $name为数组 + switch ($first) { + case '?': + // {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx + $str = ''; + break; + case '=': + // {$varname?='xxx'} $varname为真时才输出xxx + $str = ''; + break; + case ':': + // {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx + $str = ''; + break; + default: + $str = ''; + } + } + } else { + $this->parseVar($str); + $this->parseVarFunction($str); + $str = ''; + } + break; + case ':': + // 输出某个函数的结果 + $str = substr($str, 1); + $this->parseVar($str); + $str = ''; + break; + case '~': + // 执行某个函数 + $str = substr($str, 1); + $this->parseVar($str); + $str = ''; + break; + case '-': + case '+': + // 输出计算 + $this->parseVar($str); + $str = ''; + break; + case '/': + // 注释标签 + $flag2 = substr($str, 1, 1); + if ('/' == $flag2 || ('*' == $flag2 && substr(rtrim($str), -2) == '*/')) { + $str = ''; + } + break; + default: + // 未识别的标签直接返回 + $str = $this->config['tpl_begin'] . $str . $this->config['tpl_end']; + break; + } + $content = str_replace($match[0], $str, $content); + } + unset($matches); + } + return; + } + + /** + * 模板变量解析,支持使用函数 + * 格式: {$varname|function1|function2=arg1,arg2} + * @access public + * @param string $varStr 变量数据 + * @return void + */ + public function parseVar(&$varStr) + { + $varStr = trim($varStr); + if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) { + static $_varParseList = []; + while ($matches[0]) { + $match = array_pop($matches[0]); + //如果已经解析过该变量字串,则直接返回变量值 + if (isset($_varParseList[$match[0]])) { + $parseStr = $_varParseList[$match[0]]; + } else { + if (strpos($match[0], '.')) { + $vars = explode('.', $match[0]); + $first = array_shift($vars); + if ('$Think' == $first) { + // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出 + $parseStr = $this->parseThinkVar($vars); + } elseif ('$Request' == $first) { + // 获取Request请求对象参数 + $method = array_shift($vars); + if (!empty($vars)) { + $params = implode('.', $vars); + if ('true' != $params) { + $params = '\'' . $params . '\''; + } + } else { + $params = ''; + } + $parseStr = '\think\Request::instance()->' . $method . '(' . $params . ')'; + } else { + switch ($this->config['tpl_var_identify']) { + case 'array': // 识别为数组 + $parseStr = $first . '[\'' . implode('\'][\'', $vars) . '\']'; + break; + case 'obj': // 识别为对象 + $parseStr = $first . '->' . implode('->', $vars); + break; + default: // 自动判断数组或对象 + $parseStr = '(is_array(' . $first . ')?' . $first . '[\'' . implode('\'][\'', $vars) . '\']:' . $first . '->' . implode('->', $vars) . ')'; + } + } + } else { + $parseStr = str_replace(':', '->', $match[0]); + } + $_varParseList[$match[0]] = $parseStr; + } + $varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0])); + } + unset($matches); + } + return; + } + + /** + * 对模板中使用了函数的变量进行解析 + * 格式 {$varname|function1|function2=arg1,arg2} + * @access public + * @param string $varStr 变量字符串 + * @return void + */ + public function parseVarFunction(&$varStr) + { + if (false == strpos($varStr, '|')) { + return; + } + static $_varFunctionList = []; + $_key = md5($varStr); + //如果已经解析过该变量字串,则直接返回变量值 + if (isset($_varFunctionList[$_key])) { + $varStr = $_varFunctionList[$_key]; + } else { + $varArray = explode('|', $varStr); + // 取得变量名称 + $name = array_shift($varArray); + // 对变量使用函数 + $length = count($varArray); + // 取得模板禁止使用函数列表 + $template_deny_funs = explode(',', $this->config['tpl_deny_func_list']); + for ($i = 0; $i < $length; $i++) { + $args = explode('=', $varArray[$i], 2); + // 模板函数过滤 + $fun = trim($args[0]); + switch ($fun) { + case 'default': // 特殊模板函数 + if (false === strpos($name, '(')) { + $name = '(isset(' . $name . ') && (' . $name . ' !== \'\')?' . $name . ':' . $args[1] . ')'; + } else { + $name = '(' . $name . ' ?: ' . $args[1] . ')'; + } + break; + default: // 通用模板函数 + if (!in_array($fun, $template_deny_funs)) { + if (isset($args[1])) { + if (strstr($args[1], '###')) { + $args[1] = str_replace('###', $name, $args[1]); + $name = "$fun($args[1])"; + } else { + $name = "$fun($name,$args[1])"; + } + } else { + if (!empty($args[0])) { + $name = "$fun($name)"; + } + } + } + } + } + $_varFunctionList[$_key] = $name; + $varStr = $name; + } + return; + } + + /** + * 特殊模板变量解析 + * 格式 以 $Think. 打头的变量属于特殊模板变量 + * @access public + * @param array $vars 变量数组 + * @return string + */ + public function parseThinkVar($vars) + { + $type = strtoupper(trim(array_shift($vars))); + $param = implode('.', $vars); + if ($vars) { + switch ($type) { + case 'SERVER': + $parseStr = '\\think\\Request::instance()->server(\'' . $param . '\')'; + break; + case 'GET': + $parseStr = '\\think\\Request::instance()->get(\'' . $param . '\')'; + break; + case 'POST': + $parseStr = '\\think\\Request::instance()->post(\'' . $param . '\')'; + break; + case 'COOKIE': + $parseStr = '\\think\\Cookie::get(\'' . $param . '\')'; + break; + case 'SESSION': + $parseStr = '\\think\\Session::get(\'' . $param . '\')'; + break; + case 'ENV': + $parseStr = '\\think\\Request::instance()->env(\'' . $param . '\')'; + break; + case 'REQUEST': + $parseStr = '\\think\\Request::instance()->request(\'' . $param . '\')'; + break; + case 'CONST': + $parseStr = strtoupper($param); + break; + case 'LANG': + $parseStr = '\\think\\Lang::get(\'' . $param . '\')'; + break; + case 'CONFIG': + $parseStr = '\\think\\Config::get(\'' . $param . '\')'; + break; + default: + $parseStr = '\'\''; + break; + } + } else { + switch ($type) { + case 'NOW': + $parseStr = "date('Y-m-d g:i a',time())"; + break; + case 'VERSION': + $parseStr = 'THINK_VERSION'; + break; + case 'LDELIM': + $parseStr = '\'' . ltrim($this->config['tpl_begin'], '\\') . '\''; + break; + case 'RDELIM': + $parseStr = '\'' . ltrim($this->config['tpl_end'], '\\') . '\''; + break; + default: + if (defined($type)) { + $parseStr = $type; + } else { + $parseStr = ''; + } + } + } + return $parseStr; + } + + /** + * 分析加载的模板文件并读取内容 支持多个模板文件读取 + * @access private + * @param string $templateName 模板文件名 + * @return string + */ + private function parseTemplateName($templateName) + { + $array = explode(',', $templateName); + $parseStr = ''; + foreach ($array as $templateName) { + if (empty($templateName)) { + continue; + } + if (0 === strpos($templateName, '$')) { + //支持加载变量文件名 + $templateName = $this->get(substr($templateName, 1)); + } + $template = $this->parseTemplateFile($templateName); + if ($template) { + // 获取模板文件内容 + $parseStr .= file_get_contents($template); + } + } + return $parseStr; + } + + /** + * 解析模板文件名 + * @access private + * @param string $template 文件名 + * @return string|false + */ + private function parseTemplateFile($template) + { + if ('' == pathinfo($template, PATHINFO_EXTENSION)) { + if (strpos($template, '@')) { + list($module, $template) = explode('@', $template); + } + if (0 !== strpos($template, '/')) { + $template = str_replace(['/', ':'], $this->config['view_depr'], $template); + } else { + $template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1)); + } + if ($this->config['view_base']) { + $module = isset($module) ? $module : Request::instance()->module(); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); + } else { + $path = isset($module) ? APP_PATH . $module . DS . basename($this->config['view_path']) . DS : $this->config['view_path']; + } + $template = realpath($path . $template . '.' . ltrim($this->config['view_suffix'], '.')); + } + + if (is_file($template)) { + // 记录模板文件的更新时间 + $this->includeFile[$template] = filemtime($template); + return $template; + } else { + throw new TemplateNotFoundException('template not exists:' . $template, $template); + } + } + + /** + * 按标签生成正则 + * @access private + * @param string $tagName 标签名 + * @return string + */ + private function getRegex($tagName) + { + $regex = ''; + if ('tag' == $tagName) { + $begin = $this->config['tpl_begin']; + $end = $this->config['tpl_end']; + if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) { + $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end; + } else { + $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end; + } + } else { + $begin = $this->config['taglib_begin']; + $end = $this->config['taglib_end']; + $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; + switch ($tagName) { + case 'block': + if ($single) { + $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end; + } else { + $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end; + } + break; + case 'literal': + if ($single) { + $regex = '(' . $begin . $tagName . '\b(?>[^' . $end . ']*)' . $end . ')'; + $regex .= '(?:(?>[^' . $begin . ']*)(?>(?!' . $begin . '(?>' . $tagName . '\b[^' . $end . ']*|\/' . $tagName . ')' . $end . ')' . $begin . '[^' . $begin . ']*)*)'; + $regex .= '(' . $begin . '\/' . $tagName . $end . ')'; + } else { + $regex = '(' . $begin . $tagName . '\b(?>(?:(?!' . $end . ').)*)' . $end . ')'; + $regex .= '(?:(?>(?:(?!' . $begin . ').)*)(?>(?!' . $begin . '(?>' . $tagName . '\b(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end . ')' . $begin . '(?>(?:(?!' . $begin . ').)*))*)'; + $regex .= '(' . $begin . '\/' . $tagName . $end . ')'; + } + break; + case 'restoreliteral': + $regex = ''; + break; + case 'include': + $name = 'file'; + case 'taglib': + case 'layout': + case 'extend': + if (empty($name)) { + $name = 'name'; + } + if ($single) { + $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end; + } else { + $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end; + } + break; + } + } + return '/' . $regex . '/is'; + } +} diff --git a/source/thinkphp/library/think/Url.php b/source/thinkphp/library/think/Url.php new file mode 100644 index 0000000..53a545f --- /dev/null +++ b/source/thinkphp/library/think/Url.php @@ -0,0 +1,333 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class Url +{ + // 生成URL地址的root + protected static $root; + protected static $bindCheck; + + /** + * URL生成 支持路由反射 + * @param string $url 路由地址 + * @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2'] + * @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值 + * @param boolean|string $domain 是否显示域名 或者直接传入域名 + * @return string + */ + public static function build($url = '', $vars = '', $suffix = true, $domain = false) + { + if (false === $domain && Route::rules('domain')) { + $domain = true; + } + // 解析URL + if (0 === strpos($url, '[') && $pos = strpos($url, ']')) { + // [name] 表示使用路由命名标识生成URL + $name = substr($url, 1, $pos - 1); + $url = 'name' . substr($url, $pos + 1); + } + if (false === strpos($url, '://') && 0 !== strpos($url, '/')) { + $info = parse_url($url); + $url = !empty($info['path']) ? $info['path'] : ''; + if (isset($info['fragment'])) { + // 解析锚点 + $anchor = $info['fragment']; + if (false !== strpos($anchor, '?')) { + // 解析参数 + list($anchor, $info['query']) = explode('?', $anchor, 2); + } + if (false !== strpos($anchor, '@')) { + // 解析域名 + list($anchor, $domain) = explode('@', $anchor, 2); + } + } elseif (strpos($url, '@') && false === strpos($url, '\\')) { + // 解析域名 + list($url, $domain) = explode('@', $url, 2); + } + } + + // 解析参数 + if (is_string($vars)) { + // aaa=1&bbb=2 转换成数组 + parse_str($vars, $vars); + } + + if ($url) { + $rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : '')); + if (is_null($rule) && isset($info['query'])) { + $rule = Route::name($url); + // 解析地址里面参数 合并到vars + parse_str($info['query'], $params); + $vars = array_merge($params, $vars); + unset($info['query']); + } + } + if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) { + // 匹配路由命名标识 + $url = $match[0]; + // 替换可选分隔符 + $url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url); + if (!empty($match[1])) { + $domain = $match[1]; + } + if (!is_null($match[2])) { + $suffix = $match[2]; + } + } elseif (!empty($rule) && isset($name)) { + throw new \InvalidArgumentException('route name not exists:' . $name); + } else { + // 检查别名路由 + $alias = Route::rules('alias'); + $matchAlias = false; + if ($alias) { + // 别名路由解析 + foreach ($alias as $key => $val) { + if (is_array($val)) { + $val = $val[0]; + } + if (0 === strpos($url, $val)) { + $url = $key . substr($url, strlen($val)); + $matchAlias = true; + break; + } + } + } + if (!$matchAlias) { + // 路由标识不存在 直接解析 + $url = self::parseUrl($url, $domain); + } + if (isset($info['query'])) { + // 解析地址里面参数 合并到vars + parse_str($info['query'], $params); + $vars = array_merge($params, $vars); + } + } + + // 检测URL绑定 + if (!self::$bindCheck) { + $type = Route::getBind('type'); + if ($type) { + $bind = Route::getBind($type); + if ($bind && 0 === strpos($url, $bind)) { + $url = substr($url, strlen($bind) + 1); + } + } + } + // 还原URL分隔符 + $depr = Config::get('pathinfo_depr'); + $url = str_replace('/', $depr, $url); + + // URL后缀 + $suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix); + // 锚点 + $anchor = !empty($anchor) ? '#' . $anchor : ''; + // 参数组装 + if (!empty($vars)) { + // 添加参数 + if (Config::get('url_common_param')) { + $vars = http_build_query($vars); + $url .= $suffix . '?' . $vars . $anchor; + } else { + $paramType = Config::get('url_param_type'); + foreach ($vars as $var => $val) { + if ('' !== trim($val)) { + if ($paramType) { + $url .= $depr . urlencode($val); + } else { + $url .= $depr . $var . $depr . urlencode($val); + } + } + } + $url .= $suffix . $anchor; + } + } else { + $url .= $suffix . $anchor; + } + // 检测域名 + $domain = self::parseDomain($url, $domain); + // URL组装 + $url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/'); + + self::$bindCheck = false; + return $url; + } + + // 直接解析URL地址 + protected static function parseUrl($url, &$domain) + { + $request = Request::instance(); + if (0 === strpos($url, '/')) { + // 直接作为路由地址解析 + $url = substr($url, 1); + } elseif (false !== strpos($url, '\\')) { + // 解析到类 + $url = ltrim(str_replace('\\', '/', $url), '/'); + } elseif (0 === strpos($url, '@')) { + // 解析到控制器 + $url = substr($url, 1); + } else { + // 解析到 模块/控制器/操作 + $module = $request->module(); + $domains = Route::rules('domain'); + if (true === $domain && 2 == substr_count($url, '/')) { + $current = $request->host(); + $match = []; + $pos = []; + foreach ($domains as $key => $item) { + if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) { + $pos[$key] = strlen($item['[bind]'][0]) + 1; + $match[] = $key; + $module = ''; + } + } + if ($match) { + $domain = current($match); + foreach ($match as $item) { + if (0 === strpos($current, $item)) { + $domain = $item; + } + } + self::$bindCheck = true; + $url = substr($url, $pos[$domain]); + } + } elseif ($domain) { + if (isset($domains[$domain]['[bind]'][0])) { + $bindModule = $domains[$domain]['[bind]'][0]; + if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) { + $module = ''; + } + } + } + $module = $module ? $module . '/' : ''; + + $controller = $request->controller(); + if ('' == $url) { + // 空字符串输出当前的 模块/控制器/操作 + $action = $request->action(); + } else { + $path = explode('/', $url); + $action = array_pop($path); + $controller = empty($path) ? $controller : array_pop($path); + $module = empty($path) ? $module : array_pop($path) . '/'; + } + if (Config::get('url_convert')) { + $action = strtolower($action); + $controller = Loader::parseName($controller); + } + $url = $module . $controller . '/' . $action; + } + return $url; + } + + // 检测域名 + protected static function parseDomain(&$url, $domain) + { + if (!$domain) { + return ''; + } + $request = Request::instance(); + $rootDomain = Config::get('url_domain_root'); + if (true === $domain) { + // 自动判断域名 + $domain = Config::get('app_host') ?: $request->host(); + + $domains = Route::rules('domain'); + if ($domains) { + $route_domain = array_keys($domains); + foreach ($route_domain as $domain_prefix) { + if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) { + foreach ($domains as $key => $rule) { + $rule = is_array($rule) ? $rule[0] : $rule; + if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) { + $url = ltrim($url, $rule); + $domain = $key; + // 生成对应子域名 + if (!empty($rootDomain)) { + $domain .= $rootDomain; + } + break; + } elseif (false !== strpos($key, '*')) { + if (!empty($rootDomain)) { + $domain .= $rootDomain; + } + break; + } + } + } + } + } + + } else { + if (empty($rootDomain)) { + $host = Config::get('app_host') ?: $request->host(); + $rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host; + } + if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) { + $domain .= '.' . $rootDomain; + } + } + if (false !== strpos($domain, '://')) { + $scheme = ''; + } else { + $scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://'; + } + return $scheme . $domain; + } + + // 解析URL后缀 + protected static function parseSuffix($suffix) + { + if ($suffix) { + $suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix; + if ($pos = strpos($suffix, '|')) { + $suffix = substr($suffix, 0, $pos); + } + } + return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix; + } + + // 匹配路由地址 + public static function getRuleUrl($rule, &$vars = []) + { + foreach ($rule as $item) { + list($url, $pattern, $domain, $suffix) = $item; + if (empty($pattern)) { + return [rtrim($url, '$'), $domain, $suffix]; + } + $type = Config::get('url_common_param'); + foreach ($pattern as $key => $val) { + if (isset($vars[$key])) { + $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url); + unset($vars[$key]); + $result = [$url, $domain, $suffix]; + } elseif (2 == $val) { + $url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url); + $result = [$url, $domain, $suffix]; + } else { + break; + } + } + if (isset($result)) { + return $result; + } + } + return false; + } + + // 指定当前生成URL地址的root + public static function root($root) + { + self::$root = $root; + Request::instance()->root($root); + } +} diff --git a/source/thinkphp/library/think/Validate.php b/source/thinkphp/library/think/Validate.php new file mode 100644 index 0000000..608e1e4 --- /dev/null +++ b/source/thinkphp/library/think/Validate.php @@ -0,0 +1,1371 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +use think\exception\ClassNotFoundException; + +class Validate +{ + // 实例 + protected static $instance; + + // 自定义的验证类型 + protected static $type = []; + + // 验证类型别名 + protected $alias = [ + '>' => 'gt', '>=' => 'egt', '<' => 'lt', '<=' => 'elt', '=' => 'eq', 'same' => 'eq', + ]; + + // 当前验证的规则 + protected $rule = []; + + // 验证提示信息 + protected $message = []; + // 验证字段描述 + protected $field = []; + + // 验证规则默认提示信息 + protected static $typeMsg = [ + 'require' => ':attribute require', + 'number' => ':attribute must be numeric', + 'integer' => ':attribute must be integer', + 'float' => ':attribute must be float', + 'boolean' => ':attribute must be bool', + 'email' => ':attribute not a valid email address', + 'array' => ':attribute must be a array', + 'accepted' => ':attribute must be yes,on or 1', + 'date' => ':attribute not a valid datetime', + 'file' => ':attribute not a valid file', + 'image' => ':attribute not a valid image', + 'alpha' => ':attribute must be alpha', + 'alphaNum' => ':attribute must be alpha-numeric', + 'alphaDash' => ':attribute must be alpha-numeric, dash, underscore', + 'activeUrl' => ':attribute not a valid domain or ip', + 'chs' => ':attribute must be chinese', + 'chsAlpha' => ':attribute must be chinese or alpha', + 'chsAlphaNum' => ':attribute must be chinese,alpha-numeric', + 'chsDash' => ':attribute must be chinese,alpha-numeric,underscore, dash', + 'url' => ':attribute not a valid url', + 'ip' => ':attribute not a valid ip', + 'dateFormat' => ':attribute must be dateFormat of :rule', + 'in' => ':attribute must be in :rule', + 'notIn' => ':attribute be notin :rule', + 'between' => ':attribute must between :1 - :2', + 'notBetween' => ':attribute not between :1 - :2', + 'length' => 'size of :attribute must be :rule', + 'max' => 'max size of :attribute must be :rule', + 'min' => 'min size of :attribute must be :rule', + 'after' => ':attribute cannot be less than :rule', + 'before' => ':attribute cannot exceed :rule', + 'afterWith' => ':attribute cannot be less than :rule', + 'beforeWith' => ':attribute cannot exceed :rule', + 'expire' => ':attribute not within :rule', + 'allowIp' => 'access IP is not allowed', + 'denyIp' => 'access IP denied', + 'confirm' => ':attribute out of accord with :2', + 'different' => ':attribute cannot be same with :2', + 'egt' => ':attribute must greater than or equal :rule', + 'gt' => ':attribute must greater than :rule', + 'elt' => ':attribute must less than or equal :rule', + 'lt' => ':attribute must less than :rule', + 'eq' => ':attribute must equal :rule', + 'unique' => ':attribute has exists', + 'regex' => ':attribute not conform to the rules', + 'method' => 'invalid Request method', + 'token' => 'invalid token', + 'fileSize' => 'filesize not match', + 'fileExt' => 'extensions to upload is not allowed', + 'fileMime' => 'mimetype to upload is not allowed', + ]; + + // 当前验证场景 + protected $currentScene = null; + + // 正则表达式 regex = ['zip'=>'\d{6}',...] + protected $regex = []; + + // 验证场景 scene = ['edit'=>'name1,name2,...'] + protected $scene = []; + + // 验证失败错误信息 + protected $error = []; + + // 批量验证 + protected $batch = false; + + /** + * 构造函数 + * @access public + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 + */ + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->rule = array_merge($this->rule, $rules); + $this->message = array_merge($this->message, $message); + $this->field = array_merge($this->field, $field); + } + + /** + * 实例化验证 + * @access public + * @param array $rules 验证规则 + * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 + * @return Validate + */ + public static function make($rules = [], $message = [], $field = []) + { + if (is_null(self::$instance)) { + self::$instance = new self($rules, $message, $field); + } + return self::$instance; + } + + /** + * 添加字段验证规则 + * @access protected + * @param string|array $name 字段名称或者规则数组 + * @param mixed $rule 验证规则 + * @return Validate + */ + public function rule($name, $rule = '') + { + if (is_array($name)) { + $this->rule = array_merge($this->rule, $name); + } else { + $this->rule[$name] = $rule; + } + return $this; + } + + /** + * 注册验证(类型)规则 + * @access public + * @param string $type 验证规则类型 + * @param mixed $callback callback方法(或闭包) + * @return void + */ + public static function extend($type, $callback = null) + { + if (is_array($type)) { + self::$type = array_merge(self::$type, $type); + } else { + self::$type[$type] = $callback; + } + } + + /** + * 设置验证规则的默认提示信息 + * @access protected + * @param string|array $type 验证规则类型名称或者数组 + * @param string $msg 验证提示信息 + * @return void + */ + public static function setTypeMsg($type, $msg = null) + { + if (is_array($type)) { + self::$typeMsg = array_merge(self::$typeMsg, $type); + } else { + self::$typeMsg[$type] = $msg; + } + } + + /** + * 设置提示信息 + * @access public + * @param string|array $name 字段名称 + * @param string $message 提示信息 + * @return Validate + */ + public function message($name, $message = '') + { + if (is_array($name)) { + $this->message = array_merge($this->message, $name); + } else { + $this->message[$name] = $message; + } + return $this; + } + + /** + * 设置验证场景 + * @access public + * @param string|array $name 场景名或者场景设置数组 + * @param mixed $fields 要验证的字段 + * @return Validate + */ + public function scene($name, $fields = null) + { + if (is_array($name)) { + $this->scene = array_merge($this->scene, $name); + }if (is_null($fields)) { + // 设置当前场景 + $this->currentScene = $name; + } else { + // 设置验证场景 + $this->scene[$name] = $fields; + } + return $this; + } + + /** + * 判断是否存在某个验证场景 + * @access public + * @param string $name 场景名 + * @return bool + */ + public function hasScene($name) + { + return isset($this->scene[$name]); + } + + /** + * 设置批量验证 + * @access public + * @param bool $batch 是否批量验证 + * @return Validate + */ + public function batch($batch = true) + { + $this->batch = $batch; + return $this; + } + + /** + * 数据自动验证 + * @access public + * @param array $data 数据 + * @param mixed $rules 验证规则 + * @param string $scene 验证场景 + * @return bool + */ + public function check($data, $rules = [], $scene = '') + { + $this->error = []; + + if (empty($rules)) { + // 读取验证规则 + $rules = $this->rule; + } + + // 分析验证规则 + $scene = $this->getScene($scene); + if (is_array($scene)) { + // 处理场景验证字段 + $change = []; + $array = []; + foreach ($scene as $k => $val) { + if (is_numeric($k)) { + $array[] = $val; + } else { + $array[] = $k; + $change[$k] = $val; + } + } + } + + foreach ($rules as $key => $item) { + // field => rule1|rule2... field=>['rule1','rule2',...] + if (is_numeric($key)) { + // [field,rule1|rule2,msg1|msg2] + $key = $item[0]; + $rule = $item[1]; + if (isset($item[2])) { + $msg = is_string($item[2]) ? explode('|', $item[2]) : $item[2]; + } else { + $msg = []; + } + } else { + $rule = $item; + $msg = []; + } + if (strpos($key, '|')) { + // 字段|描述 用于指定属性名称 + list($key, $title) = explode('|', $key); + } else { + $title = isset($this->field[$key]) ? $this->field[$key] : $key; + } + + // 场景检测 + if (!empty($scene)) { + if ($scene instanceof \Closure && !call_user_func_array($scene, [$key, $data])) { + continue; + } elseif (is_array($scene)) { + if (!in_array($key, $array)) { + continue; + } elseif (isset($change[$key])) { + // 重载某个验证规则 + $rule = $change[$key]; + } + } + } + + // 获取数据 支持二维数组 + $value = $this->getDataValue($data, $key); + + // 字段验证 + if ($rule instanceof \Closure) { + // 匿名函数验证 支持传入当前字段和所有字段两个数据 + $result = call_user_func_array($rule, [$value, $data]); + } else { + $result = $this->checkItem($key, $value, $rule, $data, $title, $msg); + } + + if (true !== $result) { + // 没有返回true 则表示验证失败 + if (!empty($this->batch)) { + // 批量验证 + if (is_array($result)) { + $this->error = array_merge($this->error, $result); + } else { + $this->error[$key] = $result; + } + } else { + $this->error = $result; + return false; + } + } + } + return !empty($this->error) ? false : true; + } + + /** + * 根据验证规则验证数据 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @return bool + */ + protected function checkRule($value, $rules) + { + if ($rules instanceof \Closure) { + return call_user_func_array($rules, [$value]); + } elseif (is_string($rules)) { + $rules = explode('|', $rules); + } + + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value]); + } else { + // 判断验证类型 + list($type, $rule) = $this->getValidateType($key, $rule); + + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + + $result = call_user_func_array($callback, [$value, $rule]); + } + + if (true !== $result) { + return $result; + } + } + + return true; + } + + /** + * 验证单个字段规则 + * @access protected + * @param string $field 字段名 + * @param mixed $value 字段值 + * @param mixed $rules 验证规则 + * @param array $data 数据 + * @param string $title 字段描述 + * @param array $msg 提示信息 + * @return mixed + */ + protected function checkItem($field, $value, $rules, $data, $title = '', $msg = []) + { + // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...] + if (is_string($rules)) { + $rules = explode('|', $rules); + } + $i = 0; + foreach ($rules as $key => $rule) { + if ($rule instanceof \Closure) { + $result = call_user_func_array($rule, [$value, $data]); + $info = is_numeric($key) ? '' : $key; + } else { + // 判断验证类型 + list($type, $rule, $info) = $this->getValidateType($key, $rule); + + // 如果不是require 有数据才会行验证 + if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) { + // 验证类型 + $callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type]; + // 验证数据 + $result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]); + } else { + $result = true; + } + } + + if (false === $result) { + // 验证失败 返回错误信息 + if (isset($msg[$i])) { + $message = $msg[$i]; + if (is_string($message) && strpos($message, '{%') === 0) { + $message = Lang::get(substr($message, 2, -1)); + } + } else { + $message = $this->getRuleMsg($field, $title, $info, $rule); + } + return $message; + } elseif (true !== $result) { + // 返回自定义错误信息 + if (is_string($result) && false !== strpos($result, ':')) { + $result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result); + } + return $result; + } + $i++; + } + return $result; + } + + /** + * 获取当前验证类型及规则 + * @access public + * @param mixed $key + * @param mixed $rule + * @return array + */ + protected function getValidateType($key, $rule) + { + // 判断验证类型 + if (!is_numeric($key)) { + return [$key, $rule, $key]; + } + + if (strpos($rule, ':')) { + list($type, $rule) = explode(':', $rule, 2); + if (isset($this->alias[$type])) { + // 判断别名 + $type = $this->alias[$type]; + } + $info = $type; + } elseif (method_exists($this, $rule)) { + $type = $rule; + $info = $rule; + $rule = ''; + } else { + $type = 'is'; + $info = $rule; + } + + return [$type, $rule, $info]; + } + + /** + * 验证是否和某个字段的值一致 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @param string $field 字段名 + * @return bool + */ + protected function confirm($value, $rule, $data, $field = '') + { + if ('' == $rule) { + if (strpos($field, '_confirm')) { + $rule = strstr($field, '_confirm', true); + } else { + $rule = $field . '_confirm'; + } + } + return $this->getDataValue($data, $rule) === $value; + } + + /** + * 验证是否和某个字段的值是否不同 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function different($value, $rule, $data) + { + return $this->getDataValue($data, $rule) != $value; + } + + /** + * 验证是否大于等于某个值 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function egt($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + return !is_null($val) && $value >= $val; + } + + /** + * 验证是否大于某个值 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function gt($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + return !is_null($val) && $value > $val; + } + + /** + * 验证是否小于等于某个值 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function elt($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + return !is_null($val) && $value <= $val; + } + + /** + * 验证是否小于某个值 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function lt($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + return !is_null($val) && $value < $val; + } + + /** + * 验证是否等于某个值 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function eq($value, $rule) + { + return $value == $rule; + } + + /** + * 验证字段值是否为有效格式 + * @access protected + * @param mixed $value 字段值 + * @param string $rule 验证规则 + * @param array $data 验证数据 + * @return bool + */ + protected function is($value, $rule, $data = []) + { + switch ($rule) { + case 'require': + // 必须 + $result = !empty($value) || '0' == $value; + break; + case 'accepted': + // 接受 + $result = in_array($value, ['1', 'on', 'yes']); + break; + case 'date': + // 是否是一个有效日期 + $result = false !== strtotime($value); + break; + case 'alpha': + // 只允许字母 + $result = $this->regex($value, '/^[A-Za-z]+$/'); + break; + case 'alphaNum': + // 只允许字母和数字 + $result = $this->regex($value, '/^[A-Za-z0-9]+$/'); + break; + case 'alphaDash': + // 只允许字母、数字和下划线 破折号 + $result = $this->regex($value, '/^[A-Za-z0-9\-\_]+$/'); + break; + case 'chs': + // 只允许汉字 + $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}]+$/u'); + break; + case 'chsAlpha': + // 只允许汉字、字母 + $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u'); + break; + case 'chsAlphaNum': + // 只允许汉字、字母和数字 + $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9]+$/u'); + break; + case 'chsDash': + // 只允许汉字、字母、数字和下划线_及破折号- + $result = $this->regex($value, '/^[\x{4e00}-\x{9fa5}a-zA-Z0-9\_\-]+$/u'); + break; + case 'activeUrl': + // 是否为有效的网址 + $result = checkdnsrr($value); + break; + case 'ip': + // 是否为IP地址 + $result = $this->filter($value, [FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6]); + break; + case 'url': + // 是否为一个URL地址 + $result = $this->filter($value, FILTER_VALIDATE_URL); + break; + case 'float': + // 是否为float + $result = $this->filter($value, FILTER_VALIDATE_FLOAT); + break; + case 'number': + $result = is_numeric($value); + break; + case 'integer': + // 是否为整型 + $result = $this->filter($value, FILTER_VALIDATE_INT); + break; + case 'email': + // 是否为邮箱地址 + $result = $this->filter($value, FILTER_VALIDATE_EMAIL); + break; + case 'boolean': + // 是否为布尔值 + $result = in_array($value, [true, false, 0, 1, '0', '1'], true); + break; + case 'array': + // 是否为数组 + $result = is_array($value); + break; + case 'file': + $result = $value instanceof File; + break; + case 'image': + $result = $value instanceof File && in_array($this->getImageType($value->getRealPath()), [1, 2, 3, 6]); + break; + case 'token': + $result = $this->token($value, '__token__', $data); + break; + default: + if (isset(self::$type[$rule])) { + // 注册的验证规则 + $result = call_user_func_array(self::$type[$rule], [$value]); + } else { + // 正则验证 + $result = $this->regex($value, $rule); + } + } + return $result; + } + + // 判断图像类型 + protected function getImageType($image) + { + if (function_exists('exif_imagetype')) { + return exif_imagetype($image); + } else { + try { + $info = getimagesize($image); + return $info ? $info[2] : false; + } catch (\Exception $e) { + return false; + } + } + } + + /** + * 验证是否为合格的域名或者IP 支持A,MX,NS,SOA,PTR,CNAME,AAAA,A6, SRV,NAPTR,TXT 或者 ANY类型 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function activeUrl($value, $rule) + { + if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) { + $rule = 'MX'; + } + return checkdnsrr($value, $rule); + } + + /** + * 验证是否有效IP + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 ipv4 ipv6 + * @return bool + */ + protected function ip($value, $rule) + { + if (!in_array($rule, ['ipv4', 'ipv6'])) { + $rule = 'ipv4'; + } + return $this->filter($value, [FILTER_VALIDATE_IP, 'ipv6' == $rule ? FILTER_FLAG_IPV6 : FILTER_FLAG_IPV4]); + } + + /** + * 验证上传文件后缀 + * @access protected + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function fileExt($file, $rule) + { + if (is_array($file)) { + foreach ($file as $item) { + if (!($item instanceof File) || !$item->checkExt($rule)) { + return false; + } + } + return true; + } elseif ($file instanceof File) { + return $file->checkExt($rule); + } else { + return false; + } + } + + /** + * 验证上传文件类型 + * @access protected + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function fileMime($file, $rule) + { + if (is_array($file)) { + foreach ($file as $item) { + if (!($item instanceof File) || !$item->checkMime($rule)) { + return false; + } + } + return true; + } elseif ($file instanceof File) { + return $file->checkMime($rule); + } else { + return false; + } + } + + /** + * 验证上传文件大小 + * @access protected + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function fileSize($file, $rule) + { + if (is_array($file)) { + foreach ($file as $item) { + if (!($item instanceof File) || !$item->checkSize($rule)) { + return false; + } + } + return true; + } elseif ($file instanceof File) { + return $file->checkSize($rule); + } else { + return false; + } + } + + /** + * 验证图片的宽高及类型 + * @access protected + * @param mixed $file 上传文件 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function image($file, $rule) + { + if (!($file instanceof File)) { + return false; + } + if ($rule) { + $rule = explode(',', $rule); + list($width, $height, $type) = getimagesize($file->getRealPath()); + if (isset($rule[2])) { + $imageType = strtolower($rule[2]); + if ('jpeg' == $imageType) { + $imageType = 'jpg'; + } + if (image_type_to_extension($type, false) != $imageType) { + return false; + } + } + + list($w, $h) = $rule; + return $w == $width && $h == $height; + } else { + return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]); + } + } + + /** + * 验证请求类型 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function method($value, $rule) + { + $method = Request::instance()->method(); + return strtoupper($rule) == $method; + } + + /** + * 验证时间和日期是否符合指定格式 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function dateFormat($value, $rule) + { + $info = date_parse_from_format($rule, $value); + return 0 == $info['warning_count'] && 0 == $info['error_count']; + } + + /** + * 验证是否唯一 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名 + * @param array $data 数据 + * @param string $field 验证字段名 + * @return bool + */ + protected function unique($value, $rule, $data, $field) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + if (false !== strpos($rule[0], '\\')) { + // 指定模型类 + $db = new $rule[0]; + } else { + try { + $db = Loader::model($rule[0]); + } catch (ClassNotFoundException $e) { + $db = Db::name($rule[0]); + } + } + $key = isset($rule[1]) ? $rule[1] : $field; + + if (strpos($key, '^')) { + // 支持多个字段验证 + $fields = explode('^', $key); + foreach ($fields as $key) { + if (isset($data[$key])) { + $map[$key] = $data[$key]; + } + } + } elseif (strpos($key, '=')) { + parse_str($key, $map); + } elseif (isset($data[$field])) { + $map[$key] = $data[$field]; + } else { + $map = []; + } + + $pk = isset($rule[3]) ? $rule[3] : $db->getPk(); + if (is_string($pk)) { + if (isset($rule[2])) { + $map[$pk] = ['neq', $rule[2]]; + } elseif (isset($data[$pk])) { + $map[$pk] = ['neq', $data[$pk]]; + } + } + if ($db->where($map)->field($pk)->find()) { + return false; + } + return true; + } + + /** + * 使用行为类验证 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return mixed + */ + protected function behavior($value, $rule, $data) + { + return Hook::exec($rule, '', $data); + } + + /** + * 使用filter_var方式验证 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function filter($value, $rule) + { + if (is_string($rule) && strpos($rule, ',')) { + list($rule, $param) = explode(',', $rule); + } elseif (is_array($rule)) { + $param = isset($rule[1]) ? $rule[1] : null; + $rule = $rule[0]; + } else { + $param = null; + } + return false !== filter_var($value, is_int($rule) ? $rule : filter_id($rule), $param); + } + + /** + * 验证某个字段等于某个值的时候必须 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function requireIf($value, $rule, $data) + { + list($field, $val) = explode(',', $rule); + if ($this->getDataValue($data, $field) == $val) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 通过回调方法验证某个字段是否必须 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function requireCallback($value, $rule, $data) + { + $result = call_user_func_array($rule, [$value, $data]); + if ($result) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 验证某个字段有值的情况下必须 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function requireWith($value, $rule, $data) + { + $val = $this->getDataValue($data, $rule); + if (!empty($val)) { + return !empty($value) || '0' == $value; + } else { + return true; + } + } + + /** + * 验证是否在范围内 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function in($value, $rule) + { + return in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 验证是否不在某个范围 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function notIn($value, $rule) + { + return !in_array($value, is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * between验证数据 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function between($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + list($min, $max) = $rule; + return $value >= $min && $value <= $max; + } + + /** + * 使用notbetween验证数据 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function notBetween($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + list($min, $max) = $rule; + return $value < $min || $value > $max; + } + + /** + * 验证数据长度 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function length($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string) $value); + } + + if (strpos($rule, ',')) { + // 长度区间 + list($min, $max) = explode(',', $rule); + return $length >= $min && $length <= $max; + } else { + // 指定长度 + return $length == $rule; + } + } + + /** + * 验证数据最大长度 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function max($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string) $value); + } + return $length <= $rule; + } + + /** + * 验证数据最小长度 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function min($value, $rule) + { + if (is_array($value)) { + $length = count($value); + } elseif ($value instanceof File) { + $length = $value->getSize(); + } else { + $length = mb_strlen((string) $value); + } + return $length >= $rule; + } + + /** + * 验证日期 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function after($value, $rule, $data) + { + return strtotime($value) >= strtotime($rule); + } + + /** + * 验证日期 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function before($value, $rule, $data) + { + return strtotime($value) <= strtotime($rule); + } + + /** + * 验证日期字段 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function afterWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) >= strtotime($rule); + } + + /** + * 验证日期字段 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function beforeWith($value, $rule, $data) + { + $rule = $this->getDataValue($data, $rule); + return !is_null($rule) && strtotime($value) <= strtotime($rule); + } + + /** + * 验证有效期 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @return bool + */ + protected function expire($value, $rule) + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + list($start, $end) = $rule; + if (!is_numeric($start)) { + $start = strtotime($start); + } + + if (!is_numeric($end)) { + $end = strtotime($end); + } + return $_SERVER['REQUEST_TIME'] >= $start && $_SERVER['REQUEST_TIME'] <= $end; + } + + /** + * 验证IP许可 + * @access protected + * @param string $value 字段值 + * @param mixed $rule 验证规则 + * @return mixed + */ + protected function allowIp($value, $rule) + { + return in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 验证IP禁用 + * @access protected + * @param string $value 字段值 + * @param mixed $rule 验证规则 + * @return mixed + */ + protected function denyIp($value, $rule) + { + return !in_array($_SERVER['REMOTE_ADDR'], is_array($rule) ? $rule : explode(',', $rule)); + } + + /** + * 使用正则验证数据 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 正则规则或者预定义正则名 + * @return mixed + */ + protected function regex($value, $rule) + { + if (isset($this->regex[$rule])) { + $rule = $this->regex[$rule]; + } + if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) { + // 不是正则表达式则两端补上/ + $rule = '/^' . $rule . '$/'; + } + return is_scalar($value) && 1 === preg_match($rule, (string) $value); + } + + /** + * 验证表单令牌 + * @access protected + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 + * @param array $data 数据 + * @return bool + */ + protected function token($value, $rule, $data) + { + $rule = !empty($rule) ? $rule : '__token__'; + if (!isset($data[$rule]) || !Session::has($rule)) { + // 令牌数据无效 + return false; + } + + // 令牌验证 + if (isset($data[$rule]) && Session::get($rule) === $data[$rule]) { + // 防止重复提交 + Session::delete($rule); // 验证完成销毁session + return true; + } + // 开启TOKEN重置 + Session::delete($rule); + return false; + } + + // 获取错误信息 + public function getError() + { + return $this->error; + } + + /** + * 获取数据值 + * @access protected + * @param array $data 数据 + * @param string $key 数据标识 支持二维 + * @return mixed + */ + protected function getDataValue($data, $key) + { + if (is_numeric($key)) { + $value = $key; + } elseif (strpos($key, '.')) { + // 支持二维数组验证 + list($name1, $name2) = explode('.', $key); + $value = isset($data[$name1][$name2]) ? $data[$name1][$name2] : null; + } else { + $value = isset($data[$key]) ? $data[$key] : null; + } + return $value; + } + + /** + * 获取验证规则的错误提示信息 + * @access protected + * @param string $attribute 字段英文名 + * @param string $title 字段描述名 + * @param string $type 验证规则名称 + * @param mixed $rule 验证规则数据 + * @return string + */ + protected function getRuleMsg($attribute, $title, $type, $rule) + { + if (isset($this->message[$attribute . '.' . $type])) { + $msg = $this->message[$attribute . '.' . $type]; + } elseif (isset($this->message[$attribute][$type])) { + $msg = $this->message[$attribute][$type]; + } elseif (isset($this->message[$attribute])) { + $msg = $this->message[$attribute]; + } elseif (isset(self::$typeMsg[$type])) { + $msg = self::$typeMsg[$type]; + } elseif (0 === strpos($type, 'require')) { + $msg = self::$typeMsg['require']; + } else { + $msg = $title . Lang::get('not conform to the rules'); + } + + if (is_string($msg) && 0 === strpos($msg, '{%')) { + $msg = Lang::get(substr($msg, 2, -1)); + } elseif (Lang::has($msg)) { + $msg = Lang::get($msg); + } + + if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) { + // 变量替换 + if (is_string($rule) && strpos($rule, ',')) { + $array = array_pad(explode(',', $rule), 3, ''); + } else { + $array = array_pad([], 3, ''); + } + $msg = str_replace( + [':attribute', ':rule', ':1', ':2', ':3'], + [$title, (string) $rule, $array[0], $array[1], $array[2]], + $msg); + } + return $msg; + } + + /** + * 获取数据验证的场景 + * @access protected + * @param string $scene 验证场景 + * @return array + */ + protected function getScene($scene = '') + { + if (empty($scene)) { + // 读取指定场景 + $scene = $this->currentScene; + } + + if (!empty($scene) && isset($this->scene[$scene])) { + // 如果设置了验证适用场景 + $scene = $this->scene[$scene]; + if (is_string($scene)) { + $scene = explode(',', $scene); + } + } else { + $scene = []; + } + return $scene; + } + + public static function __callStatic($method, $params) + { + $class = self::make(); + if (method_exists($class, $method)) { + return call_user_func_array([$class, $method], $params); + } else { + throw new \BadMethodCallException('method not exists:' . __CLASS__ . '->' . $method); + } + } +} diff --git a/source/thinkphp/library/think/View.php b/source/thinkphp/library/think/View.php new file mode 100644 index 0000000..ca2dadb --- /dev/null +++ b/source/thinkphp/library/think/View.php @@ -0,0 +1,239 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +class View +{ + // 视图实例 + protected static $instance; + // 模板引擎实例 + public $engine; + // 模板变量 + protected $data = []; + // 用于静态赋值的模板变量 + protected static $var = []; + // 视图输出替换 + protected $replace = []; + + /** + * 构造函数 + * @access public + * @param array $engine 模板引擎参数 + * @param array $replace 字符串替换参数 + */ + public function __construct($engine = [], $replace = []) + { + // 初始化模板引擎 + $this->engine($engine); + // 基础替换字符串 + $request = Request::instance(); + $base = $request->root(); + $root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base; + if ('' != $root) { + $root = '/' . ltrim($root, '/'); + } + $baseReplace = [ + '__ROOT__' => $root, + '__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()), + '__STATIC__' => $root . '/static', + '__CSS__' => $root . '/static/css', + '__JS__' => $root . '/static/js', + ]; + $this->replace = array_merge($baseReplace, (array) $replace); + } + + /** + * 初始化视图 + * @access public + * @param array $engine 模板引擎参数 + * @param array $replace 字符串替换参数 + * @return object + */ + public static function instance($engine = [], $replace = []) + { + if (is_null(self::$instance)) { + self::$instance = new self($engine, $replace); + } + return self::$instance; + } + + /** + * 模板变量静态赋值 + * @access public + * @param mixed $name 变量名 + * @param mixed $value 变量值 + * @return void + */ + public static function share($name, $value = '') + { + if (is_array($name)) { + self::$var = array_merge(self::$var, $name); + } else { + self::$var[$name] = $value; + } + } + + /** + * 模板变量赋值 + * @access public + * @param mixed $name 变量名 + * @param mixed $value 变量值 + * @return $this + */ + public function assign($name, $value = '') + { + if (is_array($name)) { + $this->data = array_merge($this->data, $name); + } else { + $this->data[$name] = $value; + } + return $this; + } + + /** + * 设置当前模板解析的引擎 + * @access public + * @param array|string $options 引擎参数 + * @return $this + */ + public function engine($options = []) + { + if (is_string($options)) { + $type = $options; + $options = []; + } else { + $type = !empty($options['type']) ? $options['type'] : 'Think'; + } + + $class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type); + if (isset($options['type'])) { + unset($options['type']); + } + $this->engine = new $class($options); + return $this; + } + + /** + * 配置模板引擎 + * @access private + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @return $this + */ + public function config($name, $value = null) + { + $this->engine->config($name, $value); + return $this; + } + + /** + * 解析和获取模板内容 用于输出 + * @param string $template 模板文件名或者内容 + * @param array $vars 模板输出变量 + * @param array $replace 替换内容 + * @param array $config 模板参数 + * @param bool $renderContent 是否渲染内容 + * @return string + * @throws Exception + */ + public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false) + { + // 模板变量 + $vars = array_merge(self::$var, $this->data, $vars); + + // 页面缓存 + ob_start(); + ob_implicit_flush(0); + + // 渲染输出 + try { + $method = $renderContent ? 'display' : 'fetch'; + // 允许用户自定义模板的字符串替换 + $replace = array_merge($this->replace, $replace, (array) $this->engine->config('tpl_replace_string')); + $this->engine->config('tpl_replace_string', $replace); + $this->engine->$method($template, $vars, $config); + } catch (\Exception $e) { + ob_end_clean(); + throw $e; + } + + // 获取并清空缓存 + $content = ob_get_clean(); + // 内容过滤标签 + Hook::listen('view_filter', $content); + return $content; + } + + /** + * 视图内容替换 + * @access public + * @param string|array $content 被替换内容(支持批量替换) + * @param string $replace 替换内容 + * @return $this + */ + public function replace($content, $replace = '') + { + if (is_array($content)) { + $this->replace = array_merge($this->replace, $content); + } else { + $this->replace[$content] = $replace; + } + return $this; + } + + /** + * 渲染内容输出 + * @access public + * @param string $content 内容 + * @param array $vars 模板输出变量 + * @param array $replace 替换内容 + * @param array $config 模板参数 + * @return mixed + */ + public function display($content, $vars = [], $replace = [], $config = []) + { + return $this->fetch($content, $vars, $replace, $config, true); + } + + /** + * 模板变量赋值 + * @access public + * @param string $name 变量名 + * @param mixed $value 变量值 + */ + public function __set($name, $value) + { + $this->data[$name] = $value; + } + + /** + * 取得模板显示变量的值 + * @access protected + * @param string $name 模板变量 + * @return mixed + */ + public function __get($name) + { + return $this->data[$name]; + } + + /** + * 检测模板变量是否设置 + * @access public + * @param string $name 模板变量名 + * @return bool + */ + public function __isset($name) + { + return isset($this->data[$name]); + } +} diff --git a/source/thinkphp/library/think/cache/Driver.php b/source/thinkphp/library/think/cache/Driver.php new file mode 100644 index 0000000..07805e4 --- /dev/null +++ b/source/thinkphp/library/think/cache/Driver.php @@ -0,0 +1,231 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache; + +/** + * 缓存基础类 + */ +abstract class Driver +{ + protected $handler = null; + protected $options = []; + protected $tag; + + /** + * 判断缓存是否存在 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + abstract public function has($name); + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + abstract public function get($name, $default = false); + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 + * @return boolean + */ + abstract public function set($name, $value, $expire = null); + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + abstract public function inc($name, $step = 1); + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + abstract public function dec($name, $step = 1); + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + abstract public function rm($name); + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + abstract public function clear($tag = null); + + /** + * 获取实际的缓存标识 + * @access public + * @param string $name 缓存名 + * @return string + */ + protected function getCacheKey($name) + { + return $this->options['prefix'] . $name; + } + + /** + * 读取缓存并删除 + * @access public + * @param string $name 缓存变量名 + * @return mixed + */ + public function pull($name) + { + $result = $this->get($name, false); + if ($result) { + $this->rm($name); + return $result; + } else { + return; + } + } + + /** + * 如果不存在则写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param int $expire 有效时间 0为永久 + * @return mixed + */ + public function remember($name, $value, $expire = null) + { + if (!$this->has($name)) { + $time = time(); + while ($time + 5 > time() && $this->has($name . '_lock')) { + // 存在锁定则等待 + usleep(200000); + } + + try { + // 锁定 + $this->set($name . '_lock', true); + if ($value instanceof \Closure) { + $value = call_user_func($value); + } + $this->set($name, $value, $expire); + // 解锁 + $this->rm($name . '_lock'); + } catch (\Exception $e) { + // 解锁 + $this->rm($name . '_lock'); + throw $e; + } catch (\throwable $e) { + $this->rm($name . '_lock'); + throw $e; + } + } else { + $value = $this->get($name); + } + return $value; + } + + /** + * 缓存标签 + * @access public + * @param string $name 标签名 + * @param string|array $keys 缓存标识 + * @param bool $overlay 是否覆盖 + * @return $this + */ + public function tag($name, $keys = null, $overlay = false) + { + if (is_null($name)) { + + } elseif (is_null($keys)) { + $this->tag = $name; + } else { + $key = 'tag_' . md5($name); + if (is_string($keys)) { + $keys = explode(',', $keys); + } + $keys = array_map([$this, 'getCacheKey'], $keys); + if ($overlay) { + $value = $keys; + } else { + $value = array_unique(array_merge($this->getTagItem($name), $keys)); + } + $this->set($key, implode(',', $value), 0); + } + return $this; + } + + /** + * 更新标签 + * @access public + * @param string $name 缓存标识 + * @return void + */ + protected function setTagItem($name) + { + if ($this->tag) { + $key = 'tag_' . md5($this->tag); + $this->tag = null; + if ($this->has($key)) { + $value = explode(',', $this->get($key)); + $value[] = $name; + $value = implode(',', array_unique($value)); + } else { + $value = $name; + } + $this->set($key, $value, 0); + } + } + + /** + * 获取标签包含的缓存标识 + * @access public + * @param string $tag 缓存标签 + * @return array + */ + protected function getTagItem($tag) + { + $key = 'tag_' . md5($tag); + $value = $this->get($key); + if ($value) { + return array_filter(explode(',', $value)); + } else { + return []; + } + } + + /** + * 返回句柄对象,可执行其它高级方法 + * + * @access public + * @return object + */ + public function handler() + { + return $this->handler; + } +} diff --git a/source/thinkphp/library/think/cache/driver/File.php b/source/thinkphp/library/think/cache/driver/File.php new file mode 100644 index 0000000..fee6489 --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/File.php @@ -0,0 +1,268 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * 文件类型缓存类 + * @author liu21st + */ +class File extends Driver +{ + protected $options = [ + 'expire' => 0, + 'cache_subdir' => true, + 'prefix' => '', + 'path' => CACHE_PATH, + 'data_compress' => false, + ]; + + protected $expire; + + /** + * 构造函数 + * @param array $options + */ + public function __construct($options = []) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + if (substr($this->options['path'], -1) != DS) { + $this->options['path'] .= DS; + } + $this->init(); + } + + /** + * 初始化检查 + * @access private + * @return boolean + */ + private function init() + { + // 创建项目缓存目录 + if (!is_dir($this->options['path'])) { + if (mkdir($this->options['path'], 0755, true)) { + return true; + } + } + return false; + } + + /** + * 取得变量的存储文件名 + * @access protected + * @param string $name 缓存变量名 + * @param bool $auto 是否自动创建目录 + * @return string + */ + protected function getCacheKey($name, $auto = false) + { + $name = md5($name); + if ($this->options['cache_subdir']) { + // 使用子目录 + $name = substr($name, 0, 2) . DS . substr($name, 2); + } + if ($this->options['prefix']) { + $name = $this->options['prefix'] . DS . $name; + } + $filename = $this->options['path'] . $name . '.php'; + $dir = dirname($filename); + + if ($auto && !is_dir($dir)) { + mkdir($dir, 0755, true); + } + return $filename; + } + + /** + * 判断缓存是否存在 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + return $this->get($name) ? true : false; + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $filename = $this->getCacheKey($name); + if (!is_file($filename)) { + return $default; + } + $content = file_get_contents($filename); + $this->expire = null; + if (false !== $content) { + $expire = (int) substr($content, 8, 12); + if (0 != $expire && time() > filemtime($filename) + $expire) { + return $default; + } + $this->expire = $expire; + $content = substr($content, 32); + if ($this->options['data_compress'] && function_exists('gzcompress')) { + //启用数据压缩 + $content = gzuncompress($content); + } + $content = unserialize($content); + return $content; + } else { + return $default; + } + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return boolean + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + $filename = $this->getCacheKey($name, true); + if ($this->tag && !is_file($filename)) { + $first = true; + } + $data = serialize($value); + if ($this->options['data_compress'] && function_exists('gzcompress')) { + //数据压缩 + $data = gzcompress($data, 3); + } + $data = "\n" . $data; + $result = file_put_contents($filename, $data); + if ($result) { + isset($first) && $this->setTagItem($filename); + clearstatcache(); + return true; + } else { + return false; + } + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + $expire = $this->expire; + } else { + $value = $step; + $expire = 0; + } + + return $this->set($name, $value, $expire) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + $expire = $this->expire; + } else { + $value = -$step; + $expire = 0; + } + + return $this->set($name, $value, $expire) ? $value : false; + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + $filename = $this->getCacheKey($name); + try { + return $this->unlink($filename); + } catch (\Exception $e) { + } + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + $this->unlink($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } + $files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*'); + foreach ($files as $path) { + if (is_dir($path)) { + $matches = glob($path . '/*.php'); + if (is_array($matches)) { + array_map('unlink', $matches); + } + rmdir($path); + } else { + unlink($path); + } + } + return true; + } + + /** + * 判断文件是否存在后,删除 + * @param $path + * @return bool + * @author byron sampson + * @return boolean + */ + private function unlink($path) + { + return is_file($path) && unlink($path); + } + +} diff --git a/source/thinkphp/library/think/cache/driver/Lite.php b/source/thinkphp/library/think/cache/driver/Lite.php new file mode 100644 index 0000000..8cbf08f --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Lite.php @@ -0,0 +1,187 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * 文件类型缓存类 + * @author liu21st + */ +class Lite extends Driver +{ + protected $options = [ + 'prefix' => '', + 'path' => '', + 'expire' => 0, // 等于 10*365*24*3600(10年) + ]; + + /** + * 构造函数 + * @access public + * + * @param array $options + */ + public function __construct($options = []) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + if (substr($this->options['path'], -1) != DS) { + $this->options['path'] .= DS; + } + + } + + /** + * 取得变量的存储文件名 + * @access protected + * @param string $name 缓存变量名 + * @return string + */ + protected function getCacheKey($name) + { + return $this->options['path'] . $this->options['prefix'] . md5($name) . '.php'; + } + + /** + * 判断缓存是否存在 + * @access public + * @param string $name 缓存变量名 + * @return mixed + */ + public function has($name) + { + return $this->get($name) ? true : false; + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $filename = $this->getCacheKey($name); + if (is_file($filename)) { + // 判断是否过期 + $mtime = filemtime($filename); + if ($mtime < time()) { + // 清除已经过期的文件 + unlink($filename); + return $default; + } + return include $filename; + } else { + return $default; + } + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return bool + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp(); + } else { + $expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire; + $expire = time() + $expire; + } + $filename = $this->getCacheKey($name); + if ($this->tag && !is_file($filename)) { + $first = true; + } + $ret = file_put_contents($filename, ("setTagItem($filename); + touch($filename, $expire); + } + return $ret; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + } else { + $value = $step; + } + return $this->set($name, $value, 0) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + } else { + $value = -$step; + } + return $this->set($name, $value, 0) ? $value : false; + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + return unlink($this->getCacheKey($name)); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return bool + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + unlink($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } + array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*.php')); + } +} diff --git a/source/thinkphp/library/think/cache/driver/Memcache.php b/source/thinkphp/library/think/cache/driver/Memcache.php new file mode 100644 index 0000000..58703ea --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Memcache.php @@ -0,0 +1,177 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +class Memcache extends Driver +{ + protected $options = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'expire' => 0, + 'timeout' => 0, // 超时时间(单位:毫秒) + 'persistent' => true, + 'prefix' => '', + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @access public + * @throws \BadFunctionCallException + */ + public function __construct($options = []) + { + if (!extension_loaded('memcache')) { + throw new \BadFunctionCallException('not support: memcache'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->handler = new \Memcache; + // 支持集群 + $hosts = explode(',', $this->options['host']); + $ports = explode(',', $this->options['port']); + if (empty($ports[0])) { + $ports[0] = 11211; + } + // 建立连接 + foreach ((array) $hosts as $i => $host) { + $port = isset($ports[$i]) ? $ports[$i] : $ports[0]; + $this->options['timeout'] > 0 ? + $this->handler->addServer($host, $port, $this->options['persistent'], 1, $this->options['timeout']) : + $this->handler->addServer($host, $port, $this->options['persistent'], 1); + } + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $key = $this->getCacheKey($name); + return false !== $this->handler->get($key); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $result = $this->handler->get($this->getCacheKey($name)); + return false !== $result ? $result : $default; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return bool + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + if ($this->tag && !$this->has($name)) { + $first = true; + } + $key = $this->getCacheKey($name); + if ($this->handler->set($key, $value, 0, $expire)) { + isset($first) && $this->setTagItem($key); + return true; + } + return false; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + $key = $this->getCacheKey($name); + if ($this->handler->get($key)) { + return $this->handler->increment($key, $step); + } + return $this->handler->set($key, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + $key = $this->getCacheKey($name); + $value = $this->handler->get($key) - $step; + $res = $this->handler->set($key, $value); + if (!$res) { + return false; + } else { + return $value; + } + } + + /** + * 删除缓存 + * @param string $name 缓存变量名 + * @param bool|false $ttl + * @return bool + */ + public function rm($name, $ttl = false) + { + $key = $this->getCacheKey($name); + return false === $ttl ? + $this->handler->delete($key) : + $this->handler->delete($key, $ttl); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return bool + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + $this->handler->delete($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } + return $this->handler->flush(); + } +} diff --git a/source/thinkphp/library/think/cache/driver/Memcached.php b/source/thinkphp/library/think/cache/driver/Memcached.php new file mode 100644 index 0000000..5aab5a3 --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Memcached.php @@ -0,0 +1,187 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +class Memcached extends Driver +{ + protected $options = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'expire' => 0, + 'timeout' => 0, // 超时时间(单位:毫秒) + 'prefix' => '', + 'username' => '', //账号 + 'password' => '', //密码 + 'option' => [], + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @access public + */ + public function __construct($options = []) + { + if (!extension_loaded('memcached')) { + throw new \BadFunctionCallException('not support: memcached'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->handler = new \Memcached; + if (!empty($this->options['option'])) { + $this->handler->setOptions($this->options['option']); + } + // 设置连接超时时间(单位:毫秒) + if ($this->options['timeout'] > 0) { + $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']); + } + // 支持集群 + $hosts = explode(',', $this->options['host']); + $ports = explode(',', $this->options['port']); + if (empty($ports[0])) { + $ports[0] = 11211; + } + // 建立连接 + $servers = []; + foreach ((array) $hosts as $i => $host) { + $servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1]; + } + $this->handler->addServers($servers); + if ('' != $this->options['username']) { + $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + $this->handler->setSaslAuthData($this->options['username'], $this->options['password']); + } + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $key = $this->getCacheKey($name); + return $this->handler->get($key) ? true : false; + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $result = $this->handler->get($this->getCacheKey($name)); + return false !== $result ? $result : $default; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return bool + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + if ($this->tag && !$this->has($name)) { + $first = true; + } + $key = $this->getCacheKey($name); + $expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire; + if ($this->handler->set($key, $value, $expire)) { + isset($first) && $this->setTagItem($key); + return true; + } + return false; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + $key = $this->getCacheKey($name); + if ($this->handler->get($key)) { + return $this->handler->increment($key, $step); + } + return $this->handler->set($key, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + $key = $this->getCacheKey($name); + $value = $this->handler->get($key) - $step; + $res = $this->handler->set($key, $value); + if (!$res) { + return false; + } else { + return $value; + } + } + + /** + * 删除缓存 + * @param string $name 缓存变量名 + * @param bool|false $ttl + * @return bool + */ + public function rm($name, $ttl = false) + { + $key = $this->getCacheKey($name); + return false === $ttl ? + $this->handler->delete($key) : + $this->handler->delete($key, $ttl); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return bool + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + $this->handler->deleteMulti($keys); + $this->rm('tag_' . md5($tag)); + return true; + } + return $this->handler->flush(); + } +} diff --git a/source/thinkphp/library/think/cache/driver/Redis.php b/source/thinkphp/library/think/cache/driver/Redis.php new file mode 100644 index 0000000..027b3ea --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Redis.php @@ -0,0 +1,188 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好 + * 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动 + * + * 要求安装phpredis扩展:https://github.com/nicolasff/phpredis + * @author 尘缘 <130775@qq.com> + */ +class Redis extends Driver +{ + protected $options = [ + 'host' => '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'select' => 0, + 'timeout' => 0, + 'expire' => 0, + 'persistent' => false, + 'prefix' => '', + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @access public + */ + public function __construct($options = []) + { + if (!extension_loaded('redis')) { + throw new \BadFunctionCallException('not support: redis'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->handler = new \Redis; + if ($this->options['persistent']) { + $this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']); + } else { + $this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']); + } + + if ('' != $this->options['password']) { + $this->handler->auth($this->options['password']); + } + + if (0 != $this->options['select']) { + $this->handler->select($this->options['select']); + } + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + return $this->handler->exists($this->getCacheKey($name)); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $value = $this->handler->get($this->getCacheKey($name)); + if (is_null($value) || false === $value) { + return $default; + } + + try { + $result = 0 === strpos($value, 'think_serialize:') ? unserialize(substr($value, 16)) : $value; + } catch (\Exception $e) { + $result = $default; + } + + return $result; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return boolean + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + if ($this->tag && !$this->has($name)) { + $first = true; + } + $key = $this->getCacheKey($name); + $value = is_scalar($value) ? $value : 'think_serialize:' . serialize($value); + if ($expire) { + $result = $this->handler->setex($key, $expire, $value); + } else { + $result = $this->handler->set($key, $value); + } + isset($first) && $this->setTagItem($key); + return $result; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + $key = $this->getCacheKey($name); + + return $this->handler->incrby($key, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + $key = $this->getCacheKey($name); + + return $this->handler->decrby($key, $step); + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + return $this->handler->delete($this->getCacheKey($name)); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + $this->handler->delete($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } + return $this->handler->flushDB(); + } + +} diff --git a/source/thinkphp/library/think/cache/driver/Sqlite.php b/source/thinkphp/library/think/cache/driver/Sqlite.php new file mode 100644 index 0000000..dc2ee05 --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Sqlite.php @@ -0,0 +1,199 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * Sqlite缓存驱动 + * @author liu21st + */ +class Sqlite extends Driver +{ + protected $options = [ + 'db' => ':memory:', + 'table' => 'sharedmemory', + 'prefix' => '', + 'expire' => 0, + 'persistent' => false, + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @throws \BadFunctionCallException + * @access public + */ + public function __construct($options = []) + { + if (!extension_loaded('sqlite')) { + throw new \BadFunctionCallException('not support: sqlite'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open'; + $this->handler = $func($this->options['db']); + } + + /** + * 获取实际的缓存标识 + * @access public + * @param string $name 缓存名 + * @return string + */ + protected function getCacheKey($name) + { + return $this->options['prefix'] . sqlite_escape_string($name); + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->getCacheKey($name); + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $result = sqlite_query($this->handler, $sql); + return sqlite_num_rows($result); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $name = $this->getCacheKey($name); + $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; + $result = sqlite_query($this->handler, $sql); + if (sqlite_num_rows($result)) { + $content = sqlite_fetch_single($result); + if (function_exists('gzcompress')) { + //启用数据压缩 + $content = gzuncompress($content); + } + return unserialize($content); + } + return $default; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return boolean + */ + public function set($name, $value, $expire = null) + { + $name = $this->getCacheKey($name); + $value = sqlite_escape_string(serialize($value)); + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp(); + } else { + $expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存 + } + if (function_exists('gzcompress')) { + //数据压缩 + $value = gzcompress($value, 3); + } + if ($this->tag) { + $tag = $this->tag; + $this->tag = null; + } else { + $tag = ''; + } + $sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')'; + if (sqlite_query($this->handler, $sql)) { + return true; + } + return false; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + } else { + $value = $step; + } + return $this->set($name, $value, 0) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + } else { + $value = -$step; + } + return $this->set($name, $value, 0) ? $value : false; + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + $name = $this->getCacheKey($name); + $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\''; + sqlite_query($this->handler, $sql); + return true; + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public function clear($tag = null) + { + if ($tag) { + $name = sqlite_escape_string($tag); + $sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\''; + sqlite_query($this->handler, $sql); + return true; + } + $sql = 'DELETE FROM ' . $this->options['table']; + sqlite_query($this->handler, $sql); + return true; + } +} diff --git a/source/thinkphp/library/think/cache/driver/Wincache.php b/source/thinkphp/library/think/cache/driver/Wincache.php new file mode 100644 index 0000000..03f8d35 --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Wincache.php @@ -0,0 +1,152 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * Wincache缓存驱动 + * @author liu21st + */ +class Wincache extends Driver +{ + protected $options = [ + 'prefix' => '', + 'expire' => 0, + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @throws \BadFunctionCallException + * @access public + */ + public function __construct($options = []) + { + if (!function_exists('wincache_ucache_info')) { + throw new \BadFunctionCallException('not support: WinCache'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $key = $this->getCacheKey($name); + return wincache_ucache_exists($key); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $key = $this->getCacheKey($name); + return wincache_ucache_exists($key) ? wincache_ucache_get($key) : $default; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return boolean + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + $key = $this->getCacheKey($name); + if ($this->tag && !$this->has($name)) { + $first = true; + } + if (wincache_ucache_set($key, $value, $expire)) { + isset($first) && $this->setTagItem($key); + return true; + } + return false; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + $key = $this->getCacheKey($name); + return wincache_ucache_inc($key, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + $key = $this->getCacheKey($name); + return wincache_ucache_dec($key, $step); + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + return wincache_ucache_delete($this->getCacheKey($name)); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public function clear($tag = null) + { + if ($tag) { + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + wincache_ucache_delete($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } else { + return wincache_ucache_clear(); + } + } + +} diff --git a/source/thinkphp/library/think/cache/driver/Xcache.php b/source/thinkphp/library/think/cache/driver/Xcache.php new file mode 100644 index 0000000..4d94c03 --- /dev/null +++ b/source/thinkphp/library/think/cache/driver/Xcache.php @@ -0,0 +1,155 @@ + +// +---------------------------------------------------------------------- + +namespace think\cache\driver; + +use think\cache\Driver; + +/** + * Xcache缓存驱动 + * @author liu21st + */ +class Xcache extends Driver +{ + protected $options = [ + 'prefix' => '', + 'expire' => 0, + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @access public + * @throws \BadFunctionCallException + */ + public function __construct($options = []) + { + if (!function_exists('xcache_info')) { + throw new \BadFunctionCallException('not support: Xcache'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + } + + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $key = $this->getCacheKey($name); + return xcache_isset($key); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) + { + $key = $this->getCacheKey($name); + return xcache_isset($key) ? xcache_get($key) : $default; + } + + /** + * 写入缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $value 存储数据 + * @param integer|\DateTime $expire 有效时间(秒) + * @return boolean + */ + public function set($name, $value, $expire = null) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + if ($this->tag && !$this->has($name)) { + $first = true; + } + $key = $this->getCacheKey($name); + if (xcache_set($key, $value, $expire)) { + isset($first) && $this->setTagItem($key); + return true; + } + return false; + } + + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function inc($name, $step = 1) + { + $key = $this->getCacheKey($name); + return xcache_inc($key, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @return false|int + */ + public function dec($name, $step = 1) + { + $key = $this->getCacheKey($name); + return xcache_dec($key, $step); + } + + /** + * 删除缓存 + * @access public + * @param string $name 缓存变量名 + * @return boolean + */ + public function rm($name) + { + return xcache_unset($this->getCacheKey($name)); + } + + /** + * 清除缓存 + * @access public + * @param string $tag 标签名 + * @return boolean + */ + public function clear($tag = null) + { + if ($tag) { + // 指定标签清除 + $keys = $this->getTagItem($tag); + foreach ($keys as $key) { + xcache_unset($key); + } + $this->rm('tag_' . md5($tag)); + return true; + } + if (function_exists('xcache_unset_by_prefix')) { + return xcache_unset_by_prefix($this->options['prefix']); + } else { + return false; + } + } +} diff --git a/source/thinkphp/library/think/config/driver/Ini.php b/source/thinkphp/library/think/config/driver/Ini.php new file mode 100644 index 0000000..bcd12b6 --- /dev/null +++ b/source/thinkphp/library/think/config/driver/Ini.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- + +namespace think\config\driver; + +class Ini +{ + public function parse($config) + { + if (is_file($config)) { + return parse_ini_file($config, true); + } else { + return parse_ini_string($config, true); + } + } +} diff --git a/source/thinkphp/library/think/config/driver/Json.php b/source/thinkphp/library/think/config/driver/Json.php new file mode 100644 index 0000000..479dcc8 --- /dev/null +++ b/source/thinkphp/library/think/config/driver/Json.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- + +namespace think\config\driver; + +class Json +{ + public function parse($config) + { + if (is_file($config)) { + $config = file_get_contents($config); + } + $result = json_decode($config, true); + return $result; + } +} diff --git a/source/thinkphp/library/think/config/driver/Xml.php b/source/thinkphp/library/think/config/driver/Xml.php new file mode 100644 index 0000000..1158519 --- /dev/null +++ b/source/thinkphp/library/think/config/driver/Xml.php @@ -0,0 +1,31 @@ + +// +---------------------------------------------------------------------- + +namespace think\config\driver; + +class Xml +{ + public function parse($config) + { + if (is_file($config)) { + $content = simplexml_load_file($config); + } else { + $content = simplexml_load_string($config); + } + $result = (array) $content; + foreach ($result as $key => $val) { + if (is_object($val)) { + $result[$key] = (array) $val; + } + } + return $result; + } +} diff --git a/source/thinkphp/library/think/console/Command.php b/source/thinkphp/library/think/console/Command.php new file mode 100644 index 0000000..d0caad2 --- /dev/null +++ b/source/thinkphp/library/think/console/Command.php @@ -0,0 +1,470 @@ + +// +---------------------------------------------------------------------- + +namespace think\console; + +use think\Console; +use think\console\input\Argument; +use think\console\input\Definition; +use think\console\input\Option; + +class Command +{ + + /** @var Console */ + private $console; + private $name; + private $aliases = []; + private $definition; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $consoleDefinitionMerged = false; + private $consoleDefinitionMergedWithArgs = false; + private $code; + private $synopsis = []; + private $usages = []; + + /** @var Input */ + protected $input; + + /** @var Output */ + protected $output; + + /** + * 构造方法 + * @param string|null $name 命令名称,如果没有设置则比如在 configure() 里设置 + * @throws \LogicException + * @api + */ + public function __construct($name = null) + { + $this->definition = new Definition(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this))); + } + } + + /** + * 忽略验证错误 + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * 设置控制台 + * @param Console $console + */ + public function setConsole(Console $console = null) + { + $this->console = $console; + } + + /** + * 获取控制台 + * @return Console + * @api + */ + public function getConsole() + { + return $this->console; + } + + /** + * 是否有效 + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * 配置指令 + */ + protected function configure() + { + } + + /** + * 执行指令 + * @param Input $input + * @param Output $output + * @return null|int + * @throws \LogicException + * @see setCode() + */ + protected function execute(Input $input, Output $output) + { + throw new \LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * 用户验证 + * @param Input $input + * @param Output $output + */ + protected function interact(Input $input, Output $output) + { + } + + /** + * 初始化 + * @param Input $input An InputInterface instance + * @param Output $output An OutputInterface instance + */ + protected function initialize(Input $input, Output $output) + { + } + + /** + * 执行 + * @param Input $input + * @param Output $output + * @return int + * @throws \Exception + * @see setCode() + * @see execute() + */ + public function run(Input $input, Output $output) + { + $this->input = $input; + $this->output = $output; + + $this->getSynopsis(true); + $this->getSynopsis(false); + + $this->mergeConsoleDefinition(); + + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $input->validate(); + + if ($this->code) { + $statusCode = call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * 设置执行代码 + * @param callable $code callable(InputInterface $input, OutputInterface $output) + * @return Command + * @throws \InvalidArgumentException + * @see execute() + */ + public function setCode(callable $code) + { + if (!is_callable($code)) { + throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.'); + } + + if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + $code = \Closure::bind($code, $this); + } + } + + $this->code = $code; + + return $this; + } + + /** + * 合并参数定义 + * @param bool $mergeArgs + */ + public function mergeConsoleDefinition($mergeArgs = true) + { + if (null === $this->console + || (true === $this->consoleDefinitionMerged + && ($this->consoleDefinitionMergedWithArgs || !$mergeArgs)) + ) { + return; + } + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->console->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + } + + $this->definition->addOptions($this->console->getDefinition()->getOptions()); + + $this->consoleDefinitionMerged = true; + if ($mergeArgs) { + $this->consoleDefinitionMergedWithArgs = true; + } + } + + /** + * 设置参数定义 + * @param array|Definition $definition + * @return Command + * @api + */ + public function setDefinition($definition) + { + if ($definition instanceof Definition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->consoleDefinitionMerged = false; + + return $this; + } + + /** + * 获取参数定义 + * @return Definition + * @api + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * 获取当前指令的参数定义 + * @return Definition + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * 添加参数 + * @param string $name 名称 + * @param int $mode 类型 + * @param string $description 描述 + * @param mixed $default 默认值 + * @return Command + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new Argument($name, $mode, $description, $default)); + + return $this; + } + + /** + * 添加选项 + * @param string $name 选项名称 + * @param string $shortcut 别名 + * @param int $mode 类型 + * @param string $description 描述 + * @param mixed $default 默认值 + * @return Command + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * 设置指令名称 + * @param string $name + * @return Command + * @throws \InvalidArgumentException + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * 获取指令名称 + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 设置描述 + * @param string $description + * @return Command + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * 获取描述 + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * 设置帮助信息 + * @param string $help + * @return Command + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * 获取帮助信息 + * @return string + */ + public function getHelp() + { + return $this->help; + } + + /** + * 描述信息 + * @return string + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = [ + '%command.name%', + '%command.full_name%', + ]; + $replacements = [ + $name, + $_SERVER['PHP_SELF'] . ' ' . $name, + ]; + + return str_replace($placeholders, $replacements, $this->getHelp()); + } + + /** + * 设置别名 + * @param string[] $aliases + * @return Command + * @throws \InvalidArgumentException + */ + public function setAliases($aliases) + { + if (!is_array($aliases) && !$aliases instanceof \Traversable) { + throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * 获取别名 + * @return array + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * 获取简介 + * @param bool $short 是否简单的 + * @return string + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * 添加用法介绍 + * @param string $usage + * @return $this + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * 获取用法介绍 + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * 验证指令名称 + * @param string $name + * @throws \InvalidArgumentException + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/source/thinkphp/library/think/console/Input.php b/source/thinkphp/library/think/console/Input.php new file mode 100644 index 0000000..2482dfd --- /dev/null +++ b/source/thinkphp/library/think/console/Input.php @@ -0,0 +1,464 @@ + +// +---------------------------------------------------------------------- + +namespace think\console; + +use think\console\input\Argument; +use think\console\input\Definition; +use think\console\input\Option; + +class Input +{ + + /** + * @var Definition + */ + protected $definition; + + /** + * @var Option[] + */ + protected $options = []; + + /** + * @var Argument[] + */ + protected $arguments = []; + + protected $interactive = true; + + private $tokens; + private $parsed; + + public function __construct($argv = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + // 去除命令名 + array_shift($argv); + } + + $this->tokens = $argv; + + $this->definition = new Definition(); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * 绑定实例 + * @param Definition $definition A InputDefinition instance + */ + public function bind(Definition $definition) + { + $this->arguments = []; + $this->options = []; + $this->definition = $definition; + + $this->parse(); + } + + /** + * 解析参数 + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * 解析短选项 + * @param string $token 当前的指令. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) + && $this->definition->getOptionForShortcut($name[0])->acceptValue() + ) { + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * 解析短选项 + * @param string $name 当前指令 + * @throws \RuntimeException + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * 解析完整选项 + * @param string $token 当前指令 + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * 解析参数 + * @param string $token 当前指令 + * @throws \RuntimeException + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + + $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token; + + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + + $this->arguments[$arg->getName()][] = $token; + } else { + throw new \RuntimeException('Too many arguments.'); + } + } + + /** + * 添加一个短选项的值 + * @param string $shortcut 短名称 + * @param mixed $value 值 + * @throws \RuntimeException + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * 添加一个完整选项的值 + * @param string $name 选项名 + * @param mixed $value 值 + * @throws \RuntimeException + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (false === $value) { + $value = null; + } + + if (null !== $value && !$option->acceptValue()) { + throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value)); + } + + if (null === $value && $option->acceptValue() && count($this->parsed)) { + $next = array_shift($this->parsed); + if (isset($next[0]) && '-' !== $next[0]) { + $value = $next; + } elseif (empty($next)) { + $value = ''; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray()) { + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * 获取第一个参数 + * @return string|null + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + return; + } + + /** + * 检查原始参数是否包含某个值 + * @param string|array $values 需要检查的值 + * @return bool + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value . '=')) { + return true; + } + } + } + + return false; + } + + /** + * 获取原始选项的值 + * @param string|array $values 需要检查的值 + * @param mixed $default 默认值 + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < count($tokens)) { + $token = array_shift($tokens); + + foreach ($values as $value) { + if ($token === $value || 0 === strpos($token, $value . '=')) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } + + /** + * 验证输入 + * @throws \RuntimeException + */ + public function validate() + { + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } + } + + /** + * 检查输入是否是交互的 + * @return bool + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * 设置输入的交互 + * @param bool + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * 获取所有的参数 + * @return Argument[] + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * 根据名称获取参数 + * @param string $name 参数名 + * @return mixed + * @throws \InvalidArgumentException + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name) + ->getDefault(); + } + + /** + * 设置参数的值 + * @param string $name 参数名 + * @param string $value 值 + * @throws \InvalidArgumentException + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * 检查是否存在某个参数 + * @param string|int $name 参数名或位置 + * @return bool + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * 获取所有的选项 + * @return Option[] + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * 获取选项值 + * @param string $name 选项名称 + * @return mixed + * @throws \InvalidArgumentException + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * 设置选项值 + * @param string $name 选项名 + * @param string|bool $value 值 + * @throws \InvalidArgumentException + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * 是否有某个选项 + * @param string $name 选项名 + * @return bool + */ + public function hasOption($name) + { + return $this->definition->hasOption($name) && isset($this->options[$name]); + } + + /** + * 转义指令 + * @param string $token + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } + + /** + * 返回传递给命令的参数的字符串 + * @return string + */ + public function __toString() + { + $tokens = array_map(function ($token) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1] . $this->escapeToken($match[2]); + } + + if ($token && '-' !== $token[0]) { + return $this->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/source/thinkphp/library/think/console/LICENSE b/source/thinkphp/library/think/console/LICENSE new file mode 100644 index 0000000..0abe056 --- /dev/null +++ b/source/thinkphp/library/think/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2016 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/source/thinkphp/library/think/console/Output.php b/source/thinkphp/library/think/console/Output.php new file mode 100644 index 0000000..65dc9fb --- /dev/null +++ b/source/thinkphp/library/think/console/Output.php @@ -0,0 +1,222 @@ + +// +---------------------------------------------------------------------- + +namespace think\console; + +use Exception; +use think\console\output\Ask; +use think\console\output\Descriptor; +use think\console\output\driver\Buffer; +use think\console\output\driver\Console; +use think\console\output\driver\Nothing; +use think\console\output\Question; +use think\console\output\question\Choice; +use think\console\output\question\Confirmation; + +/** + * Class Output + * @package think\console + * + * @see \think\console\output\driver\Console::setDecorated + * @method void setDecorated($decorated) + * + * @see \think\console\output\driver\Buffer::fetch + * @method string fetch() + * + * @method void info($message) + * @method void error($message) + * @method void comment($message) + * @method void warning($message) + * @method void highlight($message) + * @method void question($message) + */ +class Output +{ + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; + const VERBOSITY_VERY_VERBOSE = 3; + const VERBOSITY_DEBUG = 4; + + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; + + private $verbosity = self::VERBOSITY_NORMAL; + + /** @var Buffer|Console|Nothing */ + private $handle = null; + + protected $styles = [ + 'info', + 'error', + 'comment', + 'question', + 'highlight', + 'warning' + ]; + + public function __construct($driver = 'console') + { + $class = '\\think\\console\\output\\driver\\' . ucwords($driver); + + $this->handle = new $class($this); + } + + public function ask(Input $input, $question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($input, $question); + } + + public function askHidden(Input $input, $question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($input, $question); + } + + public function confirm(Input $input, $question, $default = true) + { + return $this->askQuestion($input, new Confirmation($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice(Input $input, $question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion($input, new Choice($question, $choices, $default)); + } + + protected function askQuestion(Input $input, Question $question) + { + $ask = new Ask($input, $this, $question); + $answer = $ask->run(); + + if ($input->isInteractive()) { + $this->newLine(); + } + + return $answer; + } + + protected function block($style, $message) + { + $this->writeln("<{$style}>{$message}"); + } + + /** + * 输出空行 + * @param int $count + */ + public function newLine($count = 1) + { + $this->write(str_repeat(PHP_EOL, $count)); + } + + /** + * 输出信息并换行 + * @param string $messages + * @param int $type + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $type); + } + + /** + * 输出信息 + * @param string $messages + * @param bool $newline + * @param int $type + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->handle->write($messages, $newline, $type); + } + + public function renderException(\Exception $e) + { + $this->handle->renderException($e); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + public function describe($object, array $options = []) + { + $descriptor = new Descriptor(); + $options = array_merge([ + 'raw_text' => false, + ], $options); + + $descriptor->describe($this, $object, $options); + } + + public function __call($method, $args) + { + if (in_array($method, $this->styles)) { + array_unshift($args, $method); + return call_user_func_array([$this, 'block'], $args); + } + + if ($this->handle && method_exists($this->handle, $method)) { + return call_user_func_array([$this->handle, $method], $args); + } else { + throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + } + } + +} diff --git a/source/thinkphp/library/think/console/bin/README.md b/source/thinkphp/library/think/console/bin/README.md new file mode 100644 index 0000000..9acc52f --- /dev/null +++ b/source/thinkphp/library/think/console/bin/README.md @@ -0,0 +1 @@ +console 工具使用 hiddeninput.exe 在 windows 上隐藏密码输入,该二进制文件由第三方提供,相关源码和其他细节可以在 [Hidden Input](https://github.com/Seldaek/hidden-input) 找到。 diff --git a/source/thinkphp/library/think/console/bin/hiddeninput.exe b/source/thinkphp/library/think/console/bin/hiddeninput.exe new file mode 100644 index 0000000000000000000000000000000000000000..c8cf65e8d819e6e525121cf6b21f1c2429746038 GIT binary patch literal 9216 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 J0sQ~1{4brj`U(I5 literal 0 HcmV?d00001 diff --git a/source/thinkphp/library/think/console/command/Build.php b/source/thinkphp/library/think/console/command/Build.php new file mode 100644 index 0000000..39806c3 --- /dev/null +++ b/source/thinkphp/library/think/console/command/Build.php @@ -0,0 +1,56 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Option; +use think\console\Output; + +class Build extends Command +{ + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('build') + ->setDefinition([ + new Option('config', null, Option::VALUE_OPTIONAL, "build.php path"), + new Option('module', null, Option::VALUE_OPTIONAL, "module name"), + ]) + ->setDescription('Build Application Dirs'); + } + + protected function execute(Input $input, Output $output) + { + if ($input->hasOption('module')) { + \think\Build::module($input->getOption('module')); + $output->writeln("Successed"); + return; + } + + if ($input->hasOption('config')) { + $build = include $input->getOption('config'); + } else { + $build = include APP_PATH . 'build.php'; + } + if (empty($build)) { + $output->writeln("Build Config Is Empty"); + return; + } + \think\Build::run($build); + $output->writeln("Successed"); + + } +} diff --git a/source/thinkphp/library/think/console/command/Clear.php b/source/thinkphp/library/think/console/command/Clear.php new file mode 100644 index 0000000..1b5102e --- /dev/null +++ b/source/thinkphp/library/think/console/command/Clear.php @@ -0,0 +1,63 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command; + +use think\Cache; +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\input\Option; +use think\console\Output; + +class Clear extends Command +{ + protected function configure() + { + // 指令配置 + $this + ->setName('clear') + ->addArgument('type', Argument::OPTIONAL, 'type to clear', null) + ->addOption('path', 'd', Option::VALUE_OPTIONAL, 'path to clear', null) + ->setDescription('Clear runtime file'); + } + + protected function execute(Input $input, Output $output) + { + $path = $input->getOption('path') ?: RUNTIME_PATH; + + $type = $input->getArgument('type'); + + if ($type == 'route') { + Cache::clear('route_check'); + } else { + if (is_dir($path)) { + $this->clearPath($path); + } + } + + $output->writeln("Clear Successed"); + } + + protected function clearPath($path) + { + $path = realpath($path) . DS; + $files = scandir($path); + if ($files) { + foreach ($files as $file) { + if ('.' != $file && '..' != $file && is_dir($path . $file)) { + $this->clearPath($path . $file); + } elseif ('.gitignore' != $file && is_file($path . $file)) { + unlink($path . $file); + } + } + } + } +} diff --git a/source/thinkphp/library/think/console/command/Help.php b/source/thinkphp/library/think/console/command/Help.php new file mode 100644 index 0000000..bae2c65 --- /dev/null +++ b/source/thinkphp/library/think/console/command/Help.php @@ -0,0 +1,69 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\input\Argument as InputArgument; +use think\console\input\Option as InputOption; +use think\console\Output; + +class Help extends Command +{ + + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this->setName('help')->setDefinition([ + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + ])->setDescription('Displays help for a command')->setHelp(<<%command.name% command displays help for a given command: + + php %command.full_name% list + +To display the list of available commands, please use the list command. +EOF + ); + } + + /** + * Sets the command. + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(Input $input, Output $output) + { + if (null === $this->command) { + $this->command = $this->getConsole()->find($input->getArgument('command_name')); + } + + $output->describe($this->command, [ + 'raw_text' => $input->getOption('raw'), + ]); + + $this->command = null; + } +} diff --git a/source/thinkphp/library/think/console/command/Lists.php b/source/thinkphp/library/think/console/command/Lists.php new file mode 100644 index 0000000..084ddaa --- /dev/null +++ b/source/thinkphp/library/think/console/command/Lists.php @@ -0,0 +1,74 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command; + +use think\console\Command; +use think\console\Input; +use think\console\Output; +use think\console\input\Argument as InputArgument; +use think\console\input\Option as InputOption; +use think\console\input\Definition as InputDefinition; + +class Lists extends Command +{ + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(<<%command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ); + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(Input $input, Output $output) + { + $output->describe($this->getConsole(), [ + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + ]); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list') + ]); + } +} diff --git a/source/thinkphp/library/think/console/command/Make.php b/source/thinkphp/library/think/console/command/Make.php new file mode 100644 index 0000000..d1daf34 --- /dev/null +++ b/source/thinkphp/library/think/console/command/Make.php @@ -0,0 +1,110 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command; + +use think\App; +use think\Config; +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\Output; + +abstract class Make extends Command +{ + + protected $type; + + abstract protected function getStub(); + + protected function configure() + { + $this->addArgument('name', Argument::REQUIRED, "The name of the class"); + } + + protected function execute(Input $input, Output $output) + { + + $name = trim($input->getArgument('name')); + + $classname = $this->getClassName($name); + + $pathname = $this->getPathName($classname); + + if (is_file($pathname)) { + $output->writeln('' . $this->type . ' already exists!'); + return false; + } + + if (!is_dir(dirname($pathname))) { + mkdir(strtolower(dirname($pathname)), 0755, true); + } + + file_put_contents($pathname, $this->buildClass($classname)); + + $output->writeln('' . $this->type . ' created successfully.'); + + } + + protected function buildClass($name) + { + $stub = file_get_contents($this->getStub()); + + $namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\'); + + $class = str_replace($namespace . '\\', '', $name); + + return str_replace(['{%className%}', '{%namespace%}', '{%app_namespace%}'], [ + $class, + $namespace, + App::$namespace, + ], $stub); + + } + + protected function getPathName($name) + { + $name = str_replace(App::$namespace . '\\', '', $name); + + return APP_PATH . str_replace('\\', '/', $name) . '.php'; + } + + protected function getClassName($name) + { + $appNamespace = App::$namespace; + + if (strpos($name, $appNamespace . '\\') === 0) { + return $name; + } + + if (Config::get('app_multi_module')) { + if (strpos($name, '/')) { + list($module, $name) = explode('/', $name, 2); + } else { + $module = 'common'; + } + } else { + $module = null; + } + + if (strpos($name, '/') !== false) { + $name = str_replace('/', '\\', $name); + } + + return $this->getNamespace($appNamespace, $module) . '\\' . $name; + } + + protected function getNamespace($appNamespace, $module) + { + return $module ? ($appNamespace . '\\' . $module) : $appNamespace; + } + +} diff --git a/source/thinkphp/library/think/console/command/make/Controller.php b/source/thinkphp/library/think/console/command/make/Controller.php new file mode 100644 index 0000000..afa7be9 --- /dev/null +++ b/source/thinkphp/library/think/console/command/make/Controller.php @@ -0,0 +1,50 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command\make; + +use think\Config; +use think\console\command\Make; +use think\console\input\Option; + +class Controller extends Make +{ + + protected $type = "Controller"; + + protected function configure() + { + parent::configure(); + $this->setName('make:controller') + ->addOption('plain', null, Option::VALUE_NONE, 'Generate an empty controller class.') + ->setDescription('Create a new resource controller class'); + } + + protected function getStub() + { + if ($this->input->getOption('plain')) { + return __DIR__ . '/stubs/controller.plain.stub'; + } + + return __DIR__ . '/stubs/controller.stub'; + } + + protected function getClassName($name) + { + return parent::getClassName($name) . (Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''); + } + + protected function getNamespace($appNamespace, $module) + { + return parent::getNamespace($appNamespace, $module) . '\controller'; + } + +} diff --git a/source/thinkphp/library/think/console/command/make/Model.php b/source/thinkphp/library/think/console/command/make/Model.php new file mode 100644 index 0000000..d4e9b5d --- /dev/null +++ b/source/thinkphp/library/think/console/command/make/Model.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\command\make; + +use think\console\command\Make; + +class Model extends Make +{ + protected $type = "Model"; + + protected function configure() + { + parent::configure(); + $this->setName('make:model') + ->setDescription('Create a new model class'); + } + + protected function getStub() + { + return __DIR__ . '/stubs/model.stub'; + } + + protected function getNamespace($appNamespace, $module) + { + return parent::getNamespace($appNamespace, $module) . '\model'; + } +} diff --git a/source/thinkphp/library/think/console/command/make/stubs/controller.plain.stub b/source/thinkphp/library/think/console/command/make/stubs/controller.plain.stub new file mode 100644 index 0000000..b7539dc --- /dev/null +++ b/source/thinkphp/library/think/console/command/make/stubs/controller.plain.stub @@ -0,0 +1,10 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command\optimize; + +use think\App; +use think\Config; +use think\console\Command; +use think\console\Input; +use think\console\Output; + +class Autoload extends Command +{ + + protected function configure() + { + $this->setName('optimize:autoload') + ->setDescription('Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.'); + } + + protected function execute(Input $input, Output $output) + { + + $classmapFile = << realpath(rtrim(APP_PATH)), + 'think\\' => LIB_PATH . 'think', + 'behavior\\' => LIB_PATH . 'behavior', + 'traits\\' => LIB_PATH . 'traits', + '' => realpath(rtrim(EXTEND_PATH)), + ]; + + $root_namespace = Config::get('root_namespace'); + foreach ($root_namespace as $namespace => $dir) { + $namespacesToScan[$namespace . '\\'] = realpath($dir); + } + + krsort($namespacesToScan); + $classMap = []; + foreach ($namespacesToScan as $namespace => $dir) { + + if (!is_dir($dir)) { + continue; + } + + $namespaceFilter = $namespace === '' ? null : $namespace; + $classMap = $this->addClassMapCode($dir, $namespaceFilter, $classMap); + } + + ksort($classMap); + foreach ($classMap as $class => $code) { + $classmapFile .= ' ' . var_export($class, true) . ' => ' . $code; + } + $classmapFile .= "];\n"; + + if (!is_dir(RUNTIME_PATH)) { + @mkdir(RUNTIME_PATH, 0755, true); + } + + file_put_contents(RUNTIME_PATH . 'classmap' . EXT, $classmapFile); + + $output->writeln('Succeed!'); + } + + protected function addClassMapCode($dir, $namespace, $classMap) + { + foreach ($this->createMap($dir, $namespace) as $class => $path) { + + $pathCode = $this->getPathCode($path) . ",\n"; + + if (!isset($classMap[$class])) { + $classMap[$class] = $pathCode; + } elseif ($classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class] . ' ' . $path, '\\', '/'))) { + $this->output->writeln( + 'Warning: Ambiguous class resolution, "' . $class . '"' . + ' was found in both "' . str_replace(["',\n"], [ + '', + ], $classMap[$class]) . '" and "' . $path . '", the first will be used.' + ); + } + } + return $classMap; + } + + protected function getPathCode($path) + { + + $baseDir = ''; + $libPath = $this->normalizePath(realpath(LIB_PATH)); + $appPath = $this->normalizePath(realpath(APP_PATH)); + $extendPath = $this->normalizePath(realpath(EXTEND_PATH)); + $rootPath = $this->normalizePath(realpath(ROOT_PATH)); + $path = $this->normalizePath($path); + + if ($libPath !== null && strpos($path, $libPath . '/') === 0) { + $path = substr($path, strlen(LIB_PATH)); + $baseDir = 'LIB_PATH'; + } elseif ($appPath !== null && strpos($path, $appPath . '/') === 0) { + $path = substr($path, strlen($appPath) + 1); + $baseDir = 'APP_PATH'; + } elseif ($extendPath !== null && strpos($path, $extendPath . '/') === 0) { + $path = substr($path, strlen($extendPath) + 1); + $baseDir = 'EXTEND_PATH'; + } elseif ($rootPath !== null && strpos($path, $rootPath . '/') === 0) { + $path = substr($path, strlen($rootPath) + 1); + $baseDir = 'ROOT_PATH'; + } + + if ($path !== false) { + $baseDir .= " . "; + } + + return $baseDir . (($path !== false) ? var_export($path, true) : ""); + } + + protected function normalizePath($path) + { + if ($path === false) { + return; + } + $parts = []; + $path = strtr($path, '\\', '/'); + $prefix = ''; + $absolute = false; + + if (preg_match('{^([0-9a-z]+:(?://(?:[a-z]:)?)?)}i', $path, $match)) { + $prefix = $match[1]; + $path = substr($path, strlen($prefix)); + } + + if (substr($path, 0, 1) === '/') { + $absolute = true; + $path = substr($path, 1); + } + + $up = false; + foreach (explode('/', $path) as $chunk) { + if ('..' === $chunk && ($absolute || $up)) { + array_pop($parts); + $up = !(empty($parts) || '..' === end($parts)); + } elseif ('.' !== $chunk && '' !== $chunk) { + $parts[] = $chunk; + $up = '..' !== $chunk; + } + } + + return $prefix . ($absolute ? '/' : '') . implode('/', $parts); + } + + protected function createMap($path, $namespace = null) + { + if (is_string($path)) { + if (is_file($path)) { + $path = [new \SplFileInfo($path)]; + } elseif (is_dir($path)) { + + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST); + + $path = []; + + /** @var \SplFileInfo $object */ + foreach ($objects as $object) { + if ($object->isFile() && $object->getExtension() == 'php') { + $path[] = $object; + } + } + } else { + throw new \RuntimeException( + 'Could not scan for classes inside "' . $path . + '" which does not appear to be a file nor a folder' + ); + } + } + + $map = []; + + /** @var \SplFileInfo $file */ + foreach ($path as $file) { + $filePath = $file->getRealPath(); + + if (pathinfo($filePath, PATHINFO_EXTENSION) != 'php') { + continue; + } + + $classes = $this->findClasses($filePath); + + foreach ($classes as $class) { + if (null !== $namespace && 0 !== strpos($class, $namespace)) { + continue; + } + + if (!isset($map[$class])) { + $map[$class] = $filePath; + } elseif ($map[$class] !== $filePath && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($map[$class] . ' ' . $filePath, '\\', '/'))) { + $this->output->writeln( + 'Warning: Ambiguous class resolution, "' . $class . '"' . + ' was found in both "' . $map[$class] . '" and "' . $filePath . '", the first will be used.' + ); + } + } + } + + return $map; + } + + protected function findClasses($path) + { + $extraTypes = '|trait'; + + $contents = @php_strip_whitespace($path); + if (!$contents) { + if (!file_exists($path)) { + $message = 'File at "%s" does not exist, check your classmap definitions'; + } elseif (!is_readable($path)) { + $message = 'File at "%s" is not readable, check its permissions'; + } elseif ('' === trim(file_get_contents($path))) { + return []; + } else { + $message = 'File at "%s" could not be parsed as PHP, it may be binary or corrupted'; + } + $error = error_get_last(); + if (isset($error['message'])) { + $message .= PHP_EOL . 'The following message may be helpful:' . PHP_EOL . $error['message']; + } + throw new \RuntimeException(sprintf($message, $path)); + } + + if (!preg_match('{\b(?:class|interface' . $extraTypes . ')\s}i', $contents)) { + return []; + } + + // strip heredocs/nowdocs + $contents = preg_replace('{<<<\s*(\'?)(\w+)\\1(?:\r\n|\n|\r)(?:.*?)(?:\r\n|\n|\r)\\2(?=\r\n|\n|\r|;)}s', 'null', $contents); + // strip strings + $contents = preg_replace('{"[^"\\\\]*+(\\\\.[^"\\\\]*+)*+"|\'[^\'\\\\]*+(\\\\.[^\'\\\\]*+)*+\'}s', 'null', $contents); + // strip leading non-php code if needed + if (substr($contents, 0, 2) !== '.+<\?}s', '?>'); + if (false !== $pos && false === strpos(substr($contents, $pos), '])(?Pclass|interface' . $extraTypes . ') \s++ (?P[a-zA-Z_\x7f-\xff:][a-zA-Z0-9_\x7f-\xff:\-]*+) + | \b(?])(?Pnamespace) (?P\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+\\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;] + ) + }ix', $contents, $matches); + + $classes = []; + $namespace = ''; + + for ($i = 0, $len = count($matches['type']); $i < $len; $i++) { + if (!empty($matches['ns'][$i])) { + $namespace = str_replace([' ', "\t", "\r", "\n"], '', $matches['nsname'][$i]) . '\\'; + } else { + $name = $matches['name'][$i]; + if ($name[0] === ':') { + $name = 'xhp' . substr(str_replace(['-', ':'], ['_', '__'], $name), 1); + } elseif ($matches['type'][$i] === 'enum') { + $name = rtrim($name, ':'); + } + $classes[] = ltrim($namespace . $name, '\\'); + } + } + + return $classes; + } + +} diff --git a/source/thinkphp/library/think/console/command/optimize/Config.php b/source/thinkphp/library/think/console/command/optimize/Config.php new file mode 100644 index 0000000..59c69a8 --- /dev/null +++ b/source/thinkphp/library/think/console/command/optimize/Config.php @@ -0,0 +1,93 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command\optimize; + +use think\Config as ThinkConfig; +use think\console\Command; +use think\console\Input; +use think\console\input\Argument; +use think\console\Output; + +class Config extends Command +{ + /** @var Output */ + protected $output; + + protected function configure() + { + $this->setName('optimize:config') + ->addArgument('module', Argument::OPTIONAL, 'Build module config cache .') + ->setDescription('Build config and common file cache.'); + } + + protected function execute(Input $input, Output $output) + { + if ($input->getArgument('module')) { + $module = $input->getArgument('module') . DS; + } else { + $module = ''; + } + + $content = 'buildCacheContent($module); + + if (!is_dir(RUNTIME_PATH . $module)) { + @mkdir(RUNTIME_PATH . $module, 0755, true); + } + + file_put_contents(RUNTIME_PATH . $module . 'init' . EXT, $content); + + $output->writeln('Succeed!'); + } + + protected function buildCacheContent($module) + { + $content = ''; + $path = realpath(APP_PATH . $module) . DS; + + if ($module) { + // 加载模块配置 + $config = ThinkConfig::load(CONF_PATH . $module . 'config' . CONF_EXT); + + // 读取数据库配置文件 + $filename = CONF_PATH . $module . 'database' . CONF_EXT; + ThinkConfig::load($filename, 'database'); + + // 加载应用状态配置 + if ($config['app_status']) { + $config = ThinkConfig::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); + } + // 读取扩展配置文件 + if (is_dir(CONF_PATH . $module . 'extra')) { + $dir = CONF_PATH . $module . 'extra'; + $files = scandir($dir); + foreach ($files as $file) { + if (strpos($file, CONF_EXT)) { + $filename = $dir . DS . $file; + ThinkConfig::load($filename, pathinfo($file, PATHINFO_FILENAME)); + } + } + } + } + + // 加载行为扩展文件 + if (is_file(CONF_PATH . $module . 'tags' . EXT)) { + $content .= '\think\Hook::import(' . (var_export(include CONF_PATH . $module . 'tags' . EXT, true)) . ');' . PHP_EOL; + } + + // 加载公共文件 + if (is_file($path . 'common' . EXT)) { + $content .= substr(php_strip_whitespace($path . 'common' . EXT), 5) . PHP_EOL; + } + + $content .= '\think\Config::set(' . var_export(ThinkConfig::get(), true) . ');'; + return $content; + } +} diff --git a/source/thinkphp/library/think/console/command/optimize/Route.php b/source/thinkphp/library/think/console/command/optimize/Route.php new file mode 100644 index 0000000..6da1d9a --- /dev/null +++ b/source/thinkphp/library/think/console/command/optimize/Route.php @@ -0,0 +1,75 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command\optimize; + +use think\console\Command; +use think\console\Input; +use think\console\Output; + +class Route extends Command +{ + /** @var Output */ + protected $output; + + protected function configure() + { + $this->setName('optimize:route') + ->setDescription('Build route cache.'); + } + + protected function execute(Input $input, Output $output) + { + + if (!is_dir(RUNTIME_PATH)) { + @mkdir(RUNTIME_PATH, 0755, true); + } + + file_put_contents(RUNTIME_PATH . 'route.php', $this->buildRouteCache()); + $output->writeln('Succeed!'); + } + + protected function buildRouteCache() + { + $files = \think\Config::get('route_config_file'); + foreach ($files as $file) { + if (is_file(CONF_PATH . $file . CONF_EXT)) { + $config = include CONF_PATH . $file . CONF_EXT; + if (is_array($config)) { + \think\Route::import($config); + } + } + } + $rules = \think\Route::rules(true); + array_walk_recursive($rules, [$this, 'buildClosure']); + $content = 'getStartLine(); + $endLine = $reflection->getEndLine(); + $file = $reflection->getFileName(); + $item = file($file); + $content = ''; + for ($i = $startLine - 1; $i <= $endLine - 1; $i++) { + $content .= $item[$i]; + } + $start = strpos($content, 'function'); + $end = strrpos($content, '}'); + $value = '[__start__' . substr($content, $start, $end - $start + 1) . '__end__]'; + } + } +} diff --git a/source/thinkphp/library/think/console/command/optimize/Schema.php b/source/thinkphp/library/think/console/command/optimize/Schema.php new file mode 100644 index 0000000..3353424 --- /dev/null +++ b/source/thinkphp/library/think/console/command/optimize/Schema.php @@ -0,0 +1,118 @@ + +// +---------------------------------------------------------------------- +namespace think\console\command\optimize; + +use think\App; +use think\console\Command; +use think\console\Input; +use think\console\input\Option; +use think\console\Output; +use think\Db; + +class Schema extends Command +{ + /** @var Output */ + protected $output; + + protected function configure() + { + $this->setName('optimize:schema') + ->addOption('config', null, Option::VALUE_REQUIRED, 'db config .') + ->addOption('db', null, Option::VALUE_REQUIRED, 'db name .') + ->addOption('table', null, Option::VALUE_REQUIRED, 'table name .') + ->addOption('module', null, Option::VALUE_REQUIRED, 'module name .') + ->setDescription('Build database schema cache.'); + } + + protected function execute(Input $input, Output $output) + { + if (!is_dir(RUNTIME_PATH . 'schema')) { + @mkdir(RUNTIME_PATH . 'schema', 0755, true); + } + $config = []; + if ($input->hasOption('config')) { + $config = $input->getOption('config'); + } + if ($input->hasOption('module')) { + $module = $input->getOption('module'); + // 读取模型 + $path = APP_PATH . $module . DS . 'model'; + $list = is_dir($path) ? scandir($path) : []; + $app = App::$namespace; + foreach ($list as $file) { + if (0 === strpos($file, '.')) { + continue; + } + $class = '\\' . $app . '\\' . $module . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); + $this->buildModelSchema($class); + } + $output->writeln('Succeed!'); + return; + } elseif ($input->hasOption('table')) { + $table = $input->getOption('table'); + if (!strpos($table, '.')) { + $dbName = Db::connect($config)->getConfig('database'); + } + $tables[] = $table; + } elseif ($input->hasOption('db')) { + $dbName = $input->getOption('db'); + $tables = Db::connect($config)->getTables($dbName); + } elseif (!\think\Config::get('app_multi_module')) { + $app = App::$namespace; + $path = APP_PATH . 'model'; + $list = is_dir($path) ? scandir($path) : []; + foreach ($list as $file) { + if (0 === strpos($file, '.')) { + continue; + } + $class = '\\' . $app . '\\model\\' . pathinfo($file, PATHINFO_FILENAME); + $this->buildModelSchema($class); + } + $output->writeln('Succeed!'); + return; + } else { + $tables = Db::connect($config)->getTables(); + } + + $db = isset($dbName) ? $dbName . '.' : ''; + $this->buildDataBaseSchema($tables, $db, $config); + + $output->writeln('Succeed!'); + } + + protected function buildModelSchema($class) + { + $reflect = new \ReflectionClass($class); + if (!$reflect->isAbstract() && $reflect->isSubclassOf('\think\Model')) { + $table = $class::getTable(); + $dbName = $class::getConfig('database'); + $content = 'getFields($table); + $content .= var_export($info, true) . ';'; + file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . '.' . $table . EXT, $content); + } + } + + protected function buildDataBaseSchema($tables, $db, $config) + { + if ('' == $db) { + $dbName = Db::connect($config)->getConfig('database') . '.'; + } else { + $dbName = $db; + } + foreach ($tables as $table) { + $content = 'getFields($db . $table); + $content .= var_export($info, true) . ';'; + file_put_contents(RUNTIME_PATH . 'schema' . DS . $dbName . $table . EXT, $content); + } + } +} diff --git a/source/thinkphp/library/think/console/input/Argument.php b/source/thinkphp/library/think/console/input/Argument.php new file mode 100644 index 0000000..16223bb --- /dev/null +++ b/source/thinkphp/library/think/console/input/Argument.php @@ -0,0 +1,115 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\input; + +class Argument +{ + + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * 构造方法 + * @param string $name 参数名 + * @param int $mode 参数类型: self::REQUIRED 或者 self::OPTIONAL + * @param string $description 描述 + * @param mixed $default 默认值 (仅 self::OPTIONAL 类型有效) + * @throws \InvalidArgumentException + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * 获取参数名 + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 是否必须 + * @return bool + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * 该参数是否接受数组 + * @return bool + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * 设置默认值 + * @param mixed $default 默认值 + * @throws \LogicException + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = []; + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * 获取默认值 + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * 获取描述 + * @return string + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/source/thinkphp/library/think/console/input/Definition.php b/source/thinkphp/library/think/console/input/Definition.php new file mode 100644 index 0000000..c71977e --- /dev/null +++ b/source/thinkphp/library/think/console/input/Definition.php @@ -0,0 +1,375 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\input; + +class Definition +{ + + /** + * @var Argument[] + */ + private $arguments; + + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + + /** + * @var Option[] + */ + private $options; + private $shortcuts; + + /** + * 构造方法 + * @param array $definition + * @api + */ + public function __construct(array $definition = []) + { + $this->setDefinition($definition); + } + + /** + * 设置指令的定义 + * @param array $definition 定义的数组 + */ + public function setDefinition(array $definition) + { + $arguments = []; + $options = []; + foreach ($definition as $item) { + if ($item instanceof Option) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * 设置参数 + * @param Argument[] $arguments 参数数组 + */ + public function setArguments($arguments = []) + { + $this->arguments = []; + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * 添加参数 + * @param Argument[] $arguments 参数数组 + * @api + */ + public function addArguments($arguments = []) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * 添加一个参数 + * @param Argument $argument 参数 + * @throws \LogicException + */ + public function addArgument(Argument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new \LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new \LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * 根据名称或者位置获取参数 + * @param string|int $name 参数名或者位置 + * @return Argument 参数 + * @throws \InvalidArgumentException + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * 根据名称或位置检查是否具有某个参数 + * @param string|int $name 参数名或者位置 + * @return bool + * @api + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * 获取所有的参数 + * @return Argument[] 参数数组 + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * 获取参数数量 + * @return int + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * 获取必填的参数的数量 + * @return int + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * 获取参数默认值 + * @return array + */ + public function getArgumentDefaults() + { + $values = []; + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * 设置选项 + * @param Option[] $options 选项数组 + */ + public function setOptions($options = []) + { + $this->options = []; + $this->shortcuts = []; + $this->addOptions($options); + } + + /** + * 添加选项 + * @param Option[] $options 选项数组 + * @api + */ + public function addOptions($options = []) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * 添加一个选项 + * @param Option $option 选项 + * @throws \LogicException + * @api + */ + public function addOption(Option $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) + && !$option->equals($this->options[$this->shortcuts[$shortcut]]) + ) { + throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * 根据名称获取选项 + * @param string $name 选项名 + * @return Option + * @throws \InvalidArgumentException + * @api + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * 根据名称检查是否有这个选项 + * @param string $name 选项名 + * @return bool + * @api + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * 获取所有选项 + * @return Option[] + * @api + */ + public function getOptions() + { + return $this->options; + } + + /** + * 根据名称检查某个选项是否有短名称 + * @param string $name 短名称 + * @return bool + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * 根据短名称获取选项 + * @param string $shortcut 短名称 + * @return Option + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * 获取所有选项的默认值 + * @return array + */ + public function getOptionDefaults() + { + $values = []; + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * 根据短名称获取选项名 + * @param string $shortcut 短名称 + * @return string + * @throws \InvalidArgumentException + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * 获取该指令的介绍 + * @param bool $short 是否简洁介绍 + * @return string + */ + public function getSynopsis($short = false) + { + $elements = []; + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf(' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), $option->isValueOptional() ? ']' : ''); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<' . $argument->getName() . '>'; + if (!$argument->isRequired()) { + $element = '[' . $element . ']'; + } elseif ($argument->isArray()) { + $element .= ' (' . $element . ')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } +} diff --git a/source/thinkphp/library/think/console/input/Option.php b/source/thinkphp/library/think/console/input/Option.php new file mode 100644 index 0000000..e5707c9 --- /dev/null +++ b/source/thinkphp/library/think/console/input/Option.php @@ -0,0 +1,190 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\input; + +class Option +{ + + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * 构造方法 + * @param string $name 选项名 + * @param string|array $shortcut 短名称,多个用|隔开或者使用数组 + * @param int $mode 选项类型(可选类型为 self::VALUE_*) + * @param string $description 描述 + * @param mixed $default 默认值 (类型为 self::VALUE_REQUIRED 或者 self::VALUE_NONE 的时候必须为null) + * @throws \InvalidArgumentException + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new \InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new \InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * 获取短名称 + * @return string + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * 获取选项名 + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 是否可以设置值 + * @return bool 类型不是 self::VALUE_NONE 的时候返回true,其他均返回false + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * 是否必须 + * @return bool 类型是 self::VALUE_REQUIRED 的时候返回true,其他均返回false + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * 是否可选 + * @return bool 类型是 self::VALUE_OPTIONAL 的时候返回true,其他均返回false + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * 选项值是否接受数组 + * @return bool 类型是 self::VALUE_IS_ARRAY 的时候返回true,其他均返回false + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * 设置默认值 + * @param mixed $default 默认值 + * @throws \LogicException + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = []; + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * 获取默认值 + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * 获取描述文字 + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * 检查所给选项是否是当前这个 + * @param Option $option + * @return bool + */ + public function equals(Option $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional(); + } +} diff --git a/source/thinkphp/library/think/console/output/Ask.php b/source/thinkphp/library/think/console/output/Ask.php new file mode 100644 index 0000000..3933eb2 --- /dev/null +++ b/source/thinkphp/library/think/console/output/Ask.php @@ -0,0 +1,340 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output; + +use think\console\Input; +use think\console\Output; +use think\console\output\question\Choice; +use think\console\output\question\Confirmation; + +class Ask +{ + private static $stty; + + private static $shell; + + /** @var Input */ + protected $input; + + /** @var Output */ + protected $output; + + /** @var Question */ + protected $question; + + public function __construct(Input $input, Output $output, Question $question) + { + $this->input = $input; + $this->output = $output; + $this->question = $question; + } + + public function run() + { + if (!$this->input->isInteractive()) { + return $this->question->getDefault(); + } + + if (!$this->question->getValidator()) { + return $this->doAsk(); + } + + $that = $this; + + $interviewer = function () use ($that) { + return $that->doAsk(); + }; + + return $this->validateAttempts($interviewer); + } + + protected function doAsk() + { + $this->writePrompt(); + + $inputStream = STDIN; + $autocomplete = $this->question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($this->question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($inputStream)); + } catch (\RuntimeException $e) { + if (!$this->question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + } + } else { + $ret = trim($this->autocomplete($inputStream)); + } + + $ret = strlen($ret) > 0 ? $ret : $this->question->getDefault(); + + if ($normalizer = $this->question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + private function autocomplete($inputStream) + { + $autocomplete = $this->question->getAutocompleterValues(); + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -icanon -echo'); + + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + if ("\177" === $c) { + if (0 === $numMatches && 0 !== $i) { + --$i; + $this->output->write("\033[1D"); + } + + if ($i === 0) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = count($matches); + } else { + $numMatches = 0; + } + + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + $c .= fread($inputStream, 2); + + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + $this->output->write(substr($ret, $i)); + $i = strlen($ret); + } + + if ("\n" === $c) { + $this->output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + $this->output->write($c); + $ret .= $c; + ++$i; + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + if (0 === strpos($value, $ret) && $i !== strlen($value)) { + $matches[$numMatches++] = $value; + } + } + } + + $this->output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + $this->output->write("\0337"); + $this->output->highlight(substr($matches[$ofs], $i)); + $this->output->write("\0338"); + } + } + + shell_exec(sprintf('stty %s', $sttyMode)); + + return $ret; + } + + protected function getHiddenResponse($inputStream) + { + if ('\\' === DIRECTORY_SEPARATOR) { + $exe = __DIR__ . '/../bin/hiddeninput.exe'; + + $value = rtrim(shell_exec($exe)); + $this->output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new \RuntimeException('Aborted'); + } + + $value = trim($value); + $this->output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = $shell === 'csh' ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $this->output->writeln(''); + + return $value; + } + + throw new \RuntimeException('Unable to hide the response.'); + } + + protected function validateAttempts($interviewer) + { + /** @var \Exception $error */ + $error = null; + $attempts = $this->question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->output->error($error->getMessage()); + } + + try { + return call_user_func($this->question->getValidator(), $interviewer()); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * 显示问题的提示信息 + */ + protected function writePrompt() + { + $text = $this->question->getQuestion(); + $default = $this->question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $this->question instanceof Confirmation: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $this->question instanceof Choice && $this->question->isMultiselect(): + $choices = $this->question->getChoices(); + $default = explode(',', $default); + + foreach ($default as $key => $value) { + $default[$key] = $choices[trim($value)]; + } + + $text = sprintf(' %s [%s]:', $text, implode(', ', $default)); + + break; + + case $this->question instanceof Choice: + $choices = $this->question->getChoices(); + $text = sprintf(' %s [%s]:', $text, $choices[$default]); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, $default); + } + + $this->output->writeln($text); + + if ($this->question instanceof Choice) { + $width = max(array_map('strlen', array_keys($this->question->getChoices()))); + + foreach ($this->question->getChoices() as $key => $value) { + $this->output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $this->output->write(' > '); + } + + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = $exitcode === 0; + } +} diff --git a/source/thinkphp/library/think/console/output/Descriptor.php b/source/thinkphp/library/think/console/output/Descriptor.php new file mode 100644 index 0000000..23dc648 --- /dev/null +++ b/source/thinkphp/library/think/console/output/Descriptor.php @@ -0,0 +1,319 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output; + +use think\Console; +use think\console\Command; +use think\console\input\Argument as InputArgument; +use think\console\input\Definition as InputDefinition; +use think\console\input\Option as InputOption; +use think\console\Output; +use think\console\output\descriptor\Console as ConsoleDescription; + +class Descriptor +{ + + /** + * @var Output + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(Output $output, $object, array $options = []) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Console: + $this->describeConsole($object, $options); + break; + default: + throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object))); + } + } + + /** + * 输出内容 + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? Output::OUTPUT_NORMAL : Output::OUTPUT_RAW); + } + + /** + * 描述参数 + * @param InputArgument $argument + * @param array $options + * @return void + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + if (null !== $argument->getDefault() + && (!is_array($argument->getDefault()) + || count($argument->getDefault())) + ) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName()); + $spacingWidth = $totalWidth - strlen($argument->getName()) + 2; + + $this->writeText(sprintf(" %s%s%s%s", $argument->getName(), str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*\R\s*/', PHP_EOL . str_repeat(' ', $totalWidth + 17), $argument->getDescription()), $default), $options); + } + + /** + * 描述选项 + * @param InputOption $option + * @param array $options + * @return void + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + if ($option->acceptValue() && null !== $option->getDefault() + && (!is_array($option->getDefault()) + || count($option->getDefault())) + ) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '=' . strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '[' . $value . ']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]); + $synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', sprintf('--%s%s', $option->getName(), $value)); + + $spacingWidth = $totalWidth - strlen($synopsis) + 2; + + $this->writeText(sprintf(" %s%s%s%s%s", $synopsis, str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + + + 2 spaces + preg_replace('/\s*\R\s*/', "\n" . str_repeat(' ', $totalWidth + 17), $option->getDescription()), $default, $option->isArray() ? ' (multiple values allowed)' : ''), $options); + } + + /** + * 描述输入 + * @param InputDefinition $definition + * @param array $options + * @return void + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth])); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = []; + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); + } + } + } + + /** + * 描述指令 + * @param Command $command + * @param array $options + * @return void + */ + protected function describeCommand(Command $command, array $options = []) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeConsoleDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' ' . $usage, $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' ' . str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * 描述控制台 + * @param Console $console + * @param array $options + * @return void + */ + protected function describeConsole(Console $console, array $options = []) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ConsoleDescription($console, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $console->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($console->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $width = $this->getColumnWidth($description->getCommands()); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + // add commands by namespace + foreach ($description->getNamespaces() as $namespace) { + if (!$describedNamespace && ConsoleDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' ' . $namespace['id'] . '', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - strlen($name); + $this->writeText(sprintf(" %s%s%s", $name, str_repeat(' ', $spacingWidth), $description->getCommand($name) + ->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = []) + { + $this->write(isset($options['raw_text']) + && $options['raw_text'] ? strip_tags($content) : $content, isset($options['raw_output']) ? !$options['raw_output'] : true); + } + + /** + * 格式化 + * @param mixed $default + * @return string + */ + private function formatDefaultValue($default) + { + return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + /** + * @param Command[] $commands + * @return int + */ + private function getColumnWidth(array $commands) + { + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + + return $width + 2; + } + + /** + * @param InputOption[] $options + * @return int + */ + private function calculateTotalWidthForOptions($options) + { + $totalWidth = 0; + foreach ($options as $option) { + $nameLength = 4 + strlen($option->getName()) + 2; // - + shortcut + , + whitespace + name + -- + + if ($option->acceptValue()) { + $valueLength = 1 + strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/source/thinkphp/library/think/console/output/Formatter.php b/source/thinkphp/library/think/console/output/Formatter.php new file mode 100644 index 0000000..f8bee55 --- /dev/null +++ b/source/thinkphp/library/think/console/output/Formatter.php @@ -0,0 +1,198 @@ + +// +---------------------------------------------------------------------- +namespace think\console\output; + +use think\console\output\formatter\Stack as StyleStack; +use think\console\output\formatter\Style; + +class Formatter +{ + + private $decorated = false; + private $styles = []; + private $styleStack; + + /** + * 转义 + * @param string $text + * @return string + */ + public static function escape($text) + { + return preg_replace('/([^\\\\]?)setStyle('error', new Style('white', 'red')); + $this->setStyle('info', new Style('green')); + $this->setStyle('comment', new Style('yellow')); + $this->setStyle('question', new Style('black', 'cyan')); + $this->setStyle('highlight', new Style('red')); + $this->setStyle('warning', new Style('black', 'yellow')); + + $this->styleStack = new StyleStack(); + } + + /** + * 设置外观标识 + * @param bool $decorated 是否美化文字 + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * 获取外观标识 + * @return bool + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * 添加一个新样式 + * @param string $name 样式名 + * @param Style $style 样式实例 + */ + public function setStyle($name, Style $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * 是否有这个样式 + * @param string $name + * @return bool + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * 获取样式 + * @param string $name + * @return Style + * @throws \InvalidArgumentException + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * 使用所给的样式格式化文字 + * @param string $message 文字 + * @return string + */ + public function format($message) + { + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9_=;-]*'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + strlen($text); + + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + return str_replace('\\<', '<', $output); + } + + /** + * @return StyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * 根据字符串创建新的样式实例 + * @param string $string + * @return Style|bool + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new Style(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + try { + $style->setOption($match[1]); + } catch (\InvalidArgumentException $e) { + return false; + } + } + } + + return $style; + } + + /** + * 从堆栈应用样式到文字 + * @param string $text 文字 + * @return string + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/source/thinkphp/library/think/console/output/Question.php b/source/thinkphp/library/think/console/output/Question.php new file mode 100644 index 0000000..03975f2 --- /dev/null +++ b/source/thinkphp/library/think/console/output/Question.php @@ -0,0 +1,211 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output; + +class Question +{ + + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * 构造方法 + * @param string $question 问题 + * @param mixed $default 默认答案 + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * 获取问题 + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * 获取默认答案 + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * 是否隐藏答案 + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * 隐藏答案 + * @param bool $hidden + * @return Question + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new \LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * 不能被隐藏是否撤销 + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * 设置不能被隐藏的时候的操作 + * @param bool $fallback + * @return Question + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * 获取自动完成 + * @return null|array|\Traversable + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * 设置自动完成的值 + * @param null|array|\Traversable $values + * @return Question + * @throws \InvalidArgumentException + * @throws \LogicException + */ + public function setAutocompleterValues($values) + { + if (is_array($values) && $this->isAssoc($values)) { + $values = array_merge(array_keys($values), array_values($values)); + } + + if (null !== $values && !is_array($values)) { + if (!$values instanceof \Traversable || $values instanceof \Countable) { + throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.'); + } + } + + if ($this->hidden) { + throw new \LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * 设置答案的验证器 + * @param null|callable $validator + * @return Question The current instance + */ + public function setValidator($validator) + { + $this->validator = $validator; + + return $this; + } + + /** + * 获取验证器 + * @return null|callable + */ + public function getValidator() + { + return $this->validator; + } + + /** + * 设置最大重试次数 + * @param null|int $attempts + * @return Question + * @throws \InvalidArgumentException + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * 获取最大重试次数 + * @return null|int + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * 设置响应的回调 + * @param string|\Closure $normalizer + * @return Question + */ + public function setNormalizer($normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * 获取响应回调 + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * @return string|\Closure + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/source/thinkphp/library/think/console/output/descriptor/Console.php b/source/thinkphp/library/think/console/output/descriptor/Console.php new file mode 100644 index 0000000..4648b68 --- /dev/null +++ b/source/thinkphp/library/think/console/output/descriptor/Console.php @@ -0,0 +1,149 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\descriptor; + +use think\Console as ThinkConsole; +use think\console\Command; + +class Console +{ + + const GLOBAL_NAMESPACE = '_global'; + + /** + * @var ThinkConsole + */ + private $console; + + /** + * @var null|string + */ + private $namespace; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * 构造方法 + * @param ThinkConsole $console + * @param string|null $namespace + */ + public function __construct(ThinkConsole $console, $namespace = null) + { + $this->console = $console; + $this->namespace = $namespace; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectConsole(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectConsole(); + } + + return $this->commands; + } + + /** + * @param string $name + * @return Command + * @throws \InvalidArgumentException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectConsole() + { + $this->commands = []; + $this->namespaces = []; + + $all = $this->console->all($this->namespace ? $this->console->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = []; + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName()) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names]; + } + } + + /** + * @param array $commands + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = []; + foreach ($commands as $name => $command) { + $key = $this->console->extractNamespace($name, 1); + if (!$key) { + $key = self::GLOBAL_NAMESPACE; + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/source/thinkphp/library/think/console/output/driver/Buffer.php b/source/thinkphp/library/think/console/output/driver/Buffer.php new file mode 100644 index 0000000..c77a2ec --- /dev/null +++ b/source/thinkphp/library/think/console/output/driver/Buffer.php @@ -0,0 +1,52 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\driver; + +use think\console\Output; + +class Buffer +{ + /** + * @var string + */ + private $buffer = ''; + + public function __construct(Output $output) + { + // do nothing + } + + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + return $content; + } + + public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + foreach ($messages as $message) { + $this->buffer .= $message; + } + if ($newline) { + $this->buffer .= "\n"; + } + } + + public function renderException(\Exception $e) + { + // do nothing + } + +} diff --git a/source/thinkphp/library/think/console/output/driver/Console.php b/source/thinkphp/library/think/console/output/driver/Console.php new file mode 100644 index 0000000..8f29fd0 --- /dev/null +++ b/source/thinkphp/library/think/console/output/driver/Console.php @@ -0,0 +1,373 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\driver; + +use think\console\Output; +use think\console\output\Formatter; + +class Console +{ + + /** @var Resource */ + private $stdout; + + /** @var Formatter */ + private $formatter; + + private $terminalDimensions; + + /** @var Output */ + private $output; + + public function __construct(Output $output) + { + $this->output = $output; + $this->formatter = new Formatter(); + $this->stdout = $this->openOutputStream(); + $decorated = $this->hasColorSupport($this->stdout); + $this->formatter->setDecorated($decorated); + } + + public function getFormatter() + { + return $this->formatter; + } + + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + public function write($messages, $newline = false, $type = Output::OUTPUT_NORMAL, $stream = null) + { + if (Output::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $messages = (array) $messages; + + foreach ($messages as $message) { + switch ($type) { + case Output::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case Output::OUTPUT_RAW: + break; + case Output::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline, $stream); + } + } + + public function renderException(\Exception $e) + { + $stderr = $this->openErrorStream(); + $decorated = $this->hasColorSupport($stderr); + $this->formatter->setDecorated($decorated); + + do { + $title = sprintf(' [%s] ', get_class($e)); + + $len = $this->stringWidth($title); + + $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX; + + if (defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $lines = []; + foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + + $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $line)) + 4; + $lines[] = [$line, $lineLength]; + + $len = max($lineLength, $len); + } + } + + $messages = ['', '']; + $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))); + foreach ($lines as $line) { + $messages[] = sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1])); + } + $messages[] = $emptyLine; + $messages[] = ''; + $messages[] = ''; + + $this->write($messages, true, Output::OUTPUT_NORMAL, $stderr); + + if (Output::VERBOSITY_VERBOSE <= $this->output->getVerbosity()) { + $this->write('Exception trace:', true, Output::OUTPUT_NORMAL, $stderr); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, [ + 'function' => '', + 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', + 'args' => [], + ]); + + for ($i = 0, $count = count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $this->write(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr); + } + + $this->write('', true, Output::OUTPUT_NORMAL, $stderr); + $this->write('', true, Output::OUTPUT_NORMAL, $stderr); + } + } while ($e = $e->getPrevious()); + + } + + /** + * 获取终端宽度 + * @return int|null + */ + protected function getTerminalWidth() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[0]; + } + + /** + * 获取终端高度 + * @return int|null + */ + protected function getTerminalHeight() + { + $dimensions = $this->getTerminalDimensions(); + + return $dimensions[1]; + } + + /** + * 获取当前终端的尺寸 + * @return array + */ + public function getTerminalDimensions() + { + if ($this->terminalDimensions) { + return $this->terminalDimensions; + } + + if ('\\' === DS) { + if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) { + return [(int) $matches[1], (int) $matches[2]]; + } + if (preg_match('/^(\d+)x(\d+)$/', $this->getMode(), $matches)) { + return [(int) $matches[1], (int) $matches[2]]; + } + } + + if ($sttyString = $this->getSttyColumns()) { + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + return [(int) $matches[2], (int) $matches[1]]; + } + if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + return [(int) $matches[2], (int) $matches[1]]; + } + } + + return [null, null]; + } + + /** + * 获取stty列数 + * @return string + */ + private function getSttyColumns() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + return; + } + + /** + * 获取终端模式 + * @return string x 或 null + */ + private function getMode() + { + if (!function_exists('proc_open')) { + return; + } + + $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return $matches[2] . 'x' . $matches[1]; + } + } + return; + } + + private function stringWidth($string) + { + if (!function_exists('mb_strwidth')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + private function splitStringByWidth($string, $width) + { + if (!function_exists('mb_strwidth')) { + return str_split($string, $width); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = []; + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + if (mb_strwidth($line . $char, 'utf8') <= $width) { + $line .= $char; + continue; + } + $lines[] = str_pad($line, $width); + $line = $char; + } + if (strlen($line)) { + $lines[] = count($lines) ? str_pad($line, $width) : $line; + } + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + private function isRunningOS400() + { + $checks = [ + function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ]; + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * 当前环境是否支持写入控制台输出到stdout. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * 当前环境是否支持写入控制台输出到stderr. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * @return resource + */ + private function openOutputStream() + { + if (!$this->hasStdoutSupport()) { + return fopen('php://output', 'w'); + } + return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); + } + + /** + * 将消息写入到输出。 + * @param string $message 消息 + * @param bool $newline 是否另起一行 + * @param null $stream + */ + protected function doWrite($message, $newline, $stream = null) + { + if (null === $stream) { + $stream = $this->stdout; + } + if (false === @fwrite($stream, $message . ($newline ? PHP_EOL : ''))) { + throw new \RuntimeException('Unable to write output.'); + } + + fflush($stream); + } + + /** + * 是否支持着色 + * @param $stream + * @return bool + */ + protected function hasColorSupport($stream) + { + if (DIRECTORY_SEPARATOR === '\\') { + return + '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && @posix_isatty($stream); + } + +} diff --git a/source/thinkphp/library/think/console/output/driver/Nothing.php b/source/thinkphp/library/think/console/output/driver/Nothing.php new file mode 100644 index 0000000..9a55f77 --- /dev/null +++ b/source/thinkphp/library/think/console/output/driver/Nothing.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\driver; + +use think\console\Output; + +class Nothing +{ + + public function __construct(Output $output) + { + // do nothing + } + + public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL) + { + // do nothing + } + + public function renderException(\Exception $e) + { + // do nothing + } +} diff --git a/source/thinkphp/library/think/console/output/formatter/Stack.php b/source/thinkphp/library/think/console/output/formatter/Stack.php new file mode 100644 index 0000000..4864a3f --- /dev/null +++ b/source/thinkphp/library/think/console/output/formatter/Stack.php @@ -0,0 +1,116 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\formatter; + +class Stack +{ + + /** + * @var Style[] + */ + private $styles; + + /** + * @var Style + */ + private $emptyStyle; + + /** + * 构造方法 + * @param Style|null $emptyStyle + */ + public function __construct(Style $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new Style(); + $this->reset(); + } + + /** + * 重置堆栈 + */ + public function reset() + { + $this->styles = []; + } + + /** + * 推一个样式进入堆栈 + * @param Style $style + */ + public function push(Style $style) + { + $this->styles[] = $style; + } + + /** + * 从堆栈中弹出一个样式 + * @param Style|null $style + * @return Style + * @throws \InvalidArgumentException + */ + public function pop(Style $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + /** + * @var int $index + * @var Style $stackedStyle + */ + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new \InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * 计算堆栈的当前样式。 + * @return Style + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[count($this->styles) - 1]; + } + + /** + * @param Style $emptyStyle + * @return Stack + */ + public function setEmptyStyle(Style $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return Style + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/source/thinkphp/library/think/console/output/formatter/Style.php b/source/thinkphp/library/think/console/output/formatter/Style.php new file mode 100644 index 0000000..d9b0999 --- /dev/null +++ b/source/thinkphp/library/think/console/output/formatter/Style.php @@ -0,0 +1,189 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\formatter; + +class Style +{ + + private static $availableForegroundColors = [ + 'black' => ['set' => 30, 'unset' => 39], + 'red' => ['set' => 31, 'unset' => 39], + 'green' => ['set' => 32, 'unset' => 39], + 'yellow' => ['set' => 33, 'unset' => 39], + 'blue' => ['set' => 34, 'unset' => 39], + 'magenta' => ['set' => 35, 'unset' => 39], + 'cyan' => ['set' => 36, 'unset' => 39], + 'white' => ['set' => 37, 'unset' => 39], + ]; + private static $availableBackgroundColors = [ + 'black' => ['set' => 40, 'unset' => 49], + 'red' => ['set' => 41, 'unset' => 49], + 'green' => ['set' => 42, 'unset' => 49], + 'yellow' => ['set' => 43, 'unset' => 49], + 'blue' => ['set' => 44, 'unset' => 49], + 'magenta' => ['set' => 45, 'unset' => 49], + 'cyan' => ['set' => 46, 'unset' => 49], + 'white' => ['set' => 47, 'unset' => 49], + ]; + private static $availableOptions = [ + 'bold' => ['set' => 1, 'unset' => 22], + 'underscore' => ['set' => 4, 'unset' => 24], + 'blink' => ['set' => 5, 'unset' => 25], + 'reverse' => ['set' => 7, 'unset' => 27], + 'conceal' => ['set' => 8, 'unset' => 28], + ]; + + private $foreground; + private $background; + private $options = []; + + /** + * 初始化输出的样式 + * @param string|null $foreground 字体颜色 + * @param string|null $background 背景色 + * @param array $options 格式 + * @api + */ + public function __construct($foreground = null, $background = null, array $options = []) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * 设置字体颜色 + * @param string|null $color 颜色名 + * @throws \InvalidArgumentException + * @api + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new \InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors)))); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * 设置背景色 + * @param string|null $color 颜色名 + * @throws \InvalidArgumentException + * @api + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new \InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors)))); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * 设置字体格式 + * @param string $option 格式名 + * @throws \InvalidArgumentException When the option name isn't defined + * @api + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions)))); + } + + if (!in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * 重置字体格式 + * @param string $option 格式名 + * @throws \InvalidArgumentException + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions)))); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * 批量设置字体格式 + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = []; + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * 应用样式到文字 + * @param string $text 文字 + * @return string + */ + public function apply($text) + { + $setCodes = []; + $unsetCodes = []; + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/source/thinkphp/library/think/console/output/question/Choice.php b/source/thinkphp/library/think/console/output/question/Choice.php new file mode 100644 index 0000000..f6760e5 --- /dev/null +++ b/source/thinkphp/library/think/console/output/question/Choice.php @@ -0,0 +1,163 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\question; + +use think\console\output\Question; + +class Choice extends Question +{ + + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * 构造方法 + * @param string $question 问题 + * @param array $choices 选项 + * @param mixed $default 默认答案 + */ + public function __construct($question, array $choices, $default = null) + { + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * 可选项 + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * 设置可否多选 + * @param bool $multiselect + * @return self + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + public function isMultiselect() + { + return $this->multiselect; + } + + /** + * 获取提示 + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * 设置提示 + * @param string $prompt + * @return self + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * 设置错误提示信息 + * @param string $errorMessage + * @return self + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * 获取默认的验证方法 + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + // Collapse all spaces. + $selectedChoices = str_replace(' ', '', $selected); + + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) { + throw new \InvalidArgumentException(sprintf($errorMessage, $selected)); + } + $selectedChoices = explode(',', $selectedChoices); + } else { + $selectedChoices = [$selected]; + } + + $multiselectChoices = []; + foreach ($selectedChoices as $value) { + $results = []; + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (count($results) > 1) { + throw new \InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (!empty($result)) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (empty($result) && array_key_exists($value, $choices)) { + $result = $value; + } + + if (empty($result)) { + throw new \InvalidArgumentException(sprintf($errorMessage, $value)); + } + array_push($multiselectChoices, $result); + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/source/thinkphp/library/think/console/output/question/Confirmation.php b/source/thinkphp/library/think/console/output/question/Confirmation.php new file mode 100644 index 0000000..6598f9b --- /dev/null +++ b/source/thinkphp/library/think/console/output/question/Confirmation.php @@ -0,0 +1,57 @@ + +// +---------------------------------------------------------------------- + +namespace think\console\output\question; + +use think\console\output\Question; + +class Confirmation extends Question +{ + + private $trueAnswerRegex; + + /** + * 构造方法 + * @param string $question 问题 + * @param bool $default 默认答案 + * @param string $trueAnswerRegex 验证正则 + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * 获取默认的答案回调 + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return !$answer || $answerIsTrue; + }; + } +} diff --git a/source/thinkphp/library/think/controller/Rest.php b/source/thinkphp/library/think/controller/Rest.php new file mode 100644 index 0000000..43ab2f6 --- /dev/null +++ b/source/thinkphp/library/think/controller/Rest.php @@ -0,0 +1,99 @@ + +// +---------------------------------------------------------------------- + +namespace think\controller; + +use think\App; +use think\Request; +use think\Response; + +abstract class Rest +{ + + protected $method; // 当前请求类型 + protected $type; // 当前资源类型 + // 输出类型 + protected $restMethodList = 'get|post|put|delete'; + protected $restDefaultMethod = 'get'; + protected $restTypeList = 'html|xml|json|rss'; + protected $restDefaultType = 'html'; + protected $restOutputType = [ // REST允许输出的资源类型列表 + 'xml' => 'application/xml', + 'json' => 'application/json', + 'html' => 'text/html', + ]; + + /** + * 构造函数 取得模板对象实例 + * @access public + */ + public function __construct() + { + // 资源类型检测 + $request = Request::instance(); + $ext = $request->ext(); + if ('' == $ext) { + // 自动检测资源类型 + $this->type = $request->type(); + } elseif (!preg_match('/(' . $this->restTypeList . ')$/i', $ext)) { + // 资源类型非法 则用默认资源类型访问 + $this->type = $this->restDefaultType; + } else { + $this->type = $ext; + } + // 请求方式检测 + $method = strtolower($request->method()); + if (!preg_match('/(' . $this->restMethodList . ')$/i', $method)) { + // 请求方式非法 则用默认请求方法 + $method = $this->restDefaultMethod; + } + $this->method = $method; + } + + /** + * REST 调用 + * @access public + * @param string $method 方法名 + * @return mixed + * @throws \Exception + */ + public function _empty($method) + { + if (method_exists($this, $method . '_' . $this->method . '_' . $this->type)) { + // RESTFul方法支持 + $fun = $method . '_' . $this->method . '_' . $this->type; + } elseif ($this->method == $this->restDefaultMethod && method_exists($this, $method . '_' . $this->type)) { + $fun = $method . '_' . $this->type; + } elseif ($this->type == $this->restDefaultType && method_exists($this, $method . '_' . $this->method)) { + $fun = $method . '_' . $this->method; + } + if (isset($fun)) { + return App::invokeMethod([$this, $fun]); + } else { + // 抛出异常 + throw new \Exception('error action :' . $method); + } + } + + /** + * 输出返回数据 + * @access protected + * @param mixed $data 要返回的数据 + * @param String $type 返回类型 JSON XML + * @param integer $code HTTP状态码 + * @return Response + */ + protected function response($data, $type = 'json', $code = 200) + { + return Response::create($data, $type)->code($code); + } + +} diff --git a/source/thinkphp/library/think/controller/Yar.php b/source/thinkphp/library/think/controller/Yar.php new file mode 100644 index 0000000..af4e9a1 --- /dev/null +++ b/source/thinkphp/library/think/controller/Yar.php @@ -0,0 +1,51 @@ + +// +---------------------------------------------------------------------- + +namespace think\controller; + +/** + * ThinkPHP Yar控制器类 + */ +abstract class Yar +{ + + /** + * 构造函数 + * @access public + */ + public function __construct() + { + //控制器初始化 + if (method_exists($this, '_initialize')) { + $this->_initialize(); + } + + //判断扩展是否存在 + if (!extension_loaded('yar')) { + throw new \Exception('not support yar'); + } + + //实例化Yar_Server + $server = new \Yar_Server($this); + // 启动server + $server->handle(); + } + + /** + * 魔术方法 有不存在的操作的时候执行 + * @access public + * @param string $method 方法名 + * @param array $args 参数 + * @return mixed + */ + public function __call($method, $args) + {} +} diff --git a/source/thinkphp/library/think/db/Builder.php b/source/thinkphp/library/think/db/Builder.php new file mode 100644 index 0000000..58b45aa --- /dev/null +++ b/source/thinkphp/library/think/db/Builder.php @@ -0,0 +1,899 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +use PDO; +use think\Exception; + +abstract class Builder +{ + // connection对象实例 + protected $connection; + // 查询对象实例 + protected $query; + + // 数据库表达式 + protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME']; + + // SQL表达式 + protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%UNION%%ORDER%%LIMIT%%LOCK%%COMMENT%'; + protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * 构造函数 + * @access public + * @param Connection $connection 数据库连接对象实例 + * @param Query $query 数据库查询对象实例 + */ + public function __construct(Connection $connection, Query $query) + { + $this->connection = $connection; + $this->query = $query; + } + + /** + * 获取当前的连接对象实例 + * @access public + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * 获取当前的Query对象实例 + * @access public + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) + * @access protected + * @param string $sql sql语句 + * @return string + */ + protected function parseSqlTable($sql) + { + return $this->query->parseSqlTable($sql); + } + + /** + * 数据分析 + * @access protected + * @param array $data 数据 + * @param array $options 查询参数 + * @return array + * @throws Exception + */ + protected function parseData($data, $options) + { + if (empty($data)) { + return []; + } + + // 获取绑定信息 + $bind = $this->query->getFieldsBind($options['table']); + if ('*' == $options['field']) { + $fields = array_keys($bind); + } else { + $fields = $options['field']; + } + + $result = []; + foreach ($data as $key => $val) { + if ('*' != $options['field'] && !in_array($key, $fields, true)) { + continue; + } + + $item = $this->parseKey($key, $options, true); + if ($val instanceof Expression) { + $result[$item] = $val->getValue(); + continue; + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $val = $val->__toString(); + } + if (false === strpos($key, '.') && !in_array($key, $fields, true)) { + if ($options['strict']) { + throw new Exception('fields not exists:[' . $key . ']'); + } + } elseif (is_null($val)) { + $result[$item] = 'NULL'; + } elseif (is_array($val) && !empty($val)) { + switch (strtolower($val[0])) { + case 'inc': + $result[$item] = $item . '+' . floatval($val[1]); + break; + case 'dec': + $result[$item] = $item . '-' . floatval($val[1]); + break; + case 'exp': + throw new Exception('not support data:[' . $val[0] . ']'); + } + } elseif (is_scalar($val)) { + // 过滤非标量数据 + if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) { + $result[$item] = $val; + } else { + $key = str_replace('.', '_', $key); + $this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR); + $result[$item] = ':data__' . $key; + } + } + } + return $result; + } + + /** + * 字段名分析 + * @access protected + * @param string $key + * @param array $options + * @return string + */ + protected function parseKey($key, $options = [], $strict = false) + { + return $key; + } + + /** + * value分析 + * @access protected + * @param mixed $value + * @param string $field + * @return string|array + */ + protected function parseValue($value, $field = '') + { + if (is_string($value)) { + $value = strpos($value, ':') === 0 && $this->query->isBind(substr($value, 1)) ? $value : $this->connection->quote($value); + } elseif (is_array($value)) { + $value = array_map([$this, 'parseValue'], $value); + } elseif (is_bool($value)) { + $value = $value ? '1' : '0'; + } elseif (is_null($value)) { + $value = 'null'; + } + return $value; + } + + /** + * field分析 + * @access protected + * @param mixed $fields + * @param array $options + * @return string + */ + protected function parseField($fields, $options = []) + { + if ('*' == $fields || empty($fields)) { + $fieldsStr = '*'; + } elseif (is_array($fields)) { + // 支持 'field1'=>'field2' 这样的字段别名定义 + $array = []; + foreach ($fields as $key => $field) { + if ($field instanceof Expression) { + $array[] = $field->getValue(); + } elseif (!is_numeric($key)) { + $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true); + } else { + $array[] = $this->parseKey($field, $options); + } + } + $fieldsStr = implode(',', $array); + } + return $fieldsStr; + } + + /** + * table分析 + * @access protected + * @param mixed $tables + * @param array $options + * @return string + */ + protected function parseTable($tables, $options = []) + { + $item = []; + foreach ((array) $tables as $key => $table) { + if (!is_numeric($key)) { + $key = $this->parseSqlTable($key); + $item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table)); + } else { + $table = $this->parseSqlTable($table); + if (isset($options['alias'][$table])) { + $item[] = $this->parseKey($table) . ' ' . $this->parseKey($options['alias'][$table]); + } else { + $item[] = $this->parseKey($table); + } + } + } + return implode(',', $item); + } + + /** + * where分析 + * @access protected + * @param mixed $where 查询条件 + * @param array $options 查询参数 + * @return string + */ + protected function parseWhere($where, $options) + { + $whereStr = $this->buildWhere($where, $options); + if (!empty($options['soft_delete'])) { + // 附加软删除条件 + list($field, $condition) = $options['soft_delete']; + + $binds = $this->query->getFieldsBind($options['table']); + $whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : ''; + $whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds); + } + return empty($whereStr) ? '' : ' WHERE ' . $whereStr; + } + + /** + * 生成查询条件SQL + * @access public + * @param mixed $where + * @param array $options + * @return string + */ + public function buildWhere($where, $options) + { + if (empty($where)) { + $where = []; + } + + if ($where instanceof Query) { + return $this->buildWhere($where->getOptions('where'), $options); + } + + $whereStr = ''; + $binds = $this->query->getFieldsBind($options['table']); + foreach ($where as $key => $val) { + $str = []; + foreach ($val as $field => $value) { + if ($value instanceof Expression) { + $str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )'; + } elseif ($value instanceof \Closure) { + // 使用闭包查询 + $query = new Query($this->connection); + call_user_func_array($value, [ & $query]); + $whereClause = $this->buildWhere($query->getOptions('where'), $options); + if (!empty($whereClause)) { + $str[] = ' ' . $key . ' ( ' . $whereClause . ' )'; + } + } elseif (strpos($field, '|')) { + // 不同字段使用相同查询条件(OR) + $array = explode('|', $field); + $item = []; + foreach ($array as $k) { + $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); + } + $str[] = ' ' . $key . ' ( ' . implode(' OR ', $item) . ' )'; + } elseif (strpos($field, '&')) { + // 不同字段使用相同查询条件(AND) + $array = explode('&', $field); + $item = []; + foreach ($array as $k) { + $item[] = $this->parseWhereItem($k, $value, '', $options, $binds); + } + $str[] = ' ' . $key . ' ( ' . implode(' AND ', $item) . ' )'; + } else { + // 对字段使用表达式查询 + $field = is_string($field) ? $field : ''; + $str[] = ' ' . $key . ' ' . $this->parseWhereItem($field, $value, $key, $options, $binds); + } + } + + $whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str); + } + + return $whereStr; + } + + // where子单元分析 + protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) + { + // 字段分析 + $key = $field ? $this->parseKey($field, $options, true) : ''; + + // 查询规则和条件 + if (!is_array($val)) { + $val = is_null($val) ? ['null', ''] : ['=', $val]; + } + list($exp, $value) = $val; + + // 对一个字段使用多个查询条件 + if (is_array($exp)) { + $item = array_pop($val); + // 传入 or 或者 and + if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) { + $rule = $item; + } else { + array_push($val, $item); + } + foreach ($val as $k => $item) { + $bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k; + $str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName); + } + return '( ' . implode(' ' . $rule . ' ', $str) . ' )'; + } + + // 检测操作符 + if (!in_array($exp, $this->exp)) { + $exp = strtolower($exp); + if (isset($this->exp[$exp])) { + $exp = $this->exp[$exp]; + } else { + throw new Exception('where express error:' . $exp); + } + } + $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field); + if (preg_match('/\W/', $bindName)) { + // 处理带非单词字符的字段名 + $bindName = md5($bindName); + } + + if ($value instanceof Expression) { + + } elseif (is_object($value) && method_exists($value, '__toString')) { + // 对象数据写入 + $value = $value->__toString(); + } + + $bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR; + if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) { + if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) { + if ($this->query->isBind($bindName)) { + $bindName .= '_' . str_replace('.', '_', uniqid('', true)); + } + $this->query->bind($bindName, $value, $bindType); + $value = ':' . $bindName; + } + } + + $whereStr = ''; + if (in_array($exp, ['=', '<>', '>', '>=', '<', '<='])) { + // 比较运算 + if ($value instanceof \Closure) { + $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value); + } else { + $whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field); + } + } elseif ('LIKE' == $exp || 'NOT LIKE' == $exp) { + // 模糊匹配 + if (is_array($value)) { + foreach ($value as $item) { + $array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field); + } + $logic = isset($val[2]) ? $val[2] : 'AND'; + $whereStr .= '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')'; + } else { + $whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field); + } + } elseif ('EXP' == $exp) { + // 表达式查询 + if ($value instanceof Expression) { + $whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )'; + } else { + throw new Exception('where express error:' . $exp); + } + } elseif (in_array($exp, ['NOT NULL', 'NULL'])) { + // NULL 查询 + $whereStr .= $key . ' IS ' . $exp; + } elseif (in_array($exp, ['NOT IN', 'IN'])) { + // IN 查询 + if ($value instanceof \Closure) { + $whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value); + } else { + $value = array_unique(is_array($value) ? $value : explode(',', $value)); + if (array_key_exists($field, $binds)) { + $bind = []; + $array = []; + $i = 0; + foreach ($value as $v) { + $i++; + if ($this->query->isBind($bindName . '_in_' . $i)) { + $bindKey = $bindName . '_in_' . uniqid() . '_' . $i; + } else { + $bindKey = $bindName . '_in_' . $i; + } + $bind[$bindKey] = [$v, $bindType]; + $array[] = ':' . $bindKey; + } + $this->query->bind($bind); + $zone = implode(',', $array); + } else { + $zone = implode(',', $this->parseValue($value, $field)); + } + $whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')'; + } + } elseif (in_array($exp, ['NOT BETWEEN', 'BETWEEN'])) { + // BETWEEN 查询 + $data = is_array($value) ? $value : explode(',', $value); + if (array_key_exists($field, $binds)) { + if ($this->query->isBind($bindName . '_between_1')) { + $bindKey1 = $bindName . '_between_1' . uniqid(); + $bindKey2 = $bindName . '_between_2' . uniqid(); + } else { + $bindKey1 = $bindName . '_between_1'; + $bindKey2 = $bindName . '_between_2'; + } + $bind = [ + $bindKey1 => [$data[0], $bindType], + $bindKey2 => [$data[1], $bindType], + ]; + $this->query->bind($bind); + $between = ':' . $bindKey1 . ' AND :' . $bindKey2; + } else { + $between = $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field); + } + $whereStr .= $key . ' ' . $exp . ' ' . $between; + } elseif (in_array($exp, ['NOT EXISTS', 'EXISTS'])) { + // EXISTS 查询 + if ($value instanceof \Closure) { + $whereStr .= $exp . ' ' . $this->parseClosure($value); + } else { + $whereStr .= $exp . ' (' . $value . ')'; + } + } elseif (in_array($exp, ['< TIME', '> TIME', '<= TIME', '>= TIME'])) { + $whereStr .= $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($value, $field, $options, $bindName, $bindType); + } elseif (in_array($exp, ['BETWEEN TIME', 'NOT BETWEEN TIME'])) { + if (is_string($value)) { + $value = explode(',', $value); + } + + $whereStr .= $key . ' ' . substr($exp, 0, -4) . $this->parseDateTime($value[0], $field, $options, $bindName . '_between_1', $bindType) . ' AND ' . $this->parseDateTime($value[1], $field, $options, $bindName . '_between_2', $bindType); + } + return $whereStr; + } + + // 执行闭包子查询 + protected function parseClosure($call, $show = true) + { + $query = new Query($this->connection); + call_user_func_array($call, [ & $query]); + return $query->buildSql($show); + } + + /** + * 日期时间条件解析 + * @access protected + * @param string $value + * @param string $key + * @param array $options + * @param string $bindName + * @param integer $bindType + * @return string + */ + protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null) + { + // 获取时间字段类型 + if (strpos($key, '.')) { + list($table, $key) = explode('.', $key); + if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) { + $table = $pos; + } + } else { + $table = $options['table']; + } + $type = $this->query->getTableInfo($table, 'type'); + if (isset($type[$key])) { + $info = $type[$key]; + } + if (isset($info)) { + if (is_string($value)) { + $value = strtotime($value) ?: $value; + } + + if (preg_match('/(datetime|timestamp)/is', $info)) { + // 日期及时间戳类型 + $value = date('Y-m-d H:i:s', $value); + } elseif (preg_match('/(date)/is', $info)) { + // 日期及时间戳类型 + $value = date('Y-m-d', $value); + } + } + $bindName = $bindName ?: $key; + + if ($this->query->isBind($bindName)) { + $bindName .= '_' . str_replace('.', '_', uniqid('', true)); + } + + $this->query->bind($bindName, $value, $bindType); + return ':' . $bindName; + } + + /** + * limit分析 + * @access protected + * @param mixed $limit + * @return string + */ + protected function parseLimit($limit) + { + return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : ''; + } + + /** + * join分析 + * @access protected + * @param array $join + * @param array $options 查询条件 + * @return string + */ + protected function parseJoin($join, $options = []) + { + $joinStr = ''; + if (!empty($join)) { + foreach ($join as $item) { + list($table, $type, $on) = $item; + $condition = []; + foreach ((array) $on as $val) { + if ($val instanceof Expression) { + $condition[] = $val->getValue(); + } elseif (strpos($val, '=')) { + list($val1, $val2) = explode('=', $val, 2); + $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); + } else { + $condition[] = $val; + } + } + + $table = $this->parseTable($table, $options); + $joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition); + } + } + return $joinStr; + } + + /** + * order分析 + * @access protected + * @param mixed $order + * @param array $options 查询条件 + * @return string + */ + protected function parseOrder($order, $options = []) + { + if (empty($order)) { + return ''; + } + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); + } else { + if (is_numeric($key)) { + list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' '); + } else { + $sort = $val; + } + $sort = strtoupper($sort); + $sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : ''; + $array[] = $this->parseKey($key, $options, true) . $sort; + } + } + $order = implode(',', $array); + + return !empty($order) ? ' ORDER BY ' . $order : ''; + } + + /** + * group分析 + * @access protected + * @param mixed $group + * @return string + */ + protected function parseGroup($group) + { + return !empty($group) ? ' GROUP BY ' . $this->parseKey($group) : ''; + } + + /** + * having分析 + * @access protected + * @param string $having + * @return string + */ + protected function parseHaving($having) + { + return !empty($having) ? ' HAVING ' . $having : ''; + } + + /** + * comment分析 + * @access protected + * @param string $comment + * @return string + */ + protected function parseComment($comment) + { + if (false !== strpos($comment, '*/')) { + $comment = strstr($comment, '*/', true); + } + return !empty($comment) ? ' /* ' . $comment . ' */' : ''; + } + + /** + * distinct分析 + * @access protected + * @param mixed $distinct + * @return string + */ + protected function parseDistinct($distinct) + { + return !empty($distinct) ? ' DISTINCT ' : ''; + } + + /** + * union分析 + * @access protected + * @param mixed $union + * @return string + */ + protected function parseUnion($union) + { + if (empty($union)) { + return ''; + } + $type = $union['type']; + unset($union['type']); + foreach ($union as $u) { + if ($u instanceof \Closure) { + $sql[] = $type . ' ' . $this->parseClosure($u); + } elseif (is_string($u)) { + $sql[] = $type . ' ( ' . $this->parseSqlTable($u) . ' )'; + } + } + return ' ' . implode(' ', $sql); + } + + /** + * index分析,可在操作链中指定需要强制使用的索引 + * @access protected + * @param mixed $index + * @return string + */ + protected function parseForce($index) + { + if (empty($index)) { + return ''; + } + + return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index); + } + + /** + * 设置锁机制 + * @access protected + * @param bool|string $lock + * @return string + */ + protected function parseLock($lock = false) + { + if (is_bool($lock)) { + return $lock ? ' FOR UPDATE ' : ''; + } elseif (is_string($lock)) { + return ' ' . trim($lock) . ' '; + } + } + + /** + * 生成查询SQL + * @access public + * @param array $options 表达式 + * @return string + */ + public function select($options = []) + { + $sql = str_replace( + ['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'], + [ + $this->parseTable($options['table'], $options), + $this->parseDistinct($options['distinct']), + $this->parseField($options['field'], $options), + $this->parseJoin($options['join'], $options), + $this->parseWhere($options['where'], $options), + $this->parseGroup($options['group']), + $this->parseHaving($options['having']), + $this->parseOrder($options['order'], $options), + $this->parseLimit($options['limit']), + $this->parseUnion($options['union']), + $this->parseLock($options['lock']), + $this->parseComment($options['comment']), + $this->parseForce($options['force']), + ], $this->selectSql); + return $sql; + } + + /** + * 生成insert SQL + * @access public + * @param array $data 数据 + * @param array $options 表达式 + * @param bool $replace 是否replace + * @return string + */ + public function insert(array $data, $options = [], $replace = false) + { + // 分析并处理数据 + $data = $this->parseData($data, $options); + if (empty($data)) { + return 0; + } + $fields = array_keys($data); + $values = array_values($data); + + $sql = str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($options['table'], $options), + implode(' , ', $fields), + implode(' , ', $values), + $this->parseComment($options['comment']), + ], $this->insertSql); + + return $sql; + } + + /** + * 生成insertall SQL + * @access public + * @param array $dataSet 数据集 + * @param array $options 表达式 + * @param bool $replace 是否replace + * @return string + * @throws Exception + */ + public function insertAll($dataSet, $options = [], $replace = false) + { + // 获取合法的字段 + if ('*' == $options['field']) { + $fields = array_keys($this->query->getFieldsType($options['table'])); + } else { + $fields = $options['field']; + } + + foreach ($dataSet as $data) { + foreach ($data as $key => $val) { + if (!in_array($key, $fields, true)) { + if ($options['strict']) { + throw new Exception('fields not exists:[' . $key . ']'); + } + unset($data[$key]); + } elseif (is_null($val)) { + $data[$key] = 'NULL'; + } elseif (is_scalar($val)) { + $data[$key] = $this->parseValue($val, $key); + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $data[$key] = $val->__toString(); + } else { + // 过滤掉非标量数据 + unset($data[$key]); + } + } + $value = array_values($data); + $values[] = 'SELECT ' . implode(',', $value); + + if (!isset($insertFields)) { + $insertFields = array_keys($data); + } + } + + foreach ($insertFields as $field) { + $fields[] = $this->parseKey($field, $options, true); + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($options['table'], $options), + implode(' , ', $insertFields), + implode(' UNION ALL ', $values), + $this->parseComment($options['comment']), + ], $this->insertAllSql); + } + + /** + * 生成select insert SQL + * @access public + * @param array $fields 数据 + * @param string $table 数据表 + * @param array $options 表达式 + * @return string + */ + public function selectInsert($fields, $table, $options) + { + if (is_string($fields)) { + $fields = explode(',', $fields); + } + + $fields = array_map([$this, 'parseKey'], $fields); + $sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options); + return $sql; + } + + /** + * 生成update SQL + * @access public + * @param array $data 数据 + * @param array $options 表达式 + * @return string + */ + public function update($data, $options) + { + $table = $this->parseTable($options['table'], $options); + $data = $this->parseData($data, $options); + if (empty($data)) { + return ''; + } + foreach ($data as $key => $val) { + $set[] = $key . '=' . $val; + } + + $sql = str_replace( + ['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + [ + $this->parseTable($options['table'], $options), + implode(',', $set), + $this->parseJoin($options['join'], $options), + $this->parseWhere($options['where'], $options), + $this->parseOrder($options['order'], $options), + $this->parseLimit($options['limit']), + $this->parseLock($options['lock']), + $this->parseComment($options['comment']), + ], $this->updateSql); + + return $sql; + } + + /** + * 生成delete SQL + * @access public + * @param array $options 表达式 + * @return string + */ + public function delete($options) + { + $sql = str_replace( + ['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'], + [ + $this->parseTable($options['table'], $options), + !empty($options['using']) ? ' USING ' . $this->parseTable($options['using'], $options) . ' ' : '', + $this->parseJoin($options['join'], $options), + $this->parseWhere($options['where'], $options), + $this->parseOrder($options['order'], $options), + $this->parseLimit($options['limit']), + $this->parseLock($options['lock']), + $this->parseComment($options['comment']), + ], $this->deleteSql); + + return $sql; + } +} diff --git a/source/thinkphp/library/think/db/Connection.php b/source/thinkphp/library/think/db/Connection.php new file mode 100644 index 0000000..578cc8f --- /dev/null +++ b/source/thinkphp/library/think/db/Connection.php @@ -0,0 +1,1059 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +use PDO; +use PDOStatement; +use think\Db; +use think\db\exception\BindParamException; +use think\Debug; +use think\Exception; +use think\exception\PDOException; +use think\Log; + +/** + * Class Connection + * @package think + * @method Query table(string $table) 指定数据表(含前缀) + * @method Query name(string $name) 指定数据表(不含前缀) + * + */ +abstract class Connection +{ + + /** @var PDOStatement PDO操作实例 */ + protected $PDOStatement; + + /** @var string 当前SQL指令 */ + protected $queryStr = ''; + // 返回或者影响记录数 + protected $numRows = 0; + // 事务指令数 + protected $transTimes = 0; + // 错误信息 + protected $error = ''; + + /** @var PDO[] 数据库连接ID 支持多个连接 */ + protected $links = []; + + /** @var PDO 当前连接ID */ + protected $linkID; + protected $linkRead; + protected $linkWrite; + + // 查询结果类型 + protected $fetchType = PDO::FETCH_ASSOC; + // 字段属性大小写 + protected $attrCase = PDO::CASE_LOWER; + // 监听回调 + protected static $event = []; + // 使用Builder类 + protected $builder; + // 数据库连接参数配置 + protected $config = [ + // 数据库类型 + 'type' => '', + // 服务器地址 + 'hostname' => '', + // 数据库名 + 'database' => '', + // 用户名 + 'username' => '', + // 密码 + 'password' => '', + // 端口 + 'hostport' => '', + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => '', + // 数据库调试模式 + 'debug' => false, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 模型写入后自动读取主服务器 + 'read_master' => false, + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 数据返回类型 + 'result_type' => PDO::FETCH_ASSOC, + // 数据集返回类型 + 'resultset_type' => 'array', + // 自动写入时间戳字段 + 'auto_timestamp' => false, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + // Builder类 + 'builder' => '', + // Query类 + 'query' => '\\think\\db\\Query', + // 是否需要断线重连 + 'break_reconnect' => false, + ]; + + // PDO连接参数 + protected $params = [ + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + PDO::ATTR_EMULATE_PREPARES => false, + ]; + + // 绑定参数 + protected $bind = []; + + /** + * 构造函数 读取数据库配置信息 + * @access public + * @param array $config 数据库配置数组 + */ + public function __construct(array $config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + } + + /** + * 获取新的查询对象 + * @access protected + * @return Query + */ + protected function getQuery() + { + $class = $this->config['query']; + return new $class($this); + } + + /** + * 获取当前连接器类对应的Builder类 + * @access public + * @return string + */ + public function getBuilder() + { + if (!empty($this->builder)) { + return $this->builder; + } else { + return $this->getConfig('builder') ?: '\\think\\db\\builder\\' . ucfirst($this->getConfig('type')); + } + } + + /** + * 调用Query类的查询方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array([$this->getQuery(), $method], $args); + } + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + abstract protected function parseDsn($config); + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName + * @return array + */ + abstract public function getFields($tableName); + + /** + * 取得数据库的表信息 + * @access public + * @param string $dbName + * @return array + */ + abstract public function getTables($dbName); + + /** + * SQL性能分析 + * @access protected + * @param string $sql + * @return array + */ + abstract protected function getExplain($sql); + + /** + * 对返数据表字段信息进行大小写转换出来 + * @access public + * @param array $info 字段信息 + * @return array + */ + public function fieldCase($info) + { + // 字段大小写转换 + switch ($this->attrCase) { + case PDO::CASE_LOWER: + $info = array_change_key_case($info); + break; + case PDO::CASE_UPPER: + $info = array_change_key_case($info, CASE_UPPER); + break; + case PDO::CASE_NATURAL: + default: + // 不做转换 + } + return $info; + } + + /** + * 获取数据库的配置参数 + * @access public + * @param string $config 配置名称 + * @return mixed + */ + public function getConfig($config = '') + { + return $config ? $this->config[$config] : $this->config; + } + + /** + * 设置数据库的配置参数 + * @access public + * @param string|array $config 配置名称 + * @param mixed $value 配置值 + * @return void + */ + public function setConfig($config, $value = '') + { + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } else { + $this->config[$config] = $value; + } + } + + /** + * 连接数据库方法 + * @access public + * @param array $config 连接参数 + * @param integer $linkNum 连接序号 + * @param array|bool $autoConnection 是否自动连接主数据库(用于分布式) + * @return PDO + * @throws Exception + */ + public function connect(array $config = [], $linkNum = 0, $autoConnection = false) + { + if (!isset($this->links[$linkNum])) { + if (!$config) { + $config = $this->config; + } else { + $config = array_merge($this->config, $config); + } + // 连接参数 + if (isset($config['params']) && is_array($config['params'])) { + $params = $config['params'] + $this->params; + } else { + $params = $this->params; + } + // 记录当前字段属性大小写设置 + $this->attrCase = $params[PDO::ATTR_CASE]; + + // 数据返回类型 + if (isset($config['result_type'])) { + $this->fetchType = $config['result_type']; + } + try { + if (empty($config['dsn'])) { + $config['dsn'] = $this->parseDsn($config); + } + if ($config['debug']) { + $startTime = microtime(true); + } + $this->links[$linkNum] = new PDO($config['dsn'], $config['username'], $config['password'], $params); + if ($config['debug']) { + // 记录数据库连接信息 + Log::record('[ DB ] CONNECT:[ UseTime:' . number_format(microtime(true) - $startTime, 6) . 's ] ' . $config['dsn'], 'sql'); + } + } catch (\PDOException $e) { + if ($autoConnection) { + Log::record($e->getMessage(), 'error'); + return $this->connect($autoConnection, $linkNum); + } else { + throw $e; + } + } + } + return $this->links[$linkNum]; + } + + /** + * 释放查询结果 + * @access public + */ + public function free() + { + $this->PDOStatement = null; + } + + /** + * 获取PDO对象 + * @access public + * @return \PDO|false + */ + public function getPdo() + { + if (!$this->linkID) { + return false; + } else { + return $this->linkID; + } + } + + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param bool $master 是否在主服务器读操作 + * @param bool $pdo 是否返回PDO对象 + * @return mixed + * @throws PDOException + * @throws \Exception + */ + public function query($sql, $bind = [], $master = false, $pdo = false) + { + $this->initConnect($master); + if (!$this->linkID) { + return false; + } + + // 记录SQL语句 + $this->queryStr = $sql; + if ($bind) { + $this->bind = $bind; + } + + Db::$queryTimes++; + try { + // 调试开始 + $this->debug(true); + + // 预处理 + $this->PDOStatement = $this->linkID->prepare($sql); + + // 是否为存储过程调用 + $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + // 参数绑定 + if ($procedure) { + $this->bindParam($bind); + } else { + $this->bindValue($bind); + } + // 执行查询 + $this->PDOStatement->execute(); + // 调试结束 + $this->debug(false, '', $master); + // 返回结果集 + return $this->getResult($pdo, $procedure); + } catch (\PDOException $e) { + if ($this->isBreak($e)) { + return $this->close()->query($sql, $bind, $master, $pdo); + } + throw new PDOException($e, $this->config, $this->getLastsql()); + } catch (\Throwable $e) { + if ($this->isBreak($e)) { + return $this->close()->query($sql, $bind, $master, $pdo); + } + throw $e; + } catch (\Exception $e) { + if ($this->isBreak($e)) { + return $this->close()->query($sql, $bind, $master, $pdo); + } + throw $e; + } + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param Query $query 查询对象 + * @return int + * @throws PDOException + * @throws \Exception + */ + public function execute($sql, $bind = [], Query $query = null) + { + $this->initConnect(true); + if (!$this->linkID) { + return false; + } + + // 记录SQL语句 + $this->queryStr = $sql; + if ($bind) { + $this->bind = $bind; + } + + Db::$executeTimes++; + try { + // 调试开始 + $this->debug(true); + + // 预处理 + $this->PDOStatement = $this->linkID->prepare($sql); + + // 是否为存储过程调用 + $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); + // 参数绑定 + if ($procedure) { + $this->bindParam($bind); + } else { + $this->bindValue($bind); + } + // 执行语句 + $this->PDOStatement->execute(); + // 调试结束 + $this->debug(false, '', true); + + if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) { + $query->readMaster(); + } + + $this->numRows = $this->PDOStatement->rowCount(); + return $this->numRows; + } catch (\PDOException $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw new PDOException($e, $this->config, $this->getLastsql()); + } catch (\Throwable $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw $e; + } catch (\Exception $e) { + if ($this->isBreak($e)) { + return $this->close()->execute($sql, $bind, $query); + } + throw $e; + } + } + + /** + * 根据参数绑定组装最终的SQL语句 便于调试 + * @access public + * @param string $sql 带参数绑定的sql语句 + * @param array $bind 参数绑定列表 + * @return string + */ + public function getRealSql($sql, array $bind = []) + { + if (is_array($sql)) { + $sql = implode(';', $sql); + } + + foreach ($bind as $key => $val) { + $value = is_array($val) ? $val[0] : $val; + $type = is_array($val) ? $val[1] : PDO::PARAM_STR; + if (PDO::PARAM_STR == $type) { + $value = $this->quote($value); + } elseif (PDO::PARAM_INT == $type) { + $value = (float) $value; + } + // 判断占位符 + $sql = is_numeric($key) ? + substr_replace($sql, $value, strpos($sql, '?'), 1) : + str_replace( + [':' . $key . ')', ':' . $key . ',', ':' . $key . ' ', ':' . $key . PHP_EOL], + [$value . ')', $value . ',', $value . ' ', $value . PHP_EOL], + $sql . ' '); + } + return rtrim($sql); + } + + /** + * 参数绑定 + * 支持 ['name'=>'value','id'=>123] 对应命名占位符 + * 或者 ['value',123] 对应问号占位符 + * @access public + * @param array $bind 要绑定的参数列表 + * @return void + * @throws BindParamException + */ + protected function bindValue(array $bind = []) + { + foreach ($bind as $key => $val) { + // 占位符 + $param = is_numeric($key) ? $key + 1 : ':' . $key; + if (is_array($val)) { + if (PDO::PARAM_INT == $val[1] && '' === $val[0]) { + $val[0] = 0; + } + $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]); + } else { + $result = $this->PDOStatement->bindValue($param, $val); + } + if (!$result) { + throw new BindParamException( + "Error occurred when binding parameters '{$param}'", + $this->config, + $this->getLastsql(), + $bind + ); + } + } + } + + /** + * 存储过程的输入输出参数绑定 + * @access public + * @param array $bind 要绑定的参数列表 + * @return void + * @throws BindParamException + */ + protected function bindParam($bind) + { + foreach ($bind as $key => $val) { + $param = is_numeric($key) ? $key + 1 : ':' . $key; + if (is_array($val)) { + array_unshift($val, $param); + $result = call_user_func_array([$this->PDOStatement, 'bindParam'], $val); + } else { + $result = $this->PDOStatement->bindValue($param, $val); + } + if (!$result) { + $param = array_shift($val); + throw new BindParamException( + "Error occurred when binding parameters '{$param}'", + $this->config, + $this->getLastsql(), + $bind + ); + } + } + } + + /** + * 获得数据集数组 + * @access protected + * @param bool $pdo 是否返回PDOStatement + * @param bool $procedure 是否存储过程 + * @return PDOStatement|array + */ + protected function getResult($pdo = false, $procedure = false) + { + if ($pdo) { + // 返回PDOStatement对象处理 + return $this->PDOStatement; + } + if ($procedure) { + // 存储过程返回结果 + return $this->procedure(); + } + $result = $this->PDOStatement->fetchAll($this->fetchType); + $this->numRows = count($result); + return $result; + } + + /** + * 获得存储过程数据集 + * @access protected + * @return array + */ + protected function procedure() + { + $item = []; + do { + $result = $this->getResult(); + if ($result) { + $item[] = $result; + } + } while ($this->PDOStatement->nextRowset()); + $this->numRows = count($item); + return $item; + } + + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + * @throws PDOException + * @throws \Exception + * @throws \Throwable + */ + public function transaction($callback) + { + $this->startTrans(); + try { + $result = null; + if (is_callable($callback)) { + $result = call_user_func_array($callback, [$this]); + } + $this->commit(); + return $result; + } catch (\Exception $e) { + $this->rollback(); + throw $e; + } catch (\Throwable $e) { + $this->rollback(); + throw $e; + } + } + + /** + * 启动事务 + * @access public + * @return bool|mixed + * @throws \Exception + */ + public function startTrans() + { + $this->initConnect(true); + if (!$this->linkID) { + return false; + } + + ++$this->transTimes; + try { + if (1 == $this->transTimes) { + $this->linkID->beginTransaction(); + } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + $this->linkID->exec( + $this->parseSavepoint('trans' . $this->transTimes) + ); + } + + } catch (\Exception $e) { + if ($this->isBreak($e)) { + --$this->transTimes; + return $this->close()->startTrans(); + } + throw $e; + } catch (\Error $e) { + if ($this->isBreak($e)) { + --$this->transTimes; + return $this->close()->startTrans(); + } + throw $e; + } + } + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit() + { + $this->initConnect(true); + + if (1 == $this->transTimes) { + $this->linkID->commit(); + } + + --$this->transTimes; + } + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback() + { + $this->initConnect(true); + + if (1 == $this->transTimes) { + $this->linkID->rollBack(); + } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { + $this->linkID->exec( + $this->parseSavepointRollBack('trans' . $this->transTimes) + ); + } + + $this->transTimes = max(0, $this->transTimes - 1); + } + + /** + * 是否支持事务嵌套 + * @return bool + */ + protected function supportSavepoint() + { + return false; + } + + /** + * 生成定义保存点的SQL + * @param $name + * @return string + */ + protected function parseSavepoint($name) + { + return 'SAVEPOINT ' . $name; + } + + /** + * 生成回滚到保存点的SQL + * @param $name + * @return string + */ + protected function parseSavepointRollBack($name) + { + return 'ROLLBACK TO SAVEPOINT ' . $name; + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sqlArray SQL批处理指令 + * @return boolean + */ + public function batchQuery($sqlArray = [], $bind = [], Query $query = null) + { + if (!is_array($sqlArray)) { + return false; + } + // 自动启动事务支持 + $this->startTrans(); + try { + foreach ($sqlArray as $sql) { + $this->execute($sql, $bind, $query); + } + // 提交事务 + $this->commit(); + } catch (\Exception $e) { + $this->rollback(); + throw $e; + } + + return true; + } + + /** + * 获得查询次数 + * @access public + * @param boolean $execute 是否包含所有查询 + * @return integer + */ + public function getQueryTimes($execute = false) + { + return $execute ? Db::$queryTimes + Db::$executeTimes : Db::$queryTimes; + } + + /** + * 获得执行次数 + * @access public + * @return integer + */ + public function getExecuteTimes() + { + return Db::$executeTimes; + } + + /** + * 关闭数据库(或者重新连接) + * @access public + * @return $this + */ + public function close() + { + $this->linkID = null; + $this->linkWrite = null; + $this->linkRead = null; + $this->links = []; + // 释放查询 + $this->free(); + return $this; + } + + /** + * 是否断线 + * @access protected + * @param \PDOException|\Exception $e 异常对象 + * @return bool + */ + protected function isBreak($e) + { + if (!$this->config['break_reconnect']) { + return false; + } + + $info = [ + 'server has gone away', + 'no connection to the server', + 'Lost connection', + 'is dead or not enabled', + 'Error while sending', + 'decryption failed or bad record mac', + 'server closed the connection unexpectedly', + 'SSL connection has been closed unexpectedly', + 'Error writing data to the connection', + 'Resource deadlock avoided', + 'failed with errno', + ]; + + $error = $e->getMessage(); + + foreach ($info as $msg) { + if (false !== stripos($error, $msg)) { + return true; + } + } + return false; + } + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql() + { + return $this->getRealSql($this->queryStr, $this->bind); + } + + /** + * 获取最近插入的ID + * @access public + * @param string $sequence 自增序列名 + * @return string + */ + public function getLastInsID($sequence = null) + { + return $this->linkID->lastInsertId($sequence); + } + + /** + * 获取返回或者影响的记录数 + * @access public + * @return integer + */ + public function getNumRows() + { + return $this->numRows; + } + + /** + * 获取最近的错误信息 + * @access public + * @return string + */ + public function getError() + { + if ($this->PDOStatement) { + $error = $this->PDOStatement->errorInfo(); + $error = $error[1] . ':' . $error[2]; + } else { + $error = ''; + } + if ('' != $this->queryStr) { + $error .= "\n [ SQL语句 ] : " . $this->getLastsql(); + } + return $error; + } + + /** + * SQL指令安全过滤 + * @access public + * @param string $str SQL字符串 + * @param bool $master 是否主库查询 + * @return string + */ + public function quote($str, $master = true) + { + $this->initConnect($master); + return $this->linkID ? $this->linkID->quote($str) : $str; + } + + /** + * 数据库调试 记录当前SQL及分析性能 + * @access protected + * @param boolean $start 调试开始标记 true 开始 false 结束 + * @param string $sql 执行的SQL语句 留空自动获取 + * @param boolean $master 主从标记 + * @return void + */ + protected function debug($start, $sql = '', $master = false) + { + if (!empty($this->config['debug'])) { + // 开启数据库调试模式 + if ($start) { + Debug::remark('queryStartTime', 'time'); + } else { + // 记录操作结束时间 + Debug::remark('queryEndTime', 'time'); + $runtime = Debug::getRangeTime('queryStartTime', 'queryEndTime'); + $sql = $sql ?: $this->getLastsql(); + $result = []; + // SQL性能分析 + if ($this->config['sql_explain'] && 0 === stripos(trim($sql), 'select')) { + $result = $this->getExplain($sql); + } + // SQL监听 + $this->trigger($sql, $runtime, $result, $master); + } + } + } + + /** + * 监听SQL执行 + * @access public + * @param callable $callback 回调方法 + * @return void + */ + public function listen($callback) + { + self::$event[] = $callback; + } + + /** + * 触发SQL事件 + * @access protected + * @param string $sql SQL语句 + * @param float $runtime SQL运行时间 + * @param mixed $explain SQL分析 + * @param bool $master 主从标记 + * @return void + */ + protected function trigger($sql, $runtime, $explain = [], $master = false) + { + if (!empty(self::$event)) { + foreach (self::$event as $callback) { + if (is_callable($callback)) { + call_user_func_array($callback, [$sql, $runtime, $explain, $master]); + } + } + } else { + // 未注册监听则记录到日志中 + if ($this->config['deploy']) { + // 分布式记录当前操作的主从 + $master = $master ? 'master|' : 'slave|'; + } else { + $master = ''; + } + + Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql'); + if (!empty($explain)) { + Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); + } + } + } + + /** + * 初始化数据库连接 + * @access protected + * @param boolean $master 是否主服务器 + * @return void + */ + protected function initConnect($master = true) + { + if (!empty($this->config['deploy'])) { + // 采用分布式数据库 + if ($master || $this->transTimes) { + if (!$this->linkWrite) { + $this->linkWrite = $this->multiConnect(true); + } + $this->linkID = $this->linkWrite; + } else { + if (!$this->linkRead) { + $this->linkRead = $this->multiConnect(false); + } + $this->linkID = $this->linkRead; + } + } elseif (!$this->linkID) { + // 默认单数据库 + $this->linkID = $this->connect(); + } + } + + /** + * 连接分布式服务器 + * @access protected + * @param boolean $master 主服务器 + * @return PDO + */ + protected function multiConnect($master = false) + { + $_config = []; + // 分布式数据库配置解析 + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $_config[$name] = explode(',', $this->config[$name]); + } + + // 主服务器序号 + $m = floor(mt_rand(0, $this->config['master_num'] - 1)); + + if ($this->config['rw_separate']) { + // 主从式采用读写分离 + if ($master) // 主服务器写入 + { + $r = $m; + } elseif (is_numeric($this->config['slave_no'])) { + // 指定服务器读 + $r = $this->config['slave_no']; + } else { + // 读操作连接从服务器 每次随机连接的数据库 + $r = floor(mt_rand($this->config['master_num'], count($_config['hostname']) - 1)); + } + } else { + // 读写操作不区分服务器 每次随机连接的数据库 + $r = floor(mt_rand(0, count($_config['hostname']) - 1)); + } + $dbMaster = false; + if ($m != $r) { + $dbMaster = []; + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $dbMaster[$name] = isset($_config[$name][$m]) ? $_config[$name][$m] : $_config[$name][0]; + } + } + $dbConfig = []; + foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) { + $dbConfig[$name] = isset($_config[$name][$r]) ? $_config[$name][$r] : $_config[$name][0]; + } + return $this->connect($dbConfig, $r, $r == $m ? false : $dbMaster); + } + + /** + * 析构方法 + * @access public + */ + public function __destruct() + { + // 释放查询 + if ($this->PDOStatement) { + $this->free(); + } + // 关闭连接 + $this->close(); + } +} diff --git a/source/thinkphp/library/think/db/Expression.php b/source/thinkphp/library/think/db/Expression.php new file mode 100644 index 0000000..f1b92ab --- /dev/null +++ b/source/thinkphp/library/think/db/Expression.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +class Expression +{ + /** + * 查询表达式 + * + * @var string + */ + protected $value; + + /** + * 创建一个查询表达式 + * + * @param string $value + * @return void + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * 获取表达式 + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/source/thinkphp/library/think/db/Query.php b/source/thinkphp/library/think/db/Query.php new file mode 100644 index 0000000..ac4adea --- /dev/null +++ b/source/thinkphp/library/think/db/Query.php @@ -0,0 +1,3045 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +use PDO; +use think\App; +use think\Cache; +use think\Collection; +use think\Config; +use think\Db; +use think\db\exception\BindParamException; +use think\db\exception\DataNotFoundException; +use think\db\exception\ModelNotFoundException; +use think\Exception; +use think\exception\DbException; +use think\exception\PDOException; +use think\Loader; +use think\Model; +use think\model\Relation; +use think\model\relation\OneToOne; +use think\Paginator; + +class Query +{ + // 数据库Connection对象实例 + protected $connection; + // 数据库Builder对象实例 + protected $builder; + // 当前模型类名称 + protected $model; + // 当前数据表名称(含前缀) + protected $table = ''; + // 当前数据表名称(不含前缀) + protected $name = ''; + // 当前数据表主键 + protected $pk; + // 当前数据表前缀 + protected $prefix = ''; + // 查询参数 + protected $options = []; + // 参数绑定 + protected $bind = []; + // 数据表信息 + protected static $info = []; + // 回调事件 + private static $event = []; + // 读取主库 + protected static $readMaster = []; + + /** + * 构造函数 + * @access public + * @param Connection $connection 数据库对象实例 + * @param Model $model 模型对象 + */ + public function __construct(Connection $connection = null, $model = null) + { + $this->connection = $connection ?: Db::connect([], true); + $this->prefix = $this->connection->getConfig('prefix'); + $this->model = $model; + // 设置当前连接的Builder对象 + $this->setBuilder(); + } + + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + * @throws DbException + * @throws Exception + */ + public function __call($method, $args) + { + if (strtolower(substr($method, 0, 5)) == 'getby') { + // 根据某个字段获取记录 + $field = Loader::parseName(substr($method, 5)); + $where[$field] = $args[0]; + return $this->where($where)->find(); + } elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') { + // 根据某个字段获取记录的某个值 + $name = Loader::parseName(substr($method, 10)); + $where[$name] = $args[0]; + return $this->where($where)->value($args[1]); + } elseif ($this->model && method_exists($this->model, 'scope' . $method)) { + // 动态调用命名范围 + $method = 'scope' . $method; + array_unshift($args, $this); + + call_user_func_array([$this->model, $method], $args); + return $this; + } else { + throw new Exception('method not exist:' . __CLASS__ . '->' . $method); + } + } + + /** + * 获取当前的数据库Connection对象 + * @access public + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * 切换当前的数据库连接 + * @access public + * @param mixed $config + * @return $this + */ + public function connect($config) + { + $this->connection = Db::connect($config); + $this->setBuilder(); + $this->prefix = $this->connection->getConfig('prefix'); + return $this; + } + + /** + * 设置当前的数据库Builder对象 + * @access protected + * @return void + */ + protected function setBuilder() + { + $class = $this->connection->getBuilder(); + $this->builder = new $class($this->connection, $this); + } + + /** + * 获取当前的模型对象实例 + * @access public + * @return Model|null + */ + public function getModel() + { + return $this->model; + } + + /** + * 设置后续从主库读取数据 + * @access public + * @param bool $allTable + * @return void + */ + public function readMaster($allTable = false) + { + if ($allTable) { + $table = '*'; + } else { + $table = isset($this->options['table']) ? $this->options['table'] : $this->getTable(); + } + + static::$readMaster[$table] = true; + + return $this; + } + + /** + * 获取当前的builder实例对象 + * @access public + * @return Builder + */ + public function getBuilder() + { + return $this->builder; + } + + /** + * 指定默认的数据表名(不含前缀) + * @access public + * @param string $name + * @return $this + */ + public function name($name) + { + $this->name = $name; + return $this; + } + + /** + * 指定默认数据表名(含前缀) + * @access public + * @param string $table 表名 + * @return $this + */ + public function setTable($table) + { + $this->table = $table; + return $this; + } + + /** + * 得到当前或者指定名称的数据表 + * @access public + * @param string $name + * @return string + */ + public function getTable($name = '') + { + if ($name || empty($this->table)) { + $name = $name ?: $this->name; + $tableName = $this->prefix; + if ($name) { + $tableName .= Loader::parseName($name); + } + } else { + $tableName = $this->table; + } + return $tableName; + } + + /** + * 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写) + * @access public + * @param string $sql sql语句 + * @return string + */ + public function parseSqlTable($sql) + { + if (false !== strpos($sql, '__')) { + $prefix = $this->prefix; + $sql = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function ($match) use ($prefix) { + return $prefix . strtolower($match[1]); + }, $sql); + } + return $sql; + } + + /** + * 执行查询 返回数据集 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @param boolean $master 是否在主服务器读操作 + * @param bool|string $class 指定返回的数据集对象 + * @return mixed + * @throws BindParamException + * @throws PDOException + */ + public function query($sql, $bind = [], $master = false, $class = false) + { + return $this->connection->query($sql, $bind, $master, $class); + } + + /** + * 执行语句 + * @access public + * @param string $sql sql指令 + * @param array $bind 参数绑定 + * @return int + * @throws BindParamException + * @throws PDOException + */ + public function execute($sql, $bind = []) + { + return $this->connection->execute($sql, $bind, $this); + } + + /** + * 获取最近插入的ID + * @access public + * @param string $sequence 自增序列名 + * @return string + */ + public function getLastInsID($sequence = null) + { + return $this->connection->getLastInsID($sequence); + } + + /** + * 获取最近一次查询的sql语句 + * @access public + * @return string + */ + public function getLastSql() + { + return $this->connection->getLastSql(); + } + + /** + * 执行数据库事务 + * @access public + * @param callable $callback 数据操作方法回调 + * @return mixed + */ + public function transaction($callback) + { + return $this->connection->transaction($callback); + } + + /** + * 启动事务 + * @access public + * @return void + */ + public function startTrans() + { + $this->connection->startTrans(); + } + + /** + * 用于非自动提交状态下面的查询提交 + * @access public + * @return void + * @throws PDOException + */ + public function commit() + { + $this->connection->commit(); + } + + /** + * 事务回滚 + * @access public + * @return void + * @throws PDOException + */ + public function rollback() + { + $this->connection->rollback(); + } + + /** + * 批处理执行SQL语句 + * 批处理的指令都认为是execute操作 + * @access public + * @param array $sql SQL批处理指令 + * @return boolean + */ + public function batchQuery($sql = [], $bind = []) + { + return $this->connection->batchQuery($sql, $bind); + } + + /** + * 获取数据库的配置参数 + * @access public + * @param string $name 参数名称 + * @return boolean + */ + public function getConfig($name = '') + { + return $this->connection->getConfig($name); + } + + /** + * 得到分表的的数据表名 + * @access public + * @param array $data 操作的数据 + * @param string $field 分表依据的字段 + * @param array $rule 分表规则 + * @return string + */ + public function getPartitionTableName($data, $field, $rule = []) + { + // 对数据表进行分区 + if ($field && isset($data[$field])) { + $value = $data[$field]; + $type = $rule['type']; + switch ($type) { + case 'id': + // 按照id范围分表 + $step = $rule['expr']; + $seq = floor($value / $step) + 1; + break; + case 'year': + // 按照年份分表 + if (!is_numeric($value)) { + $value = strtotime($value); + } + $seq = date('Y', $value) - $rule['expr'] + 1; + break; + case 'mod': + // 按照id的模数分表 + $seq = ($value % $rule['num']) + 1; + break; + case 'md5': + // 按照md5的序列分表 + $seq = (ord(substr(md5($value), 0, 1)) % $rule['num']) + 1; + break; + default: + if (function_exists($type)) { + // 支持指定函数哈希 + $seq = (ord(substr($type($value), 0, 1)) % $rule['num']) + 1; + } else { + // 按照字段的首字母的值分表 + $seq = (ord($value{0}) % $rule['num']) + 1; + } + } + return $this->getTable() . '_' . $seq; + } else { + // 当设置的分表字段不在查询条件或者数据中 + // 进行联合查询,必须设定 partition['num'] + $tableName = []; + for ($i = 0; $i < $rule['num']; $i++) { + $tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1); + } + + $tableName = '( ' . implode(" UNION ", $tableName) . ') AS ' . $this->name; + return $tableName; + } + } + + /** + * 得到某个字段的值 + * @access public + * @param string $field 字段名 + * @param mixed $default 默认值 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function value($field, $default = null, $force = false) + { + $result = false; + if (empty($this->options['fetch_sql']) && !empty($this->options['cache'])) { + // 判断查询缓存 + $cache = $this->options['cache']; + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); + } + $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind)); + $result = Cache::get($key); + } + if (false === $result) { + if (isset($this->options['field'])) { + unset($this->options['field']); + } + $pdo = $this->field($field)->limit(1)->getPdo(); + if (is_string($pdo)) { + // 返回SQL语句 + return $pdo; + } + + $result = $pdo->fetchColumn(); + if ($force) { + $result = (float) $result; + } + + if (isset($cache) && false !== $result) { + // 缓存数据 + $this->cacheData($key, $result, $cache); + } + } else { + // 清空查询条件 + $this->options = []; + } + return false !== $result ? $result : $default; + } + + /** + * 得到某个列的数组 + * @access public + * @param string $field 字段名 多个字段用逗号分隔 + * @param string $key 索引 + * @return array + */ + public function column($field, $key = '') + { + $result = false; + if (empty($this->options['fetch_sql']) && !empty($this->options['cache'])) { + // 判断查询缓存 + $cache = $this->options['cache']; + if (empty($this->options['table'])) { + $this->options['table'] = $this->getTable(); + } + $guid = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . $field . serialize($this->options) . serialize($this->bind)); + $result = Cache::get($guid); + } + if (false === $result) { + if (isset($this->options['field'])) { + unset($this->options['field']); + } + if (is_null($field)) { + $field = '*'; + } elseif ($key && '*' != $field) { + $field = $key . ',' . $field; + } + $pdo = $this->field($field)->getPdo(); + if (is_string($pdo)) { + // 返回SQL语句 + return $pdo; + } + if (1 == $pdo->columnCount()) { + $result = $pdo->fetchAll(PDO::FETCH_COLUMN); + } else { + $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC); + if ($resultSet) { + $fields = array_keys($resultSet[0]); + $count = count($fields); + $key1 = array_shift($fields); + $key2 = $fields ? array_shift($fields) : ''; + $key = $key ?: $key1; + if (strpos($key, '.')) { + list($alias, $key) = explode('.', $key); + } + foreach ($resultSet as $val) { + if ($count > 2) { + $result[$val[$key]] = $val; + } elseif (2 == $count) { + $result[$val[$key]] = $val[$key2]; + } elseif (1 == $count) { + $result[$val[$key]] = $val[$key1]; + } + } + } else { + $result = []; + } + } + if (isset($cache) && isset($guid)) { + // 缓存数据 + $this->cacheData($guid, $result, $cache); + } + } else { + // 清空查询条件 + $this->options = []; + } + return $result; + } + + /** + * COUNT查询 + * @access public + * @param string $field 字段名 + * @return integer|string + */ + public function count($field = '*') + { + if (isset($this->options['group'])) { + if (!preg_match('/^[\w\.\*]+$/', $field)) { + throw new Exception('not support data:' . $field); + } + // 支持GROUP + $options = $this->getOptions(); + $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); + + $count = $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true); + } else { + $count = $this->aggregate('COUNT', $field, true); + } + + return is_string($count) ? $count : (int) $count; + + } + + /** + * 聚合查询 + * @access public + * @param string $aggregate 聚合方法 + * @param string $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function aggregate($aggregate, $field, $force = false) + { + if (0 === stripos($field, 'DISTINCT ')) { + list($distinct, $field) = explode(' ', $field); + } + + if (!preg_match('/^[\w\.\+\-\*]+$/', $field)) { + throw new Exception('not support data:' . $field); + } + + $result = $this->value($aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $field . ') AS tp_' . strtolower($aggregate), 0, $force); + + return $result; + } + + /** + * SUM查询 + * @access public + * @param string $field 字段名 + * @return float|int + */ + public function sum($field) + { + return $this->aggregate('SUM', $field, true); + } + + /** + * MIN查询 + * @access public + * @param string $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function min($field, $force = true) + { + return $this->aggregate('MIN', $field, $force); + } + + /** + * MAX查询 + * @access public + * @param string $field 字段名 + * @param bool $force 强制转为数字类型 + * @return mixed + */ + public function max($field, $force = true) + { + return $this->aggregate('MAX', $field, $force); + } + + /** + * AVG查询 + * @access public + * @param string $field 字段名 + * @return float|int + */ + public function avg($field) + { + return $this->aggregate('AVG', $field, true); + } + + /** + * 设置记录的某个字段值 + * 支持使用数据库字段和方法 + * @access public + * @param string|array $field 字段名 + * @param mixed $value 字段值 + * @return integer + */ + public function setField($field, $value = '') + { + if (is_array($field)) { + $data = $field; + } else { + $data[$field] = $value; + } + return $this->update($data); + } + + /** + * 字段值(延迟)增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return integer|true + * @throws Exception + */ + public function setInc($field, $step = 1, $lazyTime = 0) + { + $condition = !empty($this->options['where']) ? $this->options['where'] : []; + if (empty($condition)) { + // 没有条件不做任何更新 + throw new Exception('no data to update'); + } + if ($lazyTime > 0) { + // 延迟写入 + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind)); + $step = $this->lazyWrite('inc', $guid, $step, $lazyTime); + if (false === $step) { + // 清空查询条件 + $this->options = []; + return true; + } + } + return $this->setField($field, ['inc', $step]); + } + + /** + * 字段值(延迟)减少 + * @access public + * @param string $field 字段名 + * @param integer $step 减少值 + * @param integer $lazyTime 延时时间(s) + * @return integer|true + * @throws Exception + */ + public function setDec($field, $step = 1, $lazyTime = 0) + { + $condition = !empty($this->options['where']) ? $this->options['where'] : []; + if (empty($condition)) { + // 没有条件不做任何更新 + throw new Exception('no data to update'); + } + if ($lazyTime > 0) { + // 延迟写入 + $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition) . serialize($this->bind)); + $step = $this->lazyWrite('dec', $guid, $step, $lazyTime); + if (false === $step) { + // 清空查询条件 + $this->options = []; + return true; + } + return $this->setField($field, ['inc', $step]); + } + return $this->setField($field, ['dec', $step]); + } + + /** + * 延时更新检查 返回false表示需要延时 + * 否则返回实际写入的数值 + * @access protected + * @param string $type 自增或者自减 + * @param string $guid 写入标识 + * @param integer $step 写入步进值 + * @param integer $lazyTime 延时时间(s) + * @return false|integer + */ + protected function lazyWrite($type, $guid, $step, $lazyTime) + { + if (!Cache::has($guid . '_time')) { + // 计时开始 + Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0); + Cache::$type($guid, $step); + } elseif ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) { + // 删除缓存 + $value = Cache::$type($guid, $step); + Cache::rm($guid); + Cache::rm($guid . '_time'); + return 0 === $value ? false : $value; + } else { + // 更新缓存 + Cache::$type($guid, $step); + } + return false; + } + + /** + * 查询SQL组装 join + * @access public + * @param mixed $join 关联的表名 + * @param mixed $condition 条件 + * @param string $type JOIN类型 + * @return $this + */ + public function join($join, $condition = null, $type = 'INNER') + { + if (empty($condition)) { + // 如果为组数,则循环调用join + foreach ($join as $key => $value) { + if (is_array($value) && 2 <= count($value)) { + $this->join($value[0], $value[1], isset($value[2]) ? $value[2] : $type); + } + } + } else { + $table = $this->getJoinTable($join); + + $this->options['join'][] = [$table, strtoupper($type), $condition]; + } + return $this; + } + + /** + * 获取Join表名及别名 支持 + * ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias' + * @access public + * @param array|string $join + * @return array|string + */ + protected function getJoinTable($join, &$alias = null) + { + // 传入的表名为数组 + if (is_array($join)) { + $table = $join; + $alias = array_shift($join); + } else { + $join = trim($join); + if (false !== strpos($join, '(')) { + // 使用子查询 + $table = $join; + } else { + $prefix = $this->prefix; + if (strpos($join, ' ')) { + // 使用别名 + list($table, $alias) = explode(' ', $join); + } else { + $table = $join; + if (false === strpos($join, '.') && 0 !== strpos($join, '__')) { + $alias = $join; + } + } + if ($prefix && false === strpos($table, '.') && 0 !== strpos($table, $prefix) && 0 !== strpos($table, '__')) { + $table = $this->getTable($table); + } + } + if (isset($alias) && $table != $alias) { + $table = [$table => $alias]; + } + } + return $table; + } + + /** + * 查询SQL组装 union + * @access public + * @param mixed $union + * @param boolean $all + * @return $this + */ + public function union($union, $all = false) + { + $this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION'; + + if (is_array($union)) { + $this->options['union'] = array_merge($this->options['union'], $union); + } else { + $this->options['union'][] = $union; + } + return $this; + } + + /** + * 指定查询字段 支持字段排除和指定数据表 + * @access public + * @param mixed $field + * @param boolean $except 是否排除 + * @param string $tableName 数据表名 + * @param string $prefix 字段前缀 + * @param string $alias 别名前缀 + * @return $this + */ + public function field($field, $except = false, $tableName = '', $prefix = '', $alias = '') + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Expression) { + $this->options['field'][] = $field; + return $this; + } + + if (is_string($field)) { + if (preg_match('/[\<\'\"\(]/', $field)) { + return $this->fieldRaw($field); + } + $field = array_map('trim', explode(',', $field)); + } + if (true === $field) { + // 获取全部字段 + $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); + $field = $fields ?: ['*']; + } elseif ($except) { + // 字段排除 + $fields = $this->getTableInfo($tableName ?: (isset($this->options['table']) ? $this->options['table'] : ''), 'fields'); + $field = $fields ? array_diff($fields, $field) : $field; + } + if ($tableName) { + // 添加统一的前缀 + $prefix = $prefix ?: $tableName; + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $val = $prefix . '.' . $val . ($alias ? ' AS ' . $alias . $val : ''); + } + $field[$key] = $val; + } + } + + if (isset($this->options['field'])) { + $field = array_merge((array) $this->options['field'], $field); + } + $this->options['field'] = array_unique($field); + return $this; + } + + /** + * 表达式方式指定查询字段 + * @access public + * @param string $field 字段名 + * @param array $bind 参数绑定 + * @return $this + */ + public function fieldRaw($field, array $bind = []) + { + $this->options['field'][] = $this->raw($field); + + if ($bind) { + $this->bind($bind); + } + + return $this; + } + + /** + * 设置数据 + * @access public + * @param mixed $field 字段名或者数据 + * @param mixed $value 字段值 + * @return $this + */ + public function data($field, $value = null) + { + if (is_array($field)) { + $this->options['data'] = isset($this->options['data']) ? array_merge($this->options['data'], $field) : $field; + } else { + $this->options['data'][$field] = $value; + } + return $this; + } + + /** + * 字段值增长 + * @access public + * @param string|array $field 字段名 + * @param integer $step 增长值 + * @return $this + */ + public function inc($field, $step = 1) + { + $fields = is_string($field) ? explode(',', $field) : $field; + foreach ($fields as $field) { + $this->data($field, ['inc', $step]); + } + return $this; + } + + /** + * 字段值减少 + * @access public + * @param string|array $field 字段名 + * @param integer $step 增长值 + * @return $this + */ + public function dec($field, $step = 1) + { + $fields = is_string($field) ? explode(',', $field) : $field; + foreach ($fields as $field) { + $this->data($field, ['dec', $step]); + } + return $this; + } + + /** + * 使用表达式设置数据 + * @access public + * @param string $field 字段名 + * @param string $value 字段值 + * @return $this + */ + public function exp($field, $value) + { + $this->data($field, $this->raw($value)); + return $this; + } + + /** + * 使用表达式设置数据 + * @access public + * @param mixed $value 表达式 + * @return Expression + */ + public function raw($value) + { + return new Expression($value); + } + + /** + * 指定JOIN查询字段 + * @access public + * @param string|array $table 数据表 + * @param string|array $field 查询字段 + * @param mixed $on JOIN条件 + * @param string $type JOIN类型 + * @return $this + */ + public function view($join, $field = true, $on = null, $type = 'INNER') + { + $this->options['view'] = true; + if (is_array($join) && key($join) === 0) { + foreach ($join as $key => $val) { + $this->view($val[0], $val[1], isset($val[2]) ? $val[2] : null, isset($val[3]) ? $val[3] : 'INNER'); + } + } else { + $fields = []; + $table = $this->getJoinTable($join, $alias); + + if (true === $field) { + $fields = $alias . '.*'; + } else { + if (is_string($field)) { + $field = explode(',', $field); + } + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $fields[] = $alias . '.' . $val; + $this->options['map'][$val] = $alias . '.' . $val; + } else { + if (preg_match('/[,=\.\'\"\(\s]/', $key)) { + $name = $key; + } else { + $name = $alias . '.' . $key; + } + $fields[$name] = $val; + $this->options['map'][$val] = $name; + } + } + } + $this->field($fields); + if ($on) { + $this->join($table, $on, $type); + } else { + $this->table($table); + } + } + return $this; + } + + /** + * 设置分表规则 + * @access public + * @param array $data 操作的数据 + * @param string $field 分表依据的字段 + * @param array $rule 分表规则 + * @return $this + */ + public function partition($data, $field, $rule = []) + { + $this->options['table'] = $this->getPartitionTableName($data, $field, $rule); + return $this; + } + + /** + * 指定AND查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function where($field, $op = null, $condition = null) + { + $param = func_get_args(); + array_shift($param); + $this->parseWhereExp('AND', $field, $op, $condition, $param); + return $this; + } + + /** + * 指定OR查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function whereOr($field, $op = null, $condition = null) + { + $param = func_get_args(); + array_shift($param); + $this->parseWhereExp('OR', $field, $op, $condition, $param); + return $this; + } + + /** + * 指定XOR查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @return $this + */ + public function whereXor($field, $op = null, $condition = null) + { + $param = func_get_args(); + array_shift($param); + $this->parseWhereExp('XOR', $field, $op, $condition, $param); + return $this; + } + + /** + * 指定表达式查询条件 + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereRaw($where, $bind = [], $logic = 'AND') + { + $this->options['where'][$logic][] = $this->raw($where); + + if ($bind) { + $this->bind($bind); + } + + return $this; + } + + /** + * 指定表达式查询条件 OR + * @access public + * @param string $where 查询条件 + * @param array $bind 参数绑定 + * @return $this + */ + public function whereOrRaw($where, $bind = []) + { + return $this->whereRaw($where, $bind, 'OR'); + } + + /** + * 指定Null查询条件 + * @access public + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNull($field, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'null', null, [], true); + return $this; + } + + /** + * 指定NotNull查询条件 + * @access public + * @param mixed $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotNull($field, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'notnull', null, [], true); + return $this; + } + + /** + * 指定Exists查询条件 + * @access public + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereExists($condition, $logic = 'AND') + { + $this->options['where'][strtoupper($logic)][] = ['exists', $condition]; + return $this; + } + + /** + * 指定NotExists查询条件 + * @access public + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotExists($condition, $logic = 'AND') + { + $this->options['where'][strtoupper($logic)][] = ['not exists', $condition]; + return $this; + } + + /** + * 指定In查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereIn($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'in', $condition, [], true); + return $this; + } + + /** + * 指定NotIn查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotIn($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'not in', $condition, [], true); + return $this; + } + + /** + * 指定Like查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereLike($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'like', $condition, [], true); + return $this; + } + + /** + * 指定NotLike查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotLike($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'not like', $condition, [], true); + return $this; + } + + /** + * 指定Between查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereBetween($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'between', $condition, [], true); + return $this; + } + + /** + * 指定NotBetween查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereNotBetween($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'not between', $condition, [], true); + return $this; + } + + /** + * 指定Exp查询条件 + * @access public + * @param mixed $field 查询字段 + * @param mixed $condition 查询条件 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function whereExp($field, $condition, $logic = 'AND') + { + $this->parseWhereExp($logic, $field, 'exp', $this->raw($condition), [], true); + return $this; + } + + /** + * 设置软删除字段及条件 + * @access public + * @param false|string $field 查询字段 + * @param mixed $condition 查询条件 + * @return $this + */ + public function useSoftDelete($field, $condition = null) + { + if ($field) { + $this->options['soft_delete'] = [$field, $condition ?: ['null', '']]; + } + return $this; + } + + /** + * 分析查询表达式 + * @access public + * @param string $logic 查询逻辑 and or xor + * @param string|array|\Closure $field 查询字段 + * @param mixed $op 查询表达式 + * @param mixed $condition 查询条件 + * @param array $param 查询参数 + * @param bool $strict 严格模式 + * @return void + */ + protected function parseWhereExp($logic, $field, $op, $condition, $param = [], $strict = false) + { + $logic = strtoupper($logic); + if ($field instanceof \Closure) { + $this->options['where'][$logic][] = is_string($op) ? [$op, $field] : $field; + return; + } + + if (is_string($field) && !empty($this->options['via']) && !strpos($field, '.')) { + $field = $this->options['via'] . '.' . $field; + } + + if ($field instanceof Expression) { + return $this->whereRaw($field, is_array($op) ? $op : []); + } elseif ($strict) { + // 使用严格模式查询 + $where[$field] = [$op, $condition]; + + // 记录一个字段多次查询条件 + $this->options['multi'][$logic][$field][] = $where[$field]; + } elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) { + $where[] = ['exp', $this->raw($field)]; + if (is_array($op)) { + // 参数绑定 + $this->bind($op); + } + } elseif (is_null($op) && is_null($condition)) { + if (is_array($field)) { + // 数组批量查询 + $where = $field; + foreach ($where as $k => $val) { + $this->options['multi'][$logic][$k][] = $val; + } + } elseif ($field && is_string($field)) { + // 字符串查询 + $where[$field] = ['null', '']; + $this->options['multi'][$logic][$field][] = $where[$field]; + } + } elseif (is_array($op)) { + $where[$field] = $param; + } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { + // null查询 + $where[$field] = [$op, '']; + + $this->options['multi'][$logic][$field][] = $where[$field]; + } elseif (is_null($condition)) { + // 字段相等查询 + $where[$field] = ['eq', $op]; + + $this->options['multi'][$logic][$field][] = $where[$field]; + } else { + if ('exp' == strtolower($op)) { + $where[$field] = ['exp', $this->raw($condition)]; + // 参数绑定 + if (isset($param[2]) && is_array($param[2])) { + $this->bind($param[2]); + } + } else { + $where[$field] = [$op, $condition]; + } + // 记录一个字段多次查询条件 + $this->options['multi'][$logic][$field][] = $where[$field]; + } + + if (!empty($where)) { + if (!isset($this->options['where'][$logic])) { + $this->options['where'][$logic] = []; + } + if (is_string($field) && $this->checkMultiField($field, $logic)) { + $where[$field] = $this->options['multi'][$logic][$field]; + } elseif (is_array($field)) { + foreach ($field as $key => $val) { + if ($this->checkMultiField($key, $logic)) { + $where[$key] = $this->options['multi'][$logic][$key]; + } + } + } + $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where); + } + } + + /** + * 检查是否存在一个字段多次查询条件 + * @access public + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return bool + */ + private function checkMultiField($field, $logic) + { + return isset($this->options['multi'][$logic][$field]) && count($this->options['multi'][$logic][$field]) > 1; + } + + /** + * 去除某个查询条件 + * @access public + * @param string $field 查询字段 + * @param string $logic 查询逻辑 and or xor + * @return $this + */ + public function removeWhereField($field, $logic = 'AND') + { + $logic = strtoupper($logic); + if (isset($this->options['where'][$logic][$field])) { + unset($this->options['where'][$logic][$field]); + unset($this->options['multi'][$logic][$field]); + } + return $this; + } + + /** + * 去除查询参数 + * @access public + * @param string|bool $option 参数名 true 表示去除所有参数 + * @return $this + */ + public function removeOption($option = true) + { + if (true === $option) { + $this->options = []; + } elseif (is_string($option) && isset($this->options[$option])) { + unset($this->options[$option]); + } + return $this; + } + + /** + * 指定查询数量 + * @access public + * @param mixed $offset 起始位置 + * @param mixed $length 查询数量 + * @return $this + */ + public function limit($offset, $length = null) + { + if (is_null($length) && strpos($offset, ',')) { + list($offset, $length) = explode(',', $offset); + } + $this->options['limit'] = intval($offset) . ($length ? ',' . intval($length) : ''); + return $this; + } + + /** + * 指定分页 + * @access public + * @param mixed $page 页数 + * @param mixed $listRows 每页数量 + * @return $this + */ + public function page($page, $listRows = null) + { + if (is_null($listRows) && strpos($page, ',')) { + list($page, $listRows) = explode(',', $page); + } + $this->options['page'] = [intval($page), intval($listRows)]; + return $this; + } + + /** + * 分页查询 + * @param int|array $listRows 每页数量 数组表示配置参数 + * @param int|bool $simple 是否简洁模式或者总记录数 + * @param array $config 配置参数 + * page:当前页, + * path:url路径, + * query:url额外参数, + * fragment:url锚点, + * var_page:分页变量, + * list_rows:每页数量 + * type:分页类名 + * @return \think\Paginator + * @throws DbException + */ + public function paginate($listRows = null, $simple = false, $config = []) + { + if (is_int($simple)) { + $total = $simple; + $simple = false; + } + if (is_array($listRows)) { + $config = array_merge(Config::get('paginate'), $listRows); + $listRows = $config['list_rows']; + } else { + $config = array_merge(Config::get('paginate'), $config); + $listRows = $listRows ?: $config['list_rows']; + } + + /** @var Paginator $class */ + $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']); + $page = isset($config['page']) ? (int) $config['page'] : call_user_func([ + $class, + 'getCurrentPage', + ], $config['var_page']); + + $page = $page < 1 ? 1 : $page; + + $config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']); + + if (!isset($total) && !$simple) { + $options = $this->getOptions(); + + unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']); + + $bind = $this->bind; + $total = $this->count(); + $results = $this->options($options)->bind($bind)->page($page, $listRows)->select(); + } elseif ($simple) { + $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select(); + $total = null; + } else { + $results = $this->page($page, $listRows)->select(); + } + return $class::make($results, $listRows, $page, $total, $simple, $config); + } + + /** + * 指定当前操作的数据表 + * @access public + * @param mixed $table 表名 + * @return $this + */ + public function table($table) + { + if (is_string($table)) { + if (strpos($table, ')')) { + // 子查询 + } elseif (strpos($table, ',')) { + $tables = explode(',', $table); + $table = []; + foreach ($tables as $item) { + list($item, $alias) = explode(' ', trim($item)); + if ($alias) { + $this->alias([$item => $alias]); + $table[$item] = $alias; + } else { + $table[] = $item; + } + } + } elseif (strpos($table, ' ')) { + list($table, $alias) = explode(' ', $table); + + $table = [$table => $alias]; + $this->alias($table); + } + } else { + $tables = $table; + $table = []; + foreach ($tables as $key => $val) { + if (is_numeric($key)) { + $table[] = $val; + } else { + $this->alias([$key => $val]); + $table[$key] = $val; + } + } + } + $this->options['table'] = $table; + return $this; + } + + /** + * USING支持 用于多表删除 + * @access public + * @param mixed $using + * @return $this + */ + public function using($using) + { + $this->options['using'] = $using; + return $this; + } + + /** + * 指定排序 order('id','desc') 或者 order(['id'=>'desc','create_time'=>'desc']) + * @access public + * @param string|array $field 排序字段 + * @param string $order 排序 + * @return $this + */ + public function order($field, $order = null) + { + if (empty($field)) { + return $this; + } elseif ($field instanceof Expression) { + $this->options['order'][] = $field; + return $this; + } + + if (is_string($field)) { + if (!empty($this->options['via'])) { + $field = $this->options['via'] . '.' . $field; + } + if (strpos($field, ',')) { + $field = array_map('trim', explode(',', $field)); + } else { + $field = empty($order) ? $field : [$field => $order]; + } + } elseif (!empty($this->options['via'])) { + foreach ($field as $key => $val) { + if (is_numeric($key)) { + $field[$key] = $this->options['via'] . '.' . $val; + } else { + $field[$this->options['via'] . '.' . $key] = $val; + unset($field[$key]); + } + } + } + if (!isset($this->options['order'])) { + $this->options['order'] = []; + } + if (is_array($field)) { + $this->options['order'] = array_merge($this->options['order'], $field); + } else { + $this->options['order'][] = $field; + } + + return $this; + } + + /** + * 表达式方式指定Field排序 + * @access public + * @param string $field 排序字段 + * @param array $bind 参数绑定 + * @return $this + */ + public function orderRaw($field, array $bind = []) + { + $this->options['order'][] = $this->raw($field); + + if ($bind) { + $this->bind($bind); + } + + return $this; + } + + /** + * 查询缓存 + * @access public + * @param mixed $key 缓存key + * @param integer|\DateTime $expire 缓存有效期 + * @param string $tag 缓存标签 + * @return $this + */ + public function cache($key = true, $expire = null, $tag = null) + { + // 增加快捷调用方式 cache(10) 等同于 cache(true, 10) + if ($key instanceof \DateTime || (is_numeric($key) && is_null($expire))) { + $expire = $key; + $key = true; + } + + if (false !== $key) { + $this->options['cache'] = ['key' => $key, 'expire' => $expire, 'tag' => $tag]; + } + return $this; + } + + /** + * 指定group查询 + * @access public + * @param string $group GROUP + * @return $this + */ + public function group($group) + { + $this->options['group'] = $group; + return $this; + } + + /** + * 指定having查询 + * @access public + * @param string $having having + * @return $this + */ + public function having($having) + { + $this->options['having'] = $having; + return $this; + } + + /** + * 指定查询lock + * @access public + * @param bool|string $lock 是否lock + * @return $this + */ + public function lock($lock = false) + { + $this->options['lock'] = $lock; + $this->options['master'] = true; + return $this; + } + + /** + * 指定distinct查询 + * @access public + * @param string $distinct 是否唯一 + * @return $this + */ + public function distinct($distinct) + { + $this->options['distinct'] = $distinct; + return $this; + } + + /** + * 指定数据表别名 + * @access public + * @param mixed $alias 数据表别名 + * @return $this + */ + public function alias($alias) + { + if (is_array($alias)) { + foreach ($alias as $key => $val) { + if (false !== strpos($key, '__')) { + $table = $this->parseSqlTable($key); + } else { + $table = $key; + } + $this->options['alias'][$table] = $val; + } + } else { + if (isset($this->options['table'])) { + $table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table']; + if (false !== strpos($table, '__')) { + $table = $this->parseSqlTable($table); + } + } else { + $table = $this->getTable(); + } + + $this->options['alias'][$table] = $alias; + } + return $this; + } + + /** + * 指定强制索引 + * @access public + * @param string $force 索引名称 + * @return $this + */ + public function force($force) + { + $this->options['force'] = $force; + return $this; + } + + /** + * 查询注释 + * @access public + * @param string $comment 注释 + * @return $this + */ + public function comment($comment) + { + $this->options['comment'] = $comment; + return $this; + } + + /** + * 获取执行的SQL语句 + * @access public + * @param boolean $fetch 是否返回sql + * @return $this + */ + public function fetchSql($fetch = true) + { + $this->options['fetch_sql'] = $fetch; + return $this; + } + + /** + * 不主动获取数据集 + * @access public + * @param bool $pdo 是否返回 PDOStatement 对象 + * @return $this + */ + public function fetchPdo($pdo = true) + { + $this->options['fetch_pdo'] = $pdo; + return $this; + } + + /** + * 设置从主服务器读取数据 + * @access public + * @return $this + */ + public function master() + { + $this->options['master'] = true; + return $this; + } + + /** + * 设置是否严格检查字段名 + * @access public + * @param bool $strict 是否严格检查字段 + * @return $this + */ + public function strict($strict = true) + { + $this->options['strict'] = $strict; + return $this; + } + + /** + * 设置查询数据不存在是否抛出异常 + * @access public + * @param bool $fail 数据不存在是否抛出异常 + * @return $this + */ + public function failException($fail = true) + { + $this->options['fail'] = $fail; + return $this; + } + + /** + * 设置自增序列名 + * @access public + * @param string $sequence 自增序列名 + * @return $this + */ + public function sequence($sequence = null) + { + $this->options['sequence'] = $sequence; + return $this; + } + + /** + * 指定数据表主键 + * @access public + * @param string $pk 主键 + * @return $this + */ + public function pk($pk) + { + $this->pk = $pk; + return $this; + } + + /** + * 查询日期或者时间 + * @access public + * @param string $field 日期字段名 + * @param string|array $op 比较运算符或者表达式 + * @param string|array $range 比较范围 + * @return $this + */ + public function whereTime($field, $op, $range = null) + { + if (is_null($range)) { + if (is_array($op)) { + $range = $op; + } else { + // 使用日期表达式 + switch (strtolower($op)) { + case 'today': + case 'd': + $range = ['today', 'tomorrow']; + break; + case 'week': + case 'w': + $range = ['this week 00:00:00', 'next week 00:00:00']; + break; + case 'month': + case 'm': + $range = ['first Day of this month 00:00:00', 'first Day of next month 00:00:00']; + break; + case 'year': + case 'y': + $range = ['this year 1/1', 'next year 1/1']; + break; + case 'yesterday': + $range = ['yesterday', 'today']; + break; + case 'last week': + $range = ['last week 00:00:00', 'this week 00:00:00']; + break; + case 'last month': + $range = ['first Day of last month 00:00:00', 'first Day of this month 00:00:00']; + break; + case 'last year': + $range = ['last year 1/1', 'this year 1/1']; + break; + default: + $range = $op; + } + } + $op = is_array($range) ? 'between' : '>'; + } + $this->where($field, strtolower($op) . ' time', $range); + return $this; + } + + /** + * 获取数据表信息 + * @access public + * @param mixed $tableName 数据表名 留空自动获取 + * @param string $fetch 获取信息类型 包括 fields type bind pk + * @return mixed + */ + public function getTableInfo($tableName = '', $fetch = '') + { + if (!$tableName) { + $tableName = $this->getTable(); + } + if (is_array($tableName)) { + $tableName = key($tableName) ?: current($tableName); + } + + if (strpos($tableName, ',')) { + // 多表不获取字段信息 + return false; + } else { + $tableName = $this->parseSqlTable($tableName); + } + + // 修正子查询作为表名的问题 + if (strpos($tableName, ')')) { + return []; + } + + list($guid) = explode(' ', $tableName); + $db = $this->getConfig('database'); + if (!isset(self::$info[$db . '.' . $guid])) { + if (!strpos($guid, '.')) { + $schema = $db . '.' . $guid; + } else { + $schema = $guid; + } + // 读取缓存 + if (!App::$debug && is_file(RUNTIME_PATH . 'schema/' . $schema . '.php')) { + $info = include RUNTIME_PATH . 'schema/' . $schema . '.php'; + } else { + $info = $this->connection->getFields($guid); + } + $fields = array_keys($info); + $bind = $type = []; + foreach ($info as $key => $val) { + // 记录字段类型 + $type[$key] = $val['type']; + $bind[$key] = $this->getFieldBindType($val['type']); + if (!empty($val['primary'])) { + $pk[] = $key; + } + } + if (isset($pk)) { + // 设置主键 + $pk = count($pk) > 1 ? $pk : $pk[0]; + } else { + $pk = null; + } + self::$info[$db . '.' . $guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + } + return $fetch ? self::$info[$db . '.' . $guid][$fetch] : self::$info[$db . '.' . $guid]; + } + + /** + * 获取当前数据表的主键 + * @access public + * @param string|array $options 数据表名或者查询参数 + * @return string|array + */ + public function getPk($options = '') + { + if (!empty($this->pk)) { + $pk = $this->pk; + } else { + $pk = $this->getTableInfo(is_array($options) ? $options['table'] : $options, 'pk'); + } + return $pk; + } + + // 获取当前数据表字段信息 + public function getTableFields($table = '') + { + return $this->getTableInfo($table ?: $this->getOptions('table'), 'fields'); + } + + // 获取当前数据表字段类型 + public function getFieldsType($table = '') + { + return $this->getTableInfo($table ?: $this->getOptions('table'), 'type'); + } + + // 获取当前数据表绑定信息 + public function getFieldsBind($table = '') + { + $types = $this->getFieldsType($table); + $bind = []; + if ($types) { + foreach ($types as $key => $type) { + $bind[$key] = $this->getFieldBindType($type); + } + } + return $bind; + } + + /** + * 获取字段绑定类型 + * @access public + * @param string $type 字段类型 + * @return integer + */ + protected function getFieldBindType($type) + { + if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) { + $bind = PDO::PARAM_STR; + } elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) { + $bind = PDO::PARAM_INT; + } elseif (preg_match('/bool/is', $type)) { + $bind = PDO::PARAM_BOOL; + } else { + $bind = PDO::PARAM_STR; + } + return $bind; + } + + /** + * 参数绑定 + * @access public + * @param mixed $key 参数名 + * @param mixed $value 绑定变量值 + * @param integer $type 绑定类型 + * @return $this + */ + public function bind($key, $value = false, $type = PDO::PARAM_STR) + { + if (is_array($key)) { + $this->bind = array_merge($this->bind, $key); + } else { + $this->bind[$key] = [$value, $type]; + } + return $this; + } + + /** + * 检测参数是否已经绑定 + * @access public + * @param string $key 参数名 + * @return bool + */ + public function isBind($key) + { + return isset($this->bind[$key]); + } + + /** + * 查询参数赋值 + * @access protected + * @param array $options 表达式参数 + * @return $this + */ + protected function options(array $options) + { + $this->options = $options; + return $this; + } + + /** + * 获取当前的查询参数 + * @access public + * @param string $name 参数名 + * @return mixed + */ + public function getOptions($name = '') + { + if ('' === $name) { + return $this->options; + } else { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + } + + /** + * 设置关联查询JOIN预查询 + * @access public + * @param string|array $with 关联方法名称 + * @return $this + */ + public function with($with) + { + if (empty($with)) { + return $this; + } + + if (is_string($with)) { + $with = explode(',', $with); + } + + $first = true; + + /** @var Model $class */ + $class = $this->model; + foreach ($with as $key => $relation) { + $subRelation = ''; + $closure = false; + if ($relation instanceof \Closure) { + // 支持闭包查询过滤关联条件 + $closure = $relation; + $relation = $key; + $with[$key] = $key; + } elseif (is_array($relation)) { + $subRelation = $relation; + $relation = $key; + } elseif (is_string($relation) && strpos($relation, '.')) { + $with[$key] = $relation; + list($relation, $subRelation) = explode('.', $relation, 2); + } + + /** @var Relation $model */ + $relation = Loader::parseName($relation, 1, false); + $model = $class->$relation(); + if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { + $model->eagerly($this, $relation, $subRelation, $closure, $first); + $first = false; + } elseif ($closure) { + $with[$key] = $closure; + } + } + $this->via(); + if (isset($this->options['with'])) { + $this->options['with'] = array_merge($this->options['with'], $with); + } else { + $this->options['with'] = $with; + } + return $this; + } + + /** + * 关联统计 + * @access public + * @param string|array $relation 关联方法名 + * @param bool $subQuery 是否使用子查询 + * @return $this + */ + public function withCount($relation, $subQuery = true) + { + if (!$subQuery) { + $this->options['with_count'] = $relation; + } else { + $relations = is_string($relation) ? explode(',', $relation) : $relation; + if (!isset($this->options['field'])) { + $this->field('*'); + } + foreach ($relations as $key => $relation) { + $closure = $name = null; + if ($relation instanceof \Closure) { + $closure = $relation; + $relation = $key; + } elseif (!is_int($key)) { + $name = $relation; + $relation = $key; + } + $relation = Loader::parseName($relation, 1, false); + + $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $name) . ')'; + + if (empty($name)) { + $name = Loader::parseName($relation) . '_count'; + } + + $this->field([$count => $name]); + } + } + return $this; + } + + /** + * 关联预加载中 获取关联指定字段值 + * example: + * Model::with(['relation' => function($query){ + * $query->withField("id,name"); + * }]) + * + * @param string | array $field 指定获取的字段 + * @return $this + */ + public function withField($field) + { + $this->options['with_field'] = $field; + return $this; + } + + /** + * 设置当前字段添加的表别名 + * @access public + * @param string $via + * @return $this + */ + public function via($via = '') + { + $this->options['via'] = $via; + return $this; + } + + /** + * 设置关联查询 + * @access public + * @param string|array $relation 关联名称 + * @return $this + */ + public function relation($relation) + { + if (empty($relation)) { + return $this; + } + if (is_string($relation)) { + $relation = explode(',', $relation); + } + if (isset($this->options['relation'])) { + $this->options['relation'] = array_merge($this->options['relation'], $relation); + } else { + $this->options['relation'] = $relation; + } + return $this; + } + + /** + * 把主键值转换为查询条件 支持复合主键 + * @access public + * @param array|string $data 主键数据 + * @param mixed $options 表达式参数 + * @return void + * @throws Exception + */ + protected function parsePkWhere($data, &$options) + { + $pk = $this->getPk($options); + // 获取当前数据表 + $table = is_array($options['table']) ? key($options['table']) : $options['table']; + if (!empty($options['alias'][$table])) { + $alias = $options['alias'][$table]; + } + if (is_string($pk)) { + $key = isset($alias) ? $alias . '.' . $pk : $pk; + // 根据主键查询 + if (is_array($data)) { + $where[$key] = isset($data[$pk]) ? $data[$pk] : ['in', $data]; + } else { + $where[$key] = strpos($data, ',') ? ['IN', $data] : $data; + } + } elseif (is_array($pk) && is_array($data) && !empty($data)) { + // 根据复合主键查询 + foreach ($pk as $key) { + if (isset($data[$key])) { + $attr = isset($alias) ? $alias . '.' . $key : $key; + $where[$attr] = $data[$key]; + } else { + throw new Exception('miss complex primary data'); + } + } + } + + if (!empty($where)) { + if (isset($options['where']['AND'])) { + $options['where']['AND'] = array_merge($options['where']['AND'], $where); + } else { + $options['where']['AND'] = $where; + } + } + return; + } + + /** + * 插入记录 + * @access public + * @param mixed $data 数据 + * @param boolean $replace 是否replace + * @param boolean $getLastInsID 返回自增主键 + * @param string $sequence 自增序列名 + * @return integer|string + */ + public function insert(array $data = [], $replace = false, $getLastInsID = false, $sequence = null) + { + // 分析查询表达式 + $options = $this->parseExpress(); + $data = array_merge($options['data'], $data); + // 生成SQL语句 + $sql = $this->builder->insert($data, $options, $replace); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } + + // 执行操作 + $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this); + if ($result) { + $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); + $lastInsId = $this->getLastInsID($sequence); + if ($lastInsId) { + $pk = $this->getPk($options); + if (is_string($pk)) { + $data[$pk] = $lastInsId; + } + } + $options['data'] = $data; + $this->trigger('after_insert', $options); + + if ($getLastInsID) { + return $lastInsId; + } + } + return $result; + } + + /** + * 插入记录并获取自增ID + * @access public + * @param mixed $data 数据 + * @param boolean $replace 是否replace + * @param string $sequence 自增序列名 + * @return integer|string + */ + public function insertGetId(array $data, $replace = false, $sequence = null) + { + return $this->insert($data, $replace, true, $sequence); + } + + /** + * 批量插入记录 + * @access public + * @param mixed $dataSet 数据集 + * @param boolean $replace 是否replace + * @param integer $limit 每次写入数据限制 + * @return integer|string + */ + public function insertAll(array $dataSet, $replace = false, $limit = null) + { + // 分析查询表达式 + $options = $this->parseExpress(); + if (!is_array(reset($dataSet))) { + return false; + } + + // 生成SQL语句 + if (is_null($limit)) { + $sql = $this->builder->insertAll($dataSet, $options, $replace); + } else { + $array = array_chunk($dataSet, $limit, true); + foreach ($array as $item) { + $sql[] = $this->builder->insertAll($item, $options, $replace); + } + } + + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } elseif (is_array($sql)) { + // 执行操作 + return $this->batchQuery($sql, $bind, $this); + } else { + // 执行操作 + return $this->execute($sql, $bind, $this); + } + } + + /** + * 通过Select方式插入记录 + * @access public + * @param string $fields 要插入的数据表字段名 + * @param string $table 要插入的数据表名 + * @return integer|string + * @throws PDOException + */ + public function selectInsert($fields, $table) + { + // 分析查询表达式 + $options = $this->parseExpress(); + // 生成SQL语句 + $table = $this->parseSqlTable($table); + $sql = $this->builder->selectInsert($fields, $table, $options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } else { + // 执行操作 + return $this->execute($sql, $bind, $this); + } + } + + /** + * 更新记录 + * @access public + * @param mixed $data 数据 + * @return integer|string + * @throws Exception + * @throws PDOException + */ + public function update(array $data = []) + { + $options = $this->parseExpress(); + $data = array_merge($options['data'], $data); + $pk = $this->getPk($options); + if (isset($options['cache']) && is_string($options['cache']['key'])) { + $key = $options['cache']['key']; + } + + if (empty($options['where'])) { + // 如果存在主键数据 则自动作为更新条件 + if (is_string($pk) && isset($data[$pk])) { + $where[$pk] = $data[$pk]; + if (!isset($key)) { + $key = 'think:' . $options['table'] . '|' . $data[$pk]; + } + unset($data[$pk]); + } elseif (is_array($pk)) { + // 增加复合主键支持 + foreach ($pk as $field) { + if (isset($data[$field])) { + $where[$field] = $data[$field]; + } else { + // 如果缺少复合主键数据则不执行 + throw new Exception('miss complex primary data'); + } + unset($data[$field]); + } + } + if (!isset($where)) { + // 如果没有任何更新条件则不执行 + throw new Exception('miss update condition'); + } else { + $options['where']['AND'] = $where; + } + } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) { + $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); + } + + // 生成UPDATE SQL语句 + $sql = $this->builder->update($data, $options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } else { + // 检测缓存 + if (isset($key) && Cache::get($key)) { + // 删除缓存 + Cache::rm($key); + } elseif (!empty($options['cache']['tag'])) { + Cache::clear($options['cache']['tag']); + } + // 执行操作 + $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this); + if ($result) { + if (is_string($pk) && isset($where[$pk])) { + $data[$pk] = $where[$pk]; + } elseif (is_string($pk) && isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $data[$pk] = $val; + } + $options['data'] = $data; + $this->trigger('after_update', $options); + } + return $result; + } + } + + /** + * 执行查询但只返回PDOStatement对象 + * @access public + * @return \PDOStatement|string + */ + public function getPdo() + { + // 分析查询表达式 + $options = $this->parseExpress(); + // 生成查询SQL + $sql = $this->builder->select($options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } + // 执行查询操作 + return $this->query($sql, $bind, $options['master'], true); + } + + /** + * 查找记录 + * @access public + * @param array|string|Query|\Closure $data + * @return Collection|false|\PDOStatement|string + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function select($data = null) + { + if ($data instanceof Query) { + return $data->select(); + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $this]); + $data = null; + } + // 分析查询表达式 + $options = $this->parseExpress(); + + if (false === $data) { + // 用于子查询 不查询只返回SQL + $options['fetch_sql'] = true; + } elseif (!is_null($data)) { + // 主键条件分析 + $this->parsePkWhere($data, $options); + } + + $resultSet = false; + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + // 判断查询缓存 + $cache = $options['cache']; + unset($options['cache']); + $key = is_string($cache['key']) ? $cache['key'] : md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind)); + $resultSet = Cache::get($key); + } + if (false === $resultSet) { + // 生成查询SQL + $sql = $this->builder->select($options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } + + $options['data'] = $data; + if ($resultSet = $this->trigger('before_select', $options)) { + } else { + // 执行查询操作 + $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + + if ($resultSet instanceof \PDOStatement) { + // 返回PDOStatement对象 + return $resultSet; + } + } + + if (isset($cache) && false !== $resultSet) { + // 缓存数据集 + $this->cacheData($key, $resultSet, $cache); + } + } + + // 数据列表读取后的处理 + if (!empty($this->model)) { + // 生成模型对象 + if (count($resultSet) > 0) { + foreach ($resultSet as $key => $result) { + /** @var Model $model */ + $model = $this->model->newInstance($result); + $model->isUpdate(true); + + // 关联查询 + if (!empty($options['relation'])) { + $model->relationQuery($options['relation']); + } + // 关联统计 + if (!empty($options['with_count'])) { + $model->relationCount($model, $options['with_count']); + } + $resultSet[$key] = $model; + } + if (!empty($options['with'])) { + // 预载入 + $model->eagerlyResultSet($resultSet, $options['with']); + } + // 模型数据集转换 + $resultSet = $model->toCollection($resultSet); + } else { + $resultSet = $this->model->toCollection($resultSet); + } + } elseif ('collection' == $this->connection->getConfig('resultset_type')) { + // 返回Collection对象 + $resultSet = new Collection($resultSet); + } + // 返回结果处理 + if (!empty($options['fail']) && count($resultSet) == 0) { + $this->throwNotFound($options); + } + return $resultSet; + } + + /** + * 缓存数据 + * @access public + * @param string $key 缓存标识 + * @param mixed $data 缓存数据 + * @param array $config 缓存参数 + */ + protected function cacheData($key, $data, $config = []) + { + if (isset($config['tag'])) { + Cache::tag($config['tag'])->set($key, $data, $config['expire']); + } else { + Cache::set($key, $data, $config['expire']); + } + } + + /** + * 生成缓存标识 + * @access public + * @param mixed $value 缓存数据 + * @param array $options 缓存参数 + * @param array $bind 绑定参数 + * @return string + */ + protected function getCacheKey($value, $options, $bind = []) + { + if (is_scalar($value)) { + $data = $value; + } elseif (is_array($value) && is_string($value[0]) && 'eq' == strtolower($value[0])) { + $data = $value[1]; + } + $prefix = $this->connection->getConfig('database') . '.'; + + if (isset($data)) { + return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; + } + + try { + return md5($prefix . serialize($options) . serialize($bind)); + } catch (\Exception $e) { + throw new Exception('closure not support cache(true)'); + } + } + + /** + * 查找单条记录 + * @access public + * @param array|string|Query|\Closure $data + * @return array|false|\PDOStatement|string|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function find($data = null) + { + if ($data instanceof Query) { + return $data->find(); + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $this]); + $data = null; + } + // 分析查询表达式 + $options = $this->parseExpress(); + $pk = $this->getPk($options); + if (!is_null($data)) { + // AR模式分析主键条件 + $this->parsePkWhere($data, $options); + } elseif (!empty($options['cache']) && true === $options['cache']['key'] && is_string($pk) && isset($options['where']['AND'][$pk])) { + $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); + } + + $options['limit'] = 1; + $result = false; + if (empty($options['fetch_sql']) && !empty($options['cache'])) { + // 判断查询缓存 + $cache = $options['cache']; + if (true === $cache['key'] && !is_null($data) && !is_array($data)) { + $key = 'think:' . $this->connection->getConfig('database') . '.' . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; + } elseif (is_string($cache['key'])) { + $key = $cache['key']; + } elseif (!isset($key)) { + $key = md5($this->connection->getConfig('database') . '.' . serialize($options) . serialize($this->bind)); + } + $result = Cache::get($key); + } + if (false === $result) { + // 生成查询SQL + $sql = $this->builder->select($options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } + if (is_string($pk)) { + if (!is_array($data)) { + if (isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $item[$pk] = $val; + } else { + $item[$pk] = $data; + } + $data = $item; + } + } + $options['data'] = $data; + // 事件回调 + if ($result = $this->trigger('before_find', $options)) { + } else { + // 执行查询 + $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']); + + if ($resultSet instanceof \PDOStatement) { + // 返回PDOStatement对象 + return $resultSet; + } + $result = isset($resultSet[0]) ? $resultSet[0] : null; + } + + if (isset($cache) && $result) { + // 缓存数据 + $this->cacheData($key, $result, $cache); + } + } + + // 数据处理 + if (!empty($result)) { + if (!empty($this->model)) { + // 返回模型对象 + $result = $this->model->newInstance($result); + $result->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null); + // 关联查询 + if (!empty($options['relation'])) { + $result->relationQuery($options['relation']); + } + // 预载入查询 + if (!empty($options['with'])) { + $result->eagerlyResult($result, $options['with']); + } + // 关联统计 + if (!empty($options['with_count'])) { + $result->relationCount($result, $options['with_count']); + } + } + } elseif (!empty($options['fail'])) { + $this->throwNotFound($options); + } + return $result; + } + + /** + * 查询失败 抛出异常 + * @access public + * @param array $options 查询参数 + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + protected function throwNotFound($options = []) + { + if (!empty($this->model)) { + $class = get_class($this->model); + throw new ModelNotFoundException('model data Not Found:' . $class, $class, $options); + } else { + $table = is_array($options['table']) ? key($options['table']) : $options['table']; + throw new DataNotFoundException('table data not Found:' . $table, $table, $options); + } + } + + /** + * 查找多条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return array|\PDOStatement|string|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function selectOrFail($data = null) + { + return $this->failException(true)->select($data); + } + + /** + * 查找单条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return array|\PDOStatement|string|Model + * @throws DbException + * @throws ModelNotFoundException + * @throws DataNotFoundException + */ + public function findOrFail($data = null) + { + return $this->failException(true)->find($data); + } + + /** + * 分批数据返回处理 + * @access public + * @param integer $count 每次处理的数据数量 + * @param callable $callback 处理回调方法 + * @param string $column 分批处理的字段名 + * @param string $order 排序规则 + * @return boolean + * @throws \LogicException + */ + public function chunk($count, $callback, $column = null, $order = 'asc') + { + $options = $this->getOptions(); + if (empty($options['table'])) { + $options['table'] = $this->getTable(); + } + $column = $column ?: $this->getPk($options); + + if (isset($options['order'])) { + if (App::$debug) { + throw new \LogicException('chunk not support call order'); + } + unset($options['order']); + } + $bind = $this->bind; + if (is_array($column)) { + $times = 1; + $query = $this->options($options)->page($times, $count); + } else { + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + $query = $this->options($options)->limit($count); + } + $resultSet = $query->order($column, $order)->select(); + + while (count($resultSet) > 0) { + if ($resultSet instanceof Collection) { + $resultSet = $resultSet->all(); + } + + if (false === call_user_func($callback, $resultSet)) { + return false; + } + + if (is_array($column)) { + $times++; + $query = $this->options($options)->page($times, $count); + } else { + $end = end($resultSet); + $lastId = is_array($end) ? $end[$key] : $end->getData($key); + $query = $this->options($options) + ->limit($count) + ->where($column, 'asc' == strtolower($order) ? '>' : '<', $lastId); + } + + $resultSet = $query->bind($bind)->order($column, $order)->select(); + } + + return true; + } + + /** + * 获取绑定的参数 并清空 + * @access public + * @return array + */ + public function getBind() + { + $bind = $this->bind; + $this->bind = []; + return $bind; + } + + /** + * 创建子查询SQL + * @access public + * @param bool $sub + * @return string + * @throws DbException + */ + public function buildSql($sub = true) + { + return $sub ? '( ' . $this->select(false) . ' )' : $this->select(false); + } + + /** + * 删除记录 + * @access public + * @param mixed $data 表达式 true 表示强制删除 + * @return int + * @throws Exception + * @throws PDOException + */ + public function delete($data = null) + { + // 分析查询表达式 + $options = $this->parseExpress(); + $pk = $this->getPk($options); + if (isset($options['cache']) && is_string($options['cache']['key'])) { + $key = $options['cache']['key']; + } + + if (!is_null($data) && true !== $data) { + if (!isset($key) && !is_array($data)) { + // 缓存标识 + $key = 'think:' . $options['table'] . '|' . $data; + } + // AR模式分析主键条件 + $this->parsePkWhere($data, $options); + } elseif (!isset($key) && is_string($pk) && isset($options['where']['AND'][$pk])) { + $key = $this->getCacheKey($options['where']['AND'][$pk], $options, $this->bind); + } + + if (true !== $data && empty($options['where'])) { + // 如果条件为空 不进行删除操作 除非设置 1=1 + throw new Exception('delete without condition'); + } + // 生成删除SQL语句 + $sql = $this->builder->delete($options); + // 获取参数绑定 + $bind = $this->getBind(); + if ($options['fetch_sql']) { + // 获取实际执行的SQL语句 + return $this->connection->getRealSql($sql, $bind); + } + + // 检测缓存 + if (isset($key) && Cache::get($key)) { + // 删除缓存 + Cache::rm($key); + } elseif (!empty($options['cache']['tag'])) { + Cache::clear($options['cache']['tag']); + } + // 执行操作 + $result = $this->execute($sql, $bind, $this); + if ($result) { + if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { + list($a, $val) = explode('|', $key); + $item[$pk] = $val; + $data = $item; + } + $options['data'] = $data; + $this->trigger('after_delete', $options); + } + return $result; + } + + /** + * 分析表达式(可用于查询或者写入操作) + * @access protected + * @return array + */ + protected function parseExpress() + { + $options = $this->options; + + // 获取数据表 + if (empty($options['table'])) { + $options['table'] = $this->getTable(); + } + + if (!isset($options['where'])) { + $options['where'] = []; + } elseif (isset($options['view'])) { + // 视图查询条件处理 + foreach (['AND', 'OR'] as $logic) { + if (isset($options['where'][$logic])) { + foreach ($options['where'][$logic] as $key => $val) { + if (array_key_exists($key, $options['map'])) { + $options['where'][$logic][$options['map'][$key]] = $val; + unset($options['where'][$logic][$key]); + } + } + } + } + + if (isset($options['order'])) { + // 视图查询排序处理 + if (is_string($options['order'])) { + $options['order'] = explode(',', $options['order']); + } + foreach ($options['order'] as $key => $val) { + if (is_numeric($key)) { + if (strpos($val, ' ')) { + list($field, $sort) = explode(' ', $val); + if (array_key_exists($field, $options['map'])) { + $options['order'][$options['map'][$field]] = $sort; + unset($options['order'][$key]); + } + } elseif (array_key_exists($val, $options['map'])) { + $options['order'][$options['map'][$val]] = 'asc'; + unset($options['order'][$key]); + } + } elseif (array_key_exists($key, $options['map'])) { + $options['order'][$options['map'][$key]] = $val; + unset($options['order'][$key]); + } + } + } + } + + if (!isset($options['field'])) { + $options['field'] = '*'; + } + + if (!isset($options['data'])) { + $options['data'] = []; + } + + if (!isset($options['strict'])) { + $options['strict'] = $this->getConfig('fields_strict'); + } + + foreach (['master', 'lock', 'fetch_pdo', 'fetch_sql', 'distinct'] as $name) { + if (!isset($options[$name])) { + $options[$name] = false; + } + } + + if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) { + $options['master'] = true; + } + + foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { + if (!isset($options[$name])) { + $options[$name] = ''; + } + } + + if (isset($options['page'])) { + // 根据页数计算limit + list($page, $listRows) = $options['page']; + $page = $page > 0 ? $page : 1; + $listRows = $listRows > 0 ? $listRows : (is_numeric($options['limit']) ? $options['limit'] : 20); + $offset = $listRows * ($page - 1); + $options['limit'] = $offset . ',' . $listRows; + } + + $this->options = []; + return $options; + } + + /** + * 注册回调方法 + * @access public + * @param string $event 事件名 + * @param callable $callback 回调方法 + * @return void + */ + public static function event($event, $callback) + { + self::$event[$event] = $callback; + } + + /** + * 触发事件 + * @access protected + * @param string $event 事件名 + * @param mixed $params 额外参数 + * @return bool + */ + protected function trigger($event, $params = []) + { + $result = false; + if (isset(self::$event[$event])) { + $callback = self::$event[$event]; + $result = call_user_func_array($callback, [$params, $this]); + } + return $result; + } +} diff --git a/source/thinkphp/library/think/db/builder/Mysql.php b/source/thinkphp/library/think/db/builder/Mysql.php new file mode 100644 index 0000000..be2af71 --- /dev/null +++ b/source/thinkphp/library/think/db/builder/Mysql.php @@ -0,0 +1,137 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\builder; + +use think\db\Builder; +use think\Exception; + +/** + * mysql数据库驱动 + */ +class Mysql extends Builder +{ + + protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%'; + protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%'; + + /** + * 生成insertall SQL + * @access public + * @param array $dataSet 数据集 + * @param array $options 表达式 + * @param bool $replace 是否replace + * @return string + * @throws Exception + */ + public function insertAll($dataSet, $options = [], $replace = false) + { + // 获取合法的字段 + if ('*' == $options['field']) { + $fields = array_keys($this->query->getFieldsType($options['table'])); + } else { + $fields = $options['field']; + } + + foreach ($dataSet as $data) { + foreach ($data as $key => $val) { + if (!in_array($key, $fields, true)) { + if ($options['strict']) { + throw new Exception('fields not exists:[' . $key . ']'); + } + unset($data[$key]); + } elseif (is_null($val)) { + $data[$key] = 'NULL'; + } elseif (is_scalar($val)) { + $data[$key] = $this->parseValue($val, $key); + } elseif (is_object($val) && method_exists($val, '__toString')) { + // 对象数据写入 + $data[$key] = $val->__toString(); + } else { + // 过滤掉非标量数据 + unset($data[$key]); + } + } + $value = array_values($data); + $values[] = '( ' . implode(',', $value) . ' )'; + + if (!isset($insertFields)) { + $insertFields = array_map([$this, 'parseKey'], array_keys($data)); + } + } + + return str_replace( + ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], + [ + $replace ? 'REPLACE' : 'INSERT', + $this->parseTable($options['table'], $options), + implode(' , ', $insertFields), + implode(' , ', $values), + $this->parseComment($options['comment']), + ], $this->insertAllSql); + } + + /** + * 字段和表名处理 + * @access protected + * @param mixed $key + * @param array $options + * @return string + */ + protected function parseKey($key, $options = [], $strict = false) + { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + + $key = trim($key); + if (strpos($key, '$.') && false === strpos($key, '(')) { + // JSON字段支持 + list($field, $name) = explode('$.', $key); + return 'json_extract(' . $field . ', \'$.' . $name . '\')'; + } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { + list($table, $key) = explode('.', $key, 2); + if ('__TABLE__' == $table) { + $table = $this->query->getTable(); + } + if (isset($options['alias'][$table])) { + $table = $options['alias'][$table]; + } + } + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) { + $key = '`' . $key . '`'; + } + if (isset($table)) { + if (strpos($table, '.')) { + $table = str_replace('.', '`.`', $table); + } + $key = '`' . $table . '`.' . $key; + } + return $key; + } + + /** + * 随机排序 + * @access protected + * @return string + */ + protected function parseRand() + { + return 'rand()'; + } + +} diff --git a/source/thinkphp/library/think/db/builder/Pgsql.php b/source/thinkphp/library/think/db/builder/Pgsql.php new file mode 100644 index 0000000..acc2289 --- /dev/null +++ b/source/thinkphp/library/think/db/builder/Pgsql.php @@ -0,0 +1,89 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\builder; + +use think\db\Builder; + +/** + * Pgsql数据库驱动 + */ +class Pgsql extends Builder +{ + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + + /** + * limit分析 + * @access protected + * @param mixed $limit + * @return string + */ + public function parseLimit($limit) + { + $limitStr = ''; + if (!empty($limit)) { + $limit = explode(',', $limit); + if (count($limit) > 1) { + $limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' '; + } else { + $limitStr .= ' LIMIT ' . $limit[0] . ' '; + } + } + return $limitStr; + } + + /** + * 字段和表名处理 + * @access protected + * @param mixed $key + * @param array $options + * @return string + */ + protected function parseKey($key, $options = [], $strict = false) + { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + + $key = trim($key); + if (strpos($key, '$.') && false === strpos($key, '(')) { + // JSON字段支持 + list($field, $name) = explode('$.', $key); + $key = $field . '->>\'' . $name . '\''; + } elseif (strpos($key, '.')) { + list($table, $key) = explode('.', $key, 2); + if ('__TABLE__' == $table) { + $table = $this->query->getTable(); + } + if (isset($options['alias'][$table])) { + $table = $options['alias'][$table]; + } + } + if (isset($table)) { + $key = $table . '.' . $key; + } + return $key; + } + + /** + * 随机排序 + * @access protected + * @return string + */ + protected function parseRand() + { + return 'RANDOM()'; + } + +} diff --git a/source/thinkphp/library/think/db/builder/Sqlite.php b/source/thinkphp/library/think/db/builder/Sqlite.php new file mode 100644 index 0000000..c727f04 --- /dev/null +++ b/source/thinkphp/library/think/db/builder/Sqlite.php @@ -0,0 +1,82 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\builder; + +use think\db\Builder; + +/** + * Sqlite数据库驱动 + */ +class Sqlite extends Builder +{ + + /** + * limit + * @access public + * @param string $limit + * @return string + */ + public function parseLimit($limit) + { + $limitStr = ''; + if (!empty($limit)) { + $limit = explode(',', $limit); + if (count($limit) > 1) { + $limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' '; + } else { + $limitStr .= ' LIMIT ' . $limit[0] . ' '; + } + } + return $limitStr; + } + + /** + * 随机排序 + * @access protected + * @return string + */ + protected function parseRand() + { + return 'RANDOM()'; + } + + /** + * 字段和表名处理 + * @access protected + * @param mixed $key + * @param array $options + * @return string + */ + protected function parseKey($key, $options = [], $strict = false) + { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + + $key = trim($key); + if (strpos($key, '.')) { + list($table, $key) = explode('.', $key, 2); + if ('__TABLE__' == $table) { + $table = $this->query->getTable(); + } + if (isset($options['alias'][$table])) { + $table = $options['alias'][$table]; + } + } + if (isset($table)) { + $key = $table . '.' . $key; + } + return $key; + } +} diff --git a/source/thinkphp/library/think/db/builder/Sqlsrv.php b/source/thinkphp/library/think/db/builder/Sqlsrv.php new file mode 100644 index 0000000..dc425d9 --- /dev/null +++ b/source/thinkphp/library/think/db/builder/Sqlsrv.php @@ -0,0 +1,137 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\builder; + +use think\db\Builder; +use think\db\Expression; + +/** + * Sqlsrv数据库驱动 + */ +class Sqlsrv extends Builder +{ + protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%'; + protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%'; + protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%'; + protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%'; + protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%'; + + /** + * order分析 + * @access protected + * @param mixed $order + * @param array $options + * @return string + */ + protected function parseOrder($order, $options = []) + { + if (empty($order)) { + return ' ORDER BY rand()'; + } + + $array = []; + foreach ($order as $key => $val) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_numeric($key)) { + if (false === strpos($val, '(')) { + $array[] = $this->parseKey($val, $options); + } elseif ('[rand]' == $val) { + $array[] = $this->parseRand(); + } else { + $array[] = $val; + } + } else { + $sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : ''; + $array[] = $this->parseKey($key, $options, true) . ' ' . $sort; + } + } + + return ' ORDER BY ' . implode(',', $array); + } + + /** + * 随机排序 + * @access protected + * @return string + */ + protected function parseRand() + { + return 'rand()'; + } + + /** + * 字段和表名处理 + * @access protected + * @param mixed $key + * @param array $options + * @return string + */ + protected function parseKey($key, $options = [], $strict = false) + { + if (is_numeric($key)) { + return $key; + } elseif ($key instanceof Expression) { + return $key->getValue(); + } + $key = trim($key); + if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { + list($table, $key) = explode('.', $key, 2); + if ('__TABLE__' == $table) { + $table = $this->query->getTable(); + } + if (isset($options['alias'][$table])) { + $table = $options['alias'][$table]; + } + } + + if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { + throw new Exception('not support data:' . $key); + } + if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { + $key = '[' . $key . ']'; + } + if (isset($table)) { + $key = '[' . $table . '].' . $key; + } + return $key; + } + + /** + * limit + * @access protected + * @param mixed $limit + * @return string + */ + protected function parseLimit($limit) + { + if (empty($limit)) { + return ''; + } + + $limit = explode(',', $limit); + if (count($limit) > 1) { + $limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')'; + } else { + $limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")"; + } + return 'WHERE ' . $limitStr; + } + + public function selectInsert($fields, $table, $options) + { + $this->selectSql = $this->selectInsertSql; + return parent::selectInsert($fields, $table, $options); + } + +} diff --git a/source/thinkphp/library/think/db/connector/Mysql.php b/source/thinkphp/library/think/db/connector/Mysql.php new file mode 100644 index 0000000..be1a85c --- /dev/null +++ b/source/thinkphp/library/think/db/connector/Mysql.php @@ -0,0 +1,126 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\connector; + +use PDO; +use think\db\Connection; +use think\Log; + +/** + * mysql数据库驱动 + */ +class Mysql extends Connection +{ + + protected $builder = '\\think\\db\\builder\\Mysql'; + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + protected function parseDsn($config) + { + if (!empty($config['socket'])) { + $dsn = 'mysql:unix_socket=' . $config['socket']; + } elseif (!empty($config['hostport'])) { + $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport']; + } else { + $dsn = 'mysql:host=' . $config['hostname']; + } + $dsn .= ';dbname=' . $config['database']; + + if (!empty($config['charset'])) { + $dsn .= ';charset=' . $config['charset']; + } + return $dsn; + } + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName + * @return array + */ + public function getFields($tableName) + { + list($tableName) = explode(' ', $tableName); + if (false === strpos($tableName, '`')) { + if (strpos($tableName, '.')) { + $tableName = str_replace('.', '`.`', $tableName); + } + $tableName = '`' . $tableName . '`'; + } + $sql = 'SHOW COLUMNS FROM ' . $tableName; + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + if ($result) { + foreach ($result as $key => $val) { + $val = array_change_key_case($val); + $info[$val['field']] = [ + 'name' => $val['field'], + 'type' => $val['type'], + 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes + 'default' => $val['default'], + 'primary' => (strtolower($val['key']) == 'pri'), + 'autoinc' => (strtolower($val['extra']) == 'auto_increment'), + ]; + } + } + return $this->fieldCase($info); + } + + /** + * 取得数据库的表信息 + * @access public + * @param string $dbName + * @return array + */ + public function getTables($dbName = '') + { + $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES '; + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + foreach ($result as $key => $val) { + $info[$key] = current($val); + } + return $info; + } + + /** + * SQL性能分析 + * @access protected + * @param string $sql + * @return array + */ + protected function getExplain($sql) + { + $pdo = $this->linkID->query("EXPLAIN " . $sql); + $result = $pdo->fetch(PDO::FETCH_ASSOC); + $result = array_change_key_case($result); + if (isset($result['extra'])) { + if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) { + Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn'); + } + } + return $result; + } + + protected function supportSavepoint() + { + return true; + } + +} diff --git a/source/thinkphp/library/think/db/connector/Pgsql.php b/source/thinkphp/library/think/db/connector/Pgsql.php new file mode 100644 index 0000000..bbcf576 --- /dev/null +++ b/source/thinkphp/library/think/db/connector/Pgsql.php @@ -0,0 +1,103 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\connector; + +use PDO; +use think\db\Connection; + +/** + * Pgsql数据库驱动 + */ +class Pgsql extends Connection +{ + protected $builder = '\\think\\db\\builder\\Pgsql'; + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + protected function parseDsn($config) + { + $dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname']; + if (!empty($config['hostport'])) { + $dsn .= ';port=' . $config['hostport']; + } + return $dsn; + } + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName + * @return array + */ + public function getFields($tableName) + { + + list($tableName) = explode(' ', $tableName); + $sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');'; + + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + if ($result) { + foreach ($result as $key => $val) { + $val = array_change_key_case($val); + $info[$val['field']] = [ + 'name' => $val['field'], + 'type' => $val['type'], + 'notnull' => (bool) ('' !== $val['null']), + 'default' => $val['default'], + 'primary' => !empty($val['key']), + 'autoinc' => (0 === strpos($val['extra'], 'nextval(')), + ]; + } + } + return $this->fieldCase($info); + } + + /** + * 取得数据库的表信息 + * @access public + * @param string $dbName + * @return array + */ + public function getTables($dbName = '') + { + $sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'"; + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + foreach ($result as $key => $val) { + $info[$key] = current($val); + } + return $info; + } + + /** + * SQL性能分析 + * @access protected + * @param string $sql + * @return array + */ + protected function getExplain($sql) + { + return []; + } + + protected function supportSavepoint() + { + return true; + } +} diff --git a/source/thinkphp/library/think/db/connector/Sqlite.php b/source/thinkphp/library/think/db/connector/Sqlite.php new file mode 100644 index 0000000..c4e3a72 --- /dev/null +++ b/source/thinkphp/library/think/db/connector/Sqlite.php @@ -0,0 +1,104 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\connector; + +use PDO; +use think\db\Connection; + +/** + * Sqlite数据库驱动 + */ +class Sqlite extends Connection +{ + + protected $builder = '\\think\\db\\builder\\Sqlite'; + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + protected function parseDsn($config) + { + $dsn = 'sqlite:' . $config['database']; + return $dsn; + } + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName + * @return array + */ + public function getFields($tableName) + { + list($tableName) = explode(' ', $tableName); + $sql = 'PRAGMA table_info( ' . $tableName . ' )'; + + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + if ($result) { + foreach ($result as $key => $val) { + $val = array_change_key_case($val); + $info[$val['name']] = [ + 'name' => $val['name'], + 'type' => $val['type'], + 'notnull' => 1 === $val['notnull'], + 'default' => $val['dflt_value'], + 'primary' => '1' == $val['pk'], + 'autoinc' => '1' == $val['pk'], + ]; + } + } + return $this->fieldCase($info); + } + + /** + * 取得数据库的表信息 + * @access public + * @param string $dbName + * @return array + */ + public function getTables($dbName = '') + { + + $sql = "SELECT name FROM sqlite_master WHERE type='table' " + . "UNION ALL SELECT name FROM sqlite_temp_master " + . "WHERE type='table' ORDER BY name"; + + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + foreach ($result as $key => $val) { + $info[$key] = current($val); + } + return $info; + } + + /** + * SQL性能分析 + * @access protected + * @param string $sql + * @return array + */ + protected function getExplain($sql) + { + return []; + } + + protected function supportSavepoint() + { + return true; + } +} diff --git a/source/thinkphp/library/think/db/connector/Sqlsrv.php b/source/thinkphp/library/think/db/connector/Sqlsrv.php new file mode 100644 index 0000000..35c6600 --- /dev/null +++ b/source/thinkphp/library/think/db/connector/Sqlsrv.php @@ -0,0 +1,125 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\connector; + +use PDO; +use think\db\Connection; + +/** + * Sqlsrv数据库驱动 + */ +class Sqlsrv extends Connection +{ + // PDO连接参数 + protected $params = [ + PDO::ATTR_CASE => PDO::CASE_NATURAL, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_STRINGIFY_FETCHES => false, + ]; + protected $builder = '\\think\\db\\builder\\Sqlsrv'; + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + protected function parseDsn($config) + { + $dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname']; + if (!empty($config['hostport'])) { + $dsn .= ',' . $config['hostport']; + } + return $dsn; + } + + /** + * 取得数据表的字段信息 + * @access public + * @param string $tableName + * @return array + */ + public function getFields($tableName) + { + list($tableName) = explode(' ', $tableName); + $tableNames = explode('.', $tableName); + $tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0]; + + $sql = "SELECT column_name, data_type, column_default, is_nullable + FROM information_schema.tables AS t + JOIN information_schema.columns AS c + ON t.table_catalog = c.table_catalog + AND t.table_schema = c.table_schema + AND t.table_name = c.table_name + WHERE t.table_name = '$tableName'"; + + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + if ($result) { + foreach ($result as $key => $val) { + $val = array_change_key_case($val); + $info[$val['column_name']] = [ + 'name' => $val['column_name'], + 'type' => $val['data_type'], + 'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes + 'default' => $val['column_default'], + 'primary' => false, + 'autoinc' => false, + ]; + } + } + $sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'"; + // 调试开始 + $this->debug(true); + $pdo = $this->linkID->query($sql); + // 调试结束 + $this->debug(false, $sql); + $result = $pdo->fetch(PDO::FETCH_ASSOC); + if ($result) { + $info[$result['column_name']]['primary'] = true; + } + return $this->fieldCase($info); + } + + /** + * 取得数据表的字段信息 + * @access public + * @param string $dbName + * @return array + */ + public function getTables($dbName = '') + { + $sql = "SELECT TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_TYPE = 'BASE TABLE' + "; + + $pdo = $this->query($sql, [], false, true); + $result = $pdo->fetchAll(PDO::FETCH_ASSOC); + $info = []; + foreach ($result as $key => $val) { + $info[$key] = current($val); + } + return $info; + } + + /** + * SQL性能分析 + * @access protected + * @param string $sql + * @return array + */ + protected function getExplain($sql) + { + return []; + } +} diff --git a/source/thinkphp/library/think/db/connector/pgsql.sql b/source/thinkphp/library/think/db/connector/pgsql.sql new file mode 100644 index 0000000..e1a09a3 --- /dev/null +++ b/source/thinkphp/library/think/db/connector/pgsql.sql @@ -0,0 +1,117 @@ +CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS +$BODY$ +DECLARE + v_type varchar; +BEGIN + IF a_type='int8' THEN + v_type:='bigint'; + ELSIF a_type='int4' THEN + v_type:='integer'; + ELSIF a_type='int2' THEN + v_type:='smallint'; + ELSIF a_type='bpchar' THEN + v_type:='char'; + ELSE + v_type:=a_type; + END IF; + RETURN v_type; +END; +$BODY$ +LANGUAGE PLPGSQL; + +CREATE TYPE "public"."tablestruct" AS ( + "fields_key_name" varchar(100), + "fields_name" VARCHAR(200), + "fields_type" VARCHAR(20), + "fields_length" BIGINT, + "fields_not_null" VARCHAR(10), + "fields_default" VARCHAR(500), + "fields_comment" VARCHAR(1000) +); + +CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS +$body$ +DECLARE + v_ret tablestruct; + v_oid oid; + v_sql varchar; + v_rec RECORD; + v_key varchar; +BEGIN + SELECT + pg_class.oid INTO v_oid + FROM + pg_class + INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name) + WHERE + pg_class.relname=a_table_name; + IF NOT FOUND THEN + RETURN; + END IF; + + v_sql=' + SELECT + pg_attribute.attname AS fields_name, + pg_attribute.attnum AS fields_index, + pgsql_type(pg_type.typname::varchar) AS fields_type, + pg_attribute.atttypmod-4 as fields_length, + CASE WHEN pg_attribute.attnotnull THEN ''not null'' + ELSE '''' + END AS fields_not_null, + pg_attrdef.adsrc AS fields_default, + pg_description.description AS fields_comment + FROM + pg_attribute + INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid + INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid + LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum + LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum + WHERE + pg_attribute.attnum > 0 + AND attisdropped <> ''t'' + AND pg_class.oid = ' || v_oid || ' + ORDER BY pg_attribute.attnum' ; + + FOR v_rec IN EXECUTE v_sql LOOP + v_ret.fields_name=v_rec.fields_name; + v_ret.fields_type=v_rec.fields_type; + IF v_rec.fields_length > 0 THEN + v_ret.fields_length:=v_rec.fields_length; + ELSE + v_ret.fields_length:=NULL; + END IF; + v_ret.fields_not_null=v_rec.fields_not_null; + v_ret.fields_default=v_rec.fields_default; + v_ret.fields_comment=v_rec.fields_comment; + SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name; + IF FOUND THEN + v_ret.fields_key_name=v_key; + ELSE + v_ret.fields_key_name=''; + END IF; + RETURN NEXT v_ret; + END LOOP; + RETURN ; +END; +$body$ +LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; + +COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar) +IS '获得表信息'; + +---重载一个函数 +CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS +$body$ +DECLARE + v_ret tablestruct; +BEGIN + FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP + RETURN NEXT v_ret; + END LOOP; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; + +COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar) +IS '获得表信息'; \ No newline at end of file diff --git a/source/thinkphp/library/think/db/exception/BindParamException.php b/source/thinkphp/library/think/db/exception/BindParamException.php new file mode 100644 index 0000000..4ed1954 --- /dev/null +++ b/source/thinkphp/library/think/db/exception/BindParamException.php @@ -0,0 +1,35 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\exception; + +use think\exception\DbException; + +/** + * PDO参数绑定异常 + */ +class BindParamException extends DbException +{ + + /** + * BindParamException constructor. + * @param string $message + * @param array $config + * @param string $sql + * @param array $bind + * @param int $code + */ + public function __construct($message, $config, $sql, $bind, $code = 10502) + { + $this->setData('Bind Param', $bind); + parent::__construct($message, $config, $sql, $code); + } +} diff --git a/source/thinkphp/library/think/db/exception/DataNotFoundException.php b/source/thinkphp/library/think/db/exception/DataNotFoundException.php new file mode 100644 index 0000000..f2542ac --- /dev/null +++ b/source/thinkphp/library/think/db/exception/DataNotFoundException.php @@ -0,0 +1,43 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\exception; + +use think\exception\DbException; + +class DataNotFoundException extends DbException +{ + protected $table; + + /** + * DbException constructor. + * @param string $message + * @param string $table + * @param array $config + */ + public function __construct($message, $table = '', array $config = []) + { + $this->message = $message; + $this->table = $table; + + $this->setData('Database Config', $config); + } + + /** + * 获取数据表名 + * @access public + * @return string + */ + public function getTable() + { + return $this->table; + } +} diff --git a/source/thinkphp/library/think/db/exception/ModelNotFoundException.php b/source/thinkphp/library/think/db/exception/ModelNotFoundException.php new file mode 100644 index 0000000..6e5f930 --- /dev/null +++ b/source/thinkphp/library/think/db/exception/ModelNotFoundException.php @@ -0,0 +1,43 @@ + +// +---------------------------------------------------------------------- + +namespace think\db\exception; + +use think\exception\DbException; + +class ModelNotFoundException extends DbException +{ + protected $model; + + /** + * 构造方法 + * @param string $message + * @param string $model + */ + public function __construct($message, $model = '', array $config = []) + { + $this->message = $message; + $this->model = $model; + + $this->setData('Database Config', $config); + } + + /** + * 获取模型类名 + * @access public + * @return string + */ + public function getModel() + { + return $this->model; + } + +} diff --git a/source/thinkphp/library/think/debug/Console.php b/source/thinkphp/library/think/debug/Console.php new file mode 100644 index 0000000..c17911b --- /dev/null +++ b/source/thinkphp/library/think/debug/Console.php @@ -0,0 +1,160 @@ + +// +---------------------------------------------------------------------- + +namespace think\debug; + +use think\Cache; +use think\Config; +use think\Db; +use think\Debug; +use think\Request; +use think\Response; + +/** + * 浏览器调试输出 + */ +class Console +{ + protected $config = [ + 'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], + ]; + + // 实例化并传入参数 + public function __construct($config = []) + { + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } + } + + /** + * 调试输出接口 + * @access public + * @param Response $response Response对象 + * @param array $log 日志信息 + * @return bool + */ + public function output(Response $response, array $log = []) + { + $request = Request::instance(); + $contentType = $response->getHeader('Content-Type'); + $accept = $request->header('accept'); + if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { + return false; + } elseif (!empty($contentType) && strpos($contentType, 'html') === false) { + return false; + } + // 获取基本信息 + $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + if (isset($_SERVER['HTTP_HOST'])) { + $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $uri = 'cmd:' . implode(' ', $_SERVER['argv']); + } + + // 页面Trace信息 + $base = [ + '请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri, + '运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()), + '查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ', + '缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes', + '配置加载' => count(Config::get()), + ]; + + if (session_id()) { + $base['会话信息'] = 'SESSION_ID=' . session_id(); + } + + $info = Debug::getFile(true); + + // 页面Trace信息 + $trace = []; + foreach ($this->config['trace_tabs'] as $name => $title) { + $name = strtolower($name); + switch ($name) { + case 'base': // 基本信息 + $trace[$title] = $base; + break; + case 'file': // 文件信息 + $trace[$title] = $info; + break; + default: // 调试信息 + if (strpos($name, '|')) { + // 多组信息 + $names = explode('|', $name); + $result = []; + foreach ($names as $name) { + $result = array_merge($result, isset($log[$name]) ? $log[$name] : []); + } + $trace[$title] = $result; + } else { + $trace[$title] = isset($log[$name]) ? $log[$name] : ''; + } + } + } + + //输出到控制台 + $lines = ''; + foreach ($trace as $type => $msg) { + $lines .= $this->console($type, $msg); + } + $js = << +{$lines} + +JS; + return $js; + } + + protected function console($type, $msg) + { + $type = strtolower($type); + $trace_tabs = array_values($this->config['trace_tabs']); + $line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type) + ? "console.group('{$type}');" + : "console.groupCollapsed('{$type}');"; + + foreach ((array) $msg as $key => $m) { + switch ($type) { + case '调试': + $var_type = gettype($m); + if (in_array($var_type, ['array', 'string'])) { + $line[] = "console.log(" . json_encode($m) . ");"; + } else { + $line[] = "console.log(" . json_encode(var_export($m, 1)) . ");"; + } + break; + case '错误': + $msg = str_replace("\n", '\n', json_encode($m)); + $style = 'color:#F4006B;font-size:14px;'; + $line[] = "console.error(\"%c{$msg}\", \"{$style}\");"; + break; + case 'sql': + $msg = str_replace("\n", '\n', $m); + $style = "color:#009bb4;"; + $line[] = "console.log(\"%c{$msg}\", \"{$style}\");"; + break; + default: + $m = is_string($key) ? $key . ' ' . $m : $key + 1 . ' ' . $m; + $msg = json_encode($m); + $line[] = "console.log({$msg});"; + break; + } + } + $line[] = "console.groupEnd();"; + return implode(PHP_EOL, $line); + } + +} diff --git a/source/thinkphp/library/think/debug/Html.php b/source/thinkphp/library/think/debug/Html.php new file mode 100644 index 0000000..b6be7ad --- /dev/null +++ b/source/thinkphp/library/think/debug/Html.php @@ -0,0 +1,111 @@ + +// +---------------------------------------------------------------------- + +namespace think\debug; + +use think\Cache; +use think\Config; +use think\Db; +use think\Debug; +use think\Request; +use think\Response; + +/** + * 页面Trace调试 + */ +class Html +{ + protected $config = [ + 'trace_file' => '', + 'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'], + ]; + + // 实例化并传入参数 + public function __construct(array $config = []) + { + $this->config['trace_file'] = THINK_PATH . 'tpl/page_trace.tpl'; + $this->config = array_merge($this->config, $config); + } + + /** + * 调试输出接口 + * @access public + * @param Response $response Response对象 + * @param array $log 日志信息 + * @return bool + */ + public function output(Response $response, array $log = []) + { + $request = Request::instance(); + $contentType = $response->getHeader('Content-Type'); + $accept = $request->header('accept'); + if (strpos($accept, 'application/json') === 0 || $request->isAjax()) { + return false; + } elseif (!empty($contentType) && strpos($contentType, 'html') === false) { + return false; + } + // 获取基本信息 + $runtime = number_format(microtime(true) - THINK_START_TIME, 10, '.', ''); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + // 页面Trace信息 + if (isset($_SERVER['HTTP_HOST'])) { + $uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $uri = 'cmd:' . implode(' ', $_SERVER['argv']); + } + $base = [ + '请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri, + '运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()), + '查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ', + '缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes', + '配置加载' => count(Config::get()), + ]; + + if (session_id()) { + $base['会话信息'] = 'SESSION_ID=' . session_id(); + } + + $info = Debug::getFile(true); + + // 页面Trace信息 + $trace = []; + foreach ($this->config['trace_tabs'] as $name => $title) { + $name = strtolower($name); + switch ($name) { + case 'base': // 基本信息 + $trace[$title] = $base; + break; + case 'file': // 文件信息 + $trace[$title] = $info; + break; + default: // 调试信息 + if (strpos($name, '|')) { + // 多组信息 + $names = explode('|', $name); + $result = []; + foreach ($names as $name) { + $result = array_merge($result, isset($log[$name]) ? $log[$name] : []); + } + $trace[$title] = $result; + } else { + $trace[$title] = isset($log[$name]) ? $log[$name] : ''; + } + } + } + // 调用Trace页面模板 + ob_start(); + include $this->config['trace_file']; + return ob_get_clean(); + } + +} diff --git a/source/thinkphp/library/think/exception/ClassNotFoundException.php b/source/thinkphp/library/think/exception/ClassNotFoundException.php new file mode 100644 index 0000000..eb22e73 --- /dev/null +++ b/source/thinkphp/library/think/exception/ClassNotFoundException.php @@ -0,0 +1,32 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class ClassNotFoundException extends \RuntimeException +{ + protected $class; + public function __construct($message, $class = '') + { + $this->message = $message; + $this->class = $class; + } + + /** + * 获取类名 + * @access public + * @return string + */ + public function getClass() + { + return $this->class; + } +} diff --git a/source/thinkphp/library/think/exception/DbException.php b/source/thinkphp/library/think/exception/DbException.php new file mode 100644 index 0000000..0ae80ad --- /dev/null +++ b/source/thinkphp/library/think/exception/DbException.php @@ -0,0 +1,43 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +use think\Exception; + +/** + * Database相关异常处理类 + */ +class DbException extends Exception +{ + /** + * DbException constructor. + * @param string $message + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct($message, array $config, $sql, $code = 10500) + { + $this->message = $message; + $this->code = $code; + + $this->setData('Database Status', [ + 'Error Code' => $code, + 'Error Message' => $message, + 'Error SQL' => $sql, + ]); + + unset($config['username'], $config['password']); + $this->setData('Database Config', $config); + } + +} diff --git a/source/thinkphp/library/think/exception/ErrorException.php b/source/thinkphp/library/think/exception/ErrorException.php new file mode 100644 index 0000000..b3a9a30 --- /dev/null +++ b/source/thinkphp/library/think/exception/ErrorException.php @@ -0,0 +1,57 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +use think\Exception; + +/** + * ThinkPHP错误异常 + * 主要用于封装 set_error_handler 和 register_shutdown_function 得到的错误 + * 除开从 think\Exception 继承的功能 + * 其他和PHP系统\ErrorException功能基本一样 + */ +class ErrorException extends Exception +{ + /** + * 用于保存错误级别 + * @var integer + */ + protected $severity; + + /** + * 错误异常构造函数 + * @param integer $severity 错误级别 + * @param string $message 错误详细信息 + * @param string $file 出错文件路径 + * @param integer $line 出错行号 + * @param array $context 错误上下文,会包含错误触发处作用域内所有变量的数组 + */ + public function __construct($severity, $message, $file, $line, array $context = []) + { + $this->severity = $severity; + $this->message = $message; + $this->file = $file; + $this->line = $line; + $this->code = 0; + + empty($context) || $this->setData('Error Context', $context); + } + + /** + * 获取错误级别 + * @return integer 错误级别 + */ + final public function getSeverity() + { + return $this->severity; + } +} diff --git a/source/thinkphp/library/think/exception/Handle.php b/source/thinkphp/library/think/exception/Handle.php new file mode 100644 index 0000000..f523db0 --- /dev/null +++ b/source/thinkphp/library/think/exception/Handle.php @@ -0,0 +1,282 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +use Exception; +use think\App; +use think\Config; +use think\console\Output; +use think\Lang; +use think\Log; +use think\Response; + +class Handle +{ + protected $render; + protected $ignoreReport = [ + '\\think\\exception\\HttpException', + ]; + + public function setRender($render) + { + $this->render = $render; + } + + /** + * Report or log an exception. + * + * @param \Exception $exception + * @return void + */ + public function report(Exception $exception) + { + if (!$this->isIgnoreReport($exception)) { + // 收集异常数据 + if (App::$debug) { + $data = [ + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'message' => $this->getMessage($exception), + 'code' => $this->getCode($exception), + ]; + $log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]"; + } else { + $data = [ + 'code' => $this->getCode($exception), + 'message' => $this->getMessage($exception), + ]; + $log = "[{$data['code']}]{$data['message']}"; + } + + if (Config::get('record_trace')) { + $log .= "\r\n" . $exception->getTraceAsString(); + } + + Log::record($log, 'error'); + } + } + + protected function isIgnoreReport(Exception $exception) + { + foreach ($this->ignoreReport as $class) { + if ($exception instanceof $class) { + return true; + } + } + return false; + } + + /** + * Render an exception into an HTTP response. + * + * @param \Exception $e + * @return Response + */ + public function render(Exception $e) + { + if ($this->render && $this->render instanceof \Closure) { + $result = call_user_func_array($this->render, [$e]); + if ($result) { + return $result; + } + } + + if ($e instanceof HttpException) { + return $this->renderHttpException($e); + } else { + return $this->convertExceptionToResponse($e); + } + } + + /** + * @param Output $output + * @param Exception $e + */ + public function renderForConsole(Output $output, Exception $e) + { + if (App::$debug) { + $output->setVerbosity(Output::VERBOSITY_DEBUG); + } + $output->renderException($e); + } + + /** + * @param HttpException $e + * @return Response + */ + protected function renderHttpException(HttpException $e) + { + $status = $e->getStatusCode(); + $template = Config::get('http_exception_template'); + if (!App::$debug && !empty($template[$status])) { + return Response::create($template[$status], 'view', $status)->assign(['e' => $e]); + } else { + return $this->convertExceptionToResponse($e); + } + } + + /** + * @param Exception $exception + * @return Response + */ + protected function convertExceptionToResponse(Exception $exception) + { + // 收集异常数据 + if (App::$debug) { + // 调试模式,获取详细的错误信息 + $data = [ + 'name' => get_class($exception), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'message' => $this->getMessage($exception), + 'trace' => $exception->getTrace(), + 'code' => $this->getCode($exception), + 'source' => $this->getSourceCode($exception), + 'datas' => $this->getExtendData($exception), + 'tables' => [ + 'GET Data' => $_GET, + 'POST Data' => $_POST, + 'Files' => $_FILES, + 'Cookies' => $_COOKIE, + 'Session' => isset($_SESSION) ? $_SESSION : [], + 'Server/Request Data' => $_SERVER, + 'Environment Variables' => $_ENV, + 'ThinkPHP Constants' => $this->getConst(), + ], + ]; + } else { + // 部署模式仅显示 Code 和 Message + $data = [ + 'code' => $this->getCode($exception), + 'message' => $this->getMessage($exception), + ]; + + if (!Config::get('show_error_msg')) { + // 不显示详细错误信息 + $data['message'] = Config::get('error_message'); + } + } + + //保留一层 + while (ob_get_level() > 1) { + ob_end_clean(); + } + + $data['echo'] = ob_get_clean(); + + ob_start(); + extract($data); + include Config::get('exception_tmpl'); + // 获取并清空缓存 + $content = ob_get_clean(); + $response = new Response($content, 'html'); + + if ($exception instanceof HttpException) { + $statusCode = $exception->getStatusCode(); + $response->header($exception->getHeaders()); + } + + if (!isset($statusCode)) { + $statusCode = 500; + } + $response->code($statusCode); + return $response; + } + + /** + * 获取错误编码 + * ErrorException则使用错误级别作为错误编码 + * @param \Exception $exception + * @return integer 错误编码 + */ + protected function getCode(Exception $exception) + { + $code = $exception->getCode(); + if (!$code && $exception instanceof ErrorException) { + $code = $exception->getSeverity(); + } + return $code; + } + + /** + * 获取错误信息 + * ErrorException则使用错误级别作为错误编码 + * @param \Exception $exception + * @return string 错误信息 + */ + protected function getMessage(Exception $exception) + { + $message = $exception->getMessage(); + if (IS_CLI) { + return $message; + } + + if (strpos($message, ':')) { + $name = strstr($message, ':', true); + $message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message; + } elseif (strpos($message, ',')) { + $name = strstr($message, ',', true); + $message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message; + } elseif (Lang::has($message)) { + $message = Lang::get($message); + } + return $message; + } + + /** + * 获取出错文件内容 + * 获取错误的前9行和后9行 + * @param \Exception $exception + * @return array 错误文件内容 + */ + protected function getSourceCode(Exception $exception) + { + // 读取前9行和后9行 + $line = $exception->getLine(); + $first = ($line - 9 > 0) ? $line - 9 : 1; + + try { + $contents = file($exception->getFile()); + $source = [ + 'first' => $first, + 'source' => array_slice($contents, $first - 1, 19), + ]; + } catch (Exception $e) { + $source = []; + } + return $source; + } + + /** + * 获取异常扩展信息 + * 用于非调试模式html返回类型显示 + * @param \Exception $exception + * @return array 异常类定义的扩展数据 + */ + protected function getExtendData(Exception $exception) + { + $data = []; + if ($exception instanceof \think\Exception) { + $data = $exception->getData(); + } + return $data; + } + + /** + * 获取常量列表 + * @return array 常量列表 + */ + private static function getConst() + { + return get_defined_constants(true)['user']; + } +} diff --git a/source/thinkphp/library/think/exception/HttpException.php b/source/thinkphp/library/think/exception/HttpException.php new file mode 100644 index 0000000..01a27fc --- /dev/null +++ b/source/thinkphp/library/think/exception/HttpException.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class HttpException extends \RuntimeException +{ + private $statusCode; + private $headers; + + public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = [], $code = 0) + { + $this->statusCode = $statusCode; + $this->headers = $headers; + + parent::__construct($message, $code, $previous); + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/source/thinkphp/library/think/exception/HttpResponseException.php b/source/thinkphp/library/think/exception/HttpResponseException.php new file mode 100644 index 0000000..5297286 --- /dev/null +++ b/source/thinkphp/library/think/exception/HttpResponseException.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +use think\Response; + +class HttpResponseException extends \RuntimeException +{ + /** + * @var Response + */ + protected $response; + + public function __construct(Response $response) + { + $this->response = $response; + } + + public function getResponse() + { + return $this->response; + } + +} diff --git a/source/thinkphp/library/think/exception/PDOException.php b/source/thinkphp/library/think/exception/PDOException.php new file mode 100644 index 0000000..044f82a --- /dev/null +++ b/source/thinkphp/library/think/exception/PDOException.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +/** + * PDO异常处理类 + * 重新封装了系统的\PDOException类 + */ +class PDOException extends DbException +{ + /** + * PDOException constructor. + * @param \PDOException $exception + * @param array $config + * @param string $sql + * @param int $code + */ + public function __construct(\PDOException $exception, array $config, $sql, $code = 10501) + { + $error = $exception->errorInfo; + + $this->setData('PDO Error Info', [ + 'SQLSTATE' => $error[0], + 'Driver Error Code' => isset($error[1]) ? $error[1] : 0, + 'Driver Error Message' => isset($error[2]) ? $error[2] : '', + ]); + + parent::__construct($exception->getMessage(), $config, $sql, $code); + } +} diff --git a/source/thinkphp/library/think/exception/RouteNotFoundException.php b/source/thinkphp/library/think/exception/RouteNotFoundException.php new file mode 100644 index 0000000..d22e3a6 --- /dev/null +++ b/source/thinkphp/library/think/exception/RouteNotFoundException.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class RouteNotFoundException extends HttpException +{ + + public function __construct() + { + parent::__construct(404, 'Route Not Found'); + } + +} diff --git a/source/thinkphp/library/think/exception/TemplateNotFoundException.php b/source/thinkphp/library/think/exception/TemplateNotFoundException.php new file mode 100644 index 0000000..4202069 --- /dev/null +++ b/source/thinkphp/library/think/exception/TemplateNotFoundException.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class TemplateNotFoundException extends \RuntimeException +{ + protected $template; + + public function __construct($message, $template = '') + { + $this->message = $message; + $this->template = $template; + } + + /** + * 获取模板文件 + * @access public + * @return string + */ + public function getTemplate() + { + return $this->template; + } +} diff --git a/source/thinkphp/library/think/exception/ThrowableError.php b/source/thinkphp/library/think/exception/ThrowableError.php new file mode 100644 index 0000000..87b6b9d --- /dev/null +++ b/source/thinkphp/library/think/exception/ThrowableError.php @@ -0,0 +1,47 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class ThrowableError extends \ErrorException +{ + public function __construct(\Throwable $e) + { + + if ($e instanceof \ParseError) { + $message = 'Parse error: ' . $e->getMessage(); + $severity = E_PARSE; + } elseif ($e instanceof \TypeError) { + $message = 'Type error: ' . $e->getMessage(); + $severity = E_RECOVERABLE_ERROR; + } else { + $message = 'Fatal error: ' . $e->getMessage(); + $severity = E_ERROR; + } + + parent::__construct( + $message, + $e->getCode(), + $severity, + $e->getFile(), + $e->getLine() + ); + + $this->setTrace($e->getTrace()); + } + + protected function setTrace($trace) + { + $traceReflector = new \ReflectionProperty('Exception', 'trace'); + $traceReflector->setAccessible(true); + $traceReflector->setValue($this, $trace); + } +} diff --git a/source/thinkphp/library/think/exception/ValidateException.php b/source/thinkphp/library/think/exception/ValidateException.php new file mode 100644 index 0000000..b368416 --- /dev/null +++ b/source/thinkphp/library/think/exception/ValidateException.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- + +namespace think\exception; + +class ValidateException extends \RuntimeException +{ + protected $error; + + public function __construct($error) + { + $this->error = $error; + $this->message = is_array($error) ? implode("\n\r", $error) : $error; + } + + /** + * 获取验证错误信息 + * @access public + * @return array|string + */ + public function getError() + { + return $this->error; + } +} diff --git a/source/thinkphp/library/think/log/driver/File.php b/source/thinkphp/library/think/log/driver/File.php new file mode 100644 index 0000000..f2296cf --- /dev/null +++ b/source/thinkphp/library/think/log/driver/File.php @@ -0,0 +1,270 @@ + +// +---------------------------------------------------------------------- + +namespace think\log\driver; + +use think\App; +use think\Request; + +/** + * 本地化调试输出到文件 + */ +class File +{ + protected $config = [ + 'time_format' => ' c ', + 'single' => false, + 'file_size' => 2097152, + 'path' => LOG_PATH, + 'apart_level' => [], + 'max_files' => 0, + 'json' => false, + ]; + + // 实例化并传入参数 + public function __construct($config = []) + { + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } + } + + /** + * 日志写入接口 + * @access public + * @param array $log 日志信息 + * @param bool $append 是否追加请求信息 + * @return bool + */ + public function save(array $log = [], $append = false) + { + $destination = $this->getMasterLogFile(); + + $path = dirname($destination); + !is_dir($path) && mkdir($path, 0755, true); + + $info = []; + foreach ($log as $type => $val) { + + foreach ($val as $msg) { + if (!is_string($msg)) { + $msg = var_export($msg, true); + } + + $info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg; + } + + if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) { + // 独立记录的日志级别 + $filename = $this->getApartLevelFile($path, $type); + + $this->write($info[$type], $filename, true, $append); + unset($info[$type]); + } + } + + if ($info) { + return $this->write($info, $destination, false, $append); + } + + return true; + } + + /** + * 获取主日志文件名 + * @access public + * @return string + */ + protected function getMasterLogFile() + { + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + + $destination = $this->config['path'] . $name . '.log'; + } else { + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + + if ($this->config['max_files']) { + $filename = date('Ymd') . $cli . '.log'; + $files = glob($this->config['path'] . '*.log'); + + try { + if (count($files) > $this->config['max_files']) { + unlink($files[0]); + } + } catch (\Exception $e) { + } + } else { + $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log'; + } + + $destination = $this->config['path'] . $filename; + } + + return $destination; + } + + /** + * 获取独立日志文件名 + * @access public + * @param string $path 日志目录 + * @param string $type 日志类型 + * @return string + */ + protected function getApartLevelFile($path, $type) + { + $cli = PHP_SAPI == 'cli' ? '_cli' : ''; + + if ($this->config['single']) { + $name = is_string($this->config['single']) ? $this->config['single'] : 'single'; + + $name .= '_' . $type; + } elseif ($this->config['max_files']) { + $name = date('Ymd') . '_' . $type . $cli; + } else { + $name = date('d') . '_' . $type . $cli; + } + + return $path . DIRECTORY_SEPARATOR . $name . '.log'; + } + + /** + * 日志写入 + * @access protected + * @param array $message 日志信息 + * @param string $destination 日志文件 + * @param bool $apart 是否独立文件写入 + * @param bool $append 是否追加请求信息 + * @return bool + */ + protected function write($message, $destination, $apart = false, $append = false) + { + // 检测日志文件大小,超过配置大小则备份日志文件重新生成 + $this->checkLogSize($destination); + + // 日志信息封装 + $info['timestamp'] = date($this->config['time_format']); + + foreach ($message as $type => $msg) { + $info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg; + } + + if (PHP_SAPI == 'cli') { + $message = $this->parseCliLog($info); + } else { + // 添加调试日志 + $this->getDebugLog($info, $append, $apart); + + $message = $this->parseLog($info); + } + + return error_log($message, 3, $destination); + } + + /** + * 检查日志文件大小并自动生成备份文件 + * @access protected + * @param string $destination 日志文件 + * @return void + */ + protected function checkLogSize($destination) + { + if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { + try { + rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination)); + } catch (\Exception $e) { + } + } + } + + /** + * CLI日志解析 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseCliLog($info) + { + if ($this->config['json']) { + $message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } else { + $now = $info['timestamp']; + unset($info['timestamp']); + + $message = implode("\r\n", $info); + + $message = "[{$now}]" . $message . "\r\n"; + } + + return $message; + } + + /** + * 解析日志 + * @access protected + * @param array $info 日志信息 + * @return string + */ + protected function parseLog($info) + { + $request = Request::instance(); + $requestInfo = [ + 'ip' => $request->ip(), + 'method' => $request->method(), + 'host' => $request->host(), + 'uri' => $request->url(), + ]; + + if ($this->config['json']) { + $info = $requestInfo + $info; + return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n"; + } + + array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}"); + unset($info['timestamp']); + + return implode("\r\n", $info) . "\r\n"; + } + + protected function getDebugLog(&$info, $append, $apart) + { + if (App::$debug && $append) { + + if ($this->config['json']) { + // 获取基本信息 + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + $info = [ + 'runtime' => number_format($runtime, 6) . 's', + 'reqs' => $reqs . 'req/s', + 'memory' => $memory_use . 'kb', + 'file' => count(get_included_files()), + ] + $info; + + } elseif (!$apart) { + // 增加额外的调试信息 + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + + $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + + $time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]'; + $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + + array_unshift($info, $time_str . $memory_str . $file_load); + } + } + } +} diff --git a/source/thinkphp/library/think/log/driver/Socket.php b/source/thinkphp/library/think/log/driver/Socket.php new file mode 100644 index 0000000..4f62915 --- /dev/null +++ b/source/thinkphp/library/think/log/driver/Socket.php @@ -0,0 +1,250 @@ + +// +---------------------------------------------------------------------- + +namespace think\log\driver; + +use think\App; + +/** + * github: https://github.com/luofei614/SocketLog + * @author luofei614 + */ +class Socket +{ + public $port = 1116; //SocketLog 服务的http的端口号 + + protected $config = [ + // socket服务器地址 + 'host' => 'localhost', + // 是否显示加载的文件列表 + 'show_included_files' => false, + // 日志强制记录到配置的client_id + 'force_client_ids' => [], + // 限制允许读取日志的client_id + 'allow_client_ids' => [], + ]; + + protected $css = [ + 'sql' => 'color:#009bb4;', + 'sql_warn' => 'color:#009bb4;font-size:14px;', + 'error' => 'color:#f4006b;font-size:14px;', + 'page' => 'color:#40e2ff;background:#171717;', + 'big' => 'font-size:20px;color:red;', + ]; + + protected $allowForceClientIds = []; //配置强制推送且被授权的client_id + + /** + * 构造函数 + * @param array $config 缓存参数 + * @access public + */ + public function __construct(array $config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + } + + /** + * 调试输出接口 + * @access public + * @param array $log 日志信息 + * @return bool + */ + public function save(array $log = [], $append = false) + { + if (!$this->check()) { + return false; + } + $trace = []; + if (App::$debug) { + $runtime = round(microtime(true) - THINK_START_TIME, 10); + $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; + $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]'; + $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); + $memory_str = ' [内存消耗:' . $memory_use . 'kb]'; + $file_load = ' [文件加载:' . count(get_included_files()) . ']'; + + if (isset($_SERVER['HTTP_HOST'])) { + $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $current_uri = 'cmd:' . implode(' ', $_SERVER['argv']); + } + // 基本信息 + $trace[] = [ + 'type' => 'group', + 'msg' => $current_uri . $time_str . $memory_str . $file_load, + 'css' => $this->css['page'], + ]; + } + + foreach ($log as $type => $val) { + $trace[] = [ + 'type' => 'groupCollapsed', + 'msg' => '[ ' . $type . ' ]', + 'css' => isset($this->css[$type]) ? $this->css[$type] : '', + ]; + foreach ($val as $msg) { + if (!is_string($msg)) { + $msg = var_export($msg, true); + } + $trace[] = [ + 'type' => 'log', + 'msg' => $msg, + 'css' => '', + ]; + } + $trace[] = [ + 'type' => 'groupEnd', + 'msg' => '', + 'css' => '', + ]; + } + + if ($this->config['show_included_files']) { + $trace[] = [ + 'type' => 'groupCollapsed', + 'msg' => '[ file ]', + 'css' => '', + ]; + $trace[] = [ + 'type' => 'log', + 'msg' => implode("\n", get_included_files()), + 'css' => '', + ]; + $trace[] = [ + 'type' => 'groupEnd', + 'msg' => '', + 'css' => '', + ]; + } + + $trace[] = [ + 'type' => 'groupEnd', + 'msg' => '', + 'css' => '', + ]; + + $tabid = $this->getClientArg('tabid'); + if (!$client_id = $this->getClientArg('client_id')) { + $client_id = ''; + } + + if (!empty($this->allowForceClientIds)) { + //强制推送到多个client_id + foreach ($this->allowForceClientIds as $force_client_id) { + $client_id = $force_client_id; + $this->sendToClient($tabid, $client_id, $trace, $force_client_id); + } + } else { + $this->sendToClient($tabid, $client_id, $trace, ''); + } + return true; + } + + /** + * 发送给指定客户端 + * @author Zjmainstay + * @param $tabid + * @param $client_id + * @param $logs + * @param $force_client_id + */ + protected function sendToClient($tabid, $client_id, $logs, $force_client_id) + { + $logs = [ + 'tabid' => $tabid, + 'client_id' => $client_id, + 'logs' => $logs, + 'force_client_id' => $force_client_id, + ]; + $msg = @json_encode($logs); + $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁 + $this->send($this->config['host'], $msg, $address); + } + + protected function check() + { + $tabid = $this->getClientArg('tabid'); + //是否记录日志的检查 + if (!$tabid && !$this->config['force_client_ids']) { + return false; + } + //用户认证 + $allow_client_ids = $this->config['allow_client_ids']; + if (!empty($allow_client_ids)) { + //通过数组交集得出授权强制推送的client_id + $this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']); + if (!$tabid && count($this->allowForceClientIds)) { + return true; + } + + $client_id = $this->getClientArg('client_id'); + if (!in_array($client_id, $allow_client_ids)) { + return false; + } + } else { + $this->allowForceClientIds = $this->config['force_client_ids']; + } + return true; + } + + protected function getClientArg($name) + { + static $args = []; + + $key = 'HTTP_USER_AGENT'; + + if (isset($_SERVER['HTTP_SOCKETLOG'])) { + $key = 'HTTP_SOCKETLOG'; + } + + if (!isset($_SERVER[$key])) { + return; + } + if (empty($args)) { + if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) { + $args = ['tabid' => null]; + return; + } + parse_str($match[1], $args); + } + if (isset($args[$name])) { + return $args[$name]; + } + return; + } + + /** + * @param string $host - $host of socket server + * @param string $message - 发送的消息 + * @param string $address - 地址 + * @return bool + */ + protected function send($host, $message = '', $address = '/') + { + $url = 'http://' . $host . ':' . $this->port . $address; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $message); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + $headers = [ + "Content-Type: application/json;charset=UTF-8", + ]; + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header + return curl_exec($ch); + } + +} diff --git a/source/thinkphp/library/think/log/driver/Test.php b/source/thinkphp/library/think/log/driver/Test.php new file mode 100644 index 0000000..7f66338 --- /dev/null +++ b/source/thinkphp/library/think/log/driver/Test.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- + +namespace think\log\driver; + +/** + * 模拟测试输出 + */ +class Test +{ + /** + * 日志写入接口 + * @access public + * @param array $log 日志信息 + * @return bool + */ + public function save(array $log = []) + { + return true; + } + +} diff --git a/source/thinkphp/library/think/model/Collection.php b/source/thinkphp/library/think/model/Collection.php new file mode 100644 index 0000000..0406533 --- /dev/null +++ b/source/thinkphp/library/think/model/Collection.php @@ -0,0 +1,79 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\Collection as BaseCollection; +use think\Model; + +class Collection extends BaseCollection +{ + /** + * 延迟预载入关联查询 + * @access public + * @param mixed $relation 关联 + * @return $this + */ + public function load($relation) + { + $item = current($this->items); + $item->eagerlyResultSet($this->items, $relation); + return $this; + } + + /** + * 设置需要隐藏的输出属性 + * @access public + * @param array $hidden 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function hidden($hidden = [], $override = false) + { + $this->each(function ($model) use ($hidden, $override) { + /** @var Model $model */ + $model->hidden($hidden, $override); + }); + return $this; + } + + /** + * 设置需要输出的属性 + * @param array $visible + * @param bool $override 是否覆盖 + * @return $this + */ + public function visible($visible = [], $override = false) + { + $this->each(function ($model) use ($visible, $override) { + /** @var Model $model */ + $model->visible($visible, $override); + }); + return $this; + } + + /** + * 设置需要追加的输出属性 + * @access public + * @param array $append 属性列表 + * @param bool $override 是否覆盖 + * @return $this + */ + public function append($append = [], $override = false) + { + $this->each(function ($model) use ($append, $override) { + /** @var Model $model */ + $model && $model->append($append, $override); + }); + return $this; + } + +} diff --git a/source/thinkphp/library/think/model/Merge.php b/source/thinkphp/library/think/model/Merge.php new file mode 100644 index 0000000..4a9da81 --- /dev/null +++ b/source/thinkphp/library/think/model/Merge.php @@ -0,0 +1,322 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\Db; +use think\db\Query; +use think\Model; + +class Merge extends Model +{ + + protected $relationModel = []; // HAS ONE 关联的模型列表 + protected $fk = ''; // 外键名 默认为主表名_id + protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' ) + + /** + * 构造函数 + * @access public + * @param array|object $data 数据 + */ + public function __construct($data = []) + { + parent::__construct($data); + + // 设置默认外键名 仅支持单一外键 + if (empty($this->fk)) { + $this->fk = strtolower($this->name) . '_id'; + } + } + + /** + * 查找单条记录 + * @access public + * @param mixed $data 主键值或者查询条件(闭包) + * @param string|array $with 关联预查询 + * @param bool $cache 是否缓存 + * @return \think\Model + */ + public static function get($data = null, $with = [], $cache = false) + { + $query = self::parseQuery($data, $with, $cache); + $query = self::attachQuery($query); + return $query->find($data); + } + + /** + * 附加查询表达式 + * @access protected + * @param \think\db\Query $query 查询对象 + * @return \think\db\Query + */ + protected static function attachQuery($query) + { + $class = new static(); + $master = $class->name; + $fields = self::getModelField($query, $master, '', $class->mapFields, $class->field); + $query->alias($master)->field($fields); + + foreach ($class->relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? $query->getTable($name) : $model; + $query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk()); + $fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field); + $query->field($fields); + } + return $query; + } + + /** + * 获取关联模型的字段 并解决混淆 + * @access protected + * @param \think\db\Query $query 查询对象 + * @param string $name 模型名称 + * @param string $table 关联表名称 + * @param array $map 字段映射 + * @param array $fields 查询字段 + * @return array + */ + protected static function getModelField($query, $name, $table = '', $map = [], $fields = []) + { + // 获取模型的字段信息 + $fields = $fields ?: $query->getTableInfo($table, 'fields'); + $array = []; + foreach ($fields as $field) { + if ($key = array_search($name . '.' . $field, $map)) { + // 需要处理映射字段 + $array[] = $name . '.' . $field . ' AS ' . $key; + } else { + $array[] = $field; + } + } + return $array; + } + + /** + * 查找所有记录 + * @access public + * @param mixed $data 主键列表或者查询条件(闭包) + * @param array|string $with 关联预查询 + * @param bool $cache + * @return array|false|string + */ + public static function all($data = null, $with = [], $cache = false) + { + $query = self::parseQuery($data, $with, $cache); + $query = self::attachQuery($query); + return $query->select($data); + } + + /** + * 处理写入的模型数据 + * @access public + * @param string $model 模型名称 + * @param array $data 数据 + * @return array + */ + protected function parseData($model, $data) + { + $item = []; + foreach ($data as $key => $val) { + if ($this->fk != $key && array_key_exists($key, $this->mapFields)) { + list($name, $key) = explode('.', $this->mapFields[$key]); + if ($model == $name) { + $item[$key] = $val; + } + } else { + $item[$key] = $val; + } + } + return $item; + } + + /** + * 保存模型数据 以及关联数据 + * @access public + * @param mixed $data 数据 + * @param array $where 更新条件 + * @param string $sequence 自增序列名 + * @return false|int + * @throws \Exception + */ + public function save($data = [], $where = [], $sequence = null) + { + if (!empty($data)) { + // 数据自动验证 + if (!$this->validateData($data)) { + return false; + } + // 数据对象赋值 + foreach ($data as $key => $value) { + $this->setAttr($key, $value, $data); + } + if (!empty($where)) { + $this->isUpdate = true; + } + } + + // 数据自动完成 + $this->autoCompleteData($this->auto); + + // 自动写入更新时间 + if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) { + $this->setAttr($this->updateTime, null); + } + + // 事件回调 + if (false === $this->trigger('before_write', $this)) { + return false; + } + + $db = $this->db(); + $db->startTrans(); + $pk = $this->getPk(); + try { + if ($this->isUpdate) { + // 自动写入 + $this->autoCompleteData($this->update); + + if (false === $this->trigger('before_update', $this)) { + return false; + } + + if (empty($where) && !empty($this->updateWhere)) { + $where = $this->updateWhere; + } + + // 获取有更新的数据 + $data = $this->getChangedData(); + // 保留主键数据 + foreach ($this->data as $key => $val) { + if ($this->isPk($key)) { + $data[$key] = $val; + } + } + // 处理模型数据 + $data = $this->parseData($this->name, $data); + if (is_string($pk) && isset($data[$pk])) { + if (!isset($where[$pk])) { + unset($where); + $where[$pk] = $data[$pk]; + } + unset($data[$pk]); + } + // 写入主表数据 + $result = $db->strict(false)->where($where)->update($data); + + // 写入附表数据 + foreach ($this->relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? $db->getTable($model) : $model; + // 处理关联模型数据 + $data = $this->parseData($name, $data); + if (Db::table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) { + $result = 1; + } + } + + // 新增回调 + $this->trigger('after_update', $this); + } else { + // 自动写入 + $this->autoCompleteData($this->insert); + + // 自动写入创建时间 + if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) { + $this->setAttr($this->createTime, null); + } + + if (false === $this->trigger('before_insert', $this)) { + return false; + } + + // 处理模型数据 + $data = $this->parseData($this->name, $this->data); + // 写入主表数据 + $result = $db->name($this->name)->strict(false)->insert($data); + if ($result) { + $insertId = $db->getLastInsID($sequence); + // 写入外键数据 + if ($insertId) { + if (is_string($pk)) { + $this->data[$pk] = $insertId; + } + $this->data[$this->fk] = $insertId; + } + + // 写入附表数据 + $source = $this->data; + if ($insertId && is_string($pk) && isset($source[$pk]) && $this->fk != $pk) { + unset($source[$pk]); + } + foreach ($this->relationModel as $key => $model) { + $name = is_int($key) ? $model : $key; + $table = is_int($key) ? $db->getTable($model) : $model; + // 处理关联模型数据 + $data = $this->parseData($name, $source); + Db::table($table)->strict(false)->insert($data); + } + } + // 标记为更新 + $this->isUpdate = true; + // 新增回调 + $this->trigger('after_insert', $this); + } + $db->commit(); + // 写入回调 + $this->trigger('after_write', $this); + + $this->origin = $this->data; + return $result; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + + /** + * 删除当前的记录 并删除关联数据 + * @access public + * @return int + * @throws \Exception + */ + public function delete() + { + if (false === $this->trigger('before_delete', $this)) { + return false; + } + + $db = $this->db(); + $db->startTrans(); + try { + $result = $db->delete($this->data); + if ($result) { + // 获取主键数据 + $pk = $this->data[$this->getPk()]; + + // 删除关联数据 + foreach ($this->relationModel as $key => $model) { + $table = is_int($key) ? $db->getTable($model) : $model; + $query = new Query; + $query->table($table)->where($this->fk, $pk)->delete(); + } + } + $this->trigger('after_delete', $this); + $db->commit(); + return $result; + } catch (\Exception $e) { + $db->rollback(); + throw $e; + } + } + +} diff --git a/source/thinkphp/library/think/model/Pivot.php b/source/thinkphp/library/think/model/Pivot.php new file mode 100644 index 0000000..13525cd --- /dev/null +++ b/source/thinkphp/library/think/model/Pivot.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\Model; + +class Pivot extends Model +{ + + /** @var Model */ + public $parent; + + protected $autoWriteTimestamp = false; + + /** + * 架构函数 + * @access public + * @param array|object $data 数据 + * @param Model $parent 上级模型 + * @param string $table 中间数据表名 + */ + public function __construct($data = [], Model $parent = null, $table = '') + { + $this->parent = $parent; + + if (is_null($this->name)) { + $this->name = $table; + } + + parent::__construct($data); + } + +} diff --git a/source/thinkphp/library/think/model/Relation.php b/source/thinkphp/library/think/model/Relation.php new file mode 100644 index 0000000..25fe88d --- /dev/null +++ b/source/thinkphp/library/think/model/Relation.php @@ -0,0 +1,155 @@ + +// +---------------------------------------------------------------------- + +namespace think\model; + +use think\db\Query; +use think\Exception; +use think\Model; + +/** + * Class Relation + * @package think\model + * + * @mixin Query + */ +abstract class Relation +{ + // 父模型对象 + protected $parent; + /** @var Model 当前关联的模型类 */ + protected $model; + /** @var Query 关联模型查询对象 */ + protected $query; + // 关联表外键 + protected $foreignKey; + // 关联表主键 + protected $localKey; + // 基础查询 + protected $baseQuery; + // 是否为自关联 + protected $selfRelation; + + /** + * 获取关联的所属模型 + * @access public + * @return Model + */ + public function getParent() + { + return $this->parent; + } + + /** + * 获取当前的关联模型对象实例 + * @access public + * @return Model + */ + public function getModel() + { + return $this->query->getModel(); + } + + /** + * 获取关联的查询对象 + * @access public + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * 设置当前关联为自关联 + * @access public + * @param bool $self 是否自关联 + * @return $this + */ + public function selfRelation($self = true) + { + $this->selfRelation = $self; + return $this; + } + + /** + * 当前关联是否为自关联 + * @access public + * @return bool + */ + public function isSelfRelation() + { + return $this->selfRelation; + } + + /** + * 封装关联数据集 + * @access public + * @param array $resultSet 数据集 + * @return mixed + */ + protected function resultSetBuild($resultSet) + { + return (new $this->model)->toCollection($resultSet); + } + + protected function getQueryFields($model) + { + $fields = $this->query->getOptions('field'); + return $this->getRelationQueryFields($fields, $model); + } + + protected function getRelationQueryFields($fields, $model) + { + if ($fields) { + + if (is_string($fields)) { + $fields = explode(',', $fields); + } + + foreach ($fields as &$field) { + if (false === strpos($field, '.')) { + $field = $model . '.' . $field; + } + } + } else { + $fields = $model . '.*'; + } + + return $fields; + } + + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + {} + + public function __call($method, $args) + { + if ($this->query) { + // 执行基础查询 + $this->baseQuery(); + + $result = call_user_func_array([$this->query, $method], $args); + if ($result instanceof Query) { + return $this; + } else { + $this->baseQuery = false; + return $result; + } + } else { + throw new Exception('method not exists:' . __CLASS__ . '->' . $method); + } + } +} diff --git a/source/thinkphp/library/think/model/relation/BelongsTo.php b/source/thinkphp/library/think/model/relation/BelongsTo.php new file mode 100644 index 0000000..c1cbab9 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/BelongsTo.php @@ -0,0 +1,243 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Loader; +use think\Model; + +class BelongsTo extends OneToOne +{ + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 关联主键 + * @param string $joinType JOIN类型 + * @param string $relation 关联名 + */ + public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null) + { + $this->parent = $parent; + $this->model = $model; + $this->foreignKey = $foreignKey; + $this->localKey = $localKey; + $this->joinType = $joinType; + $this->query = (new $model)->db(); + $this->relation = $relation; + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @access public + * @return array|false|\PDOStatement|string|Model + */ + public function getRelation($subRelation = '', $closure = null) + { + $foreignKey = $this->foreignKey; + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $relationModel = $this->query + ->removeWhereField($this->localKey) + ->where($this->localKey, $this->parent->$foreignKey) + ->relation($subRelation) + ->find(); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + + return $relationModel; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*') + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + + if (is_array($where)) { + foreach ($where as $key => $val) { + if (false === strpos($key, '.')) { + $where[$relation . '.' . $key] = $val; + unset($where[$key]); + } + } + } + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db()->alias($model) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType) + ->where($where); + } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$foreignKey)) { + $range[] = $result->$foreignKey; + } + } + + if (!empty($range)) { + $this->query->removeWhereField($localKey); + $data = $this->eagerlyWhere($this->query, [ + $localKey => [ + 'in', + $range, + ], + ], $localKey, $relation, $subRelation, $closure); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + // 关联模型 + if (!isset($data[$result->$foreignKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$foreignKey]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + // 设置关联属性 + $result->setRelation($attr, $relationModel); + } + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $this->query->removeWhereField($localKey); + $data = $this->eagerlyWhere($this->query, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); + // 关联模型 + if (!isset($data[$result->$foreignKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$foreignKey]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + // 设置关联属性 + $result->setRelation(Loader::parseName($relation), $relationModel); + } + } + + /** + * 添加关联数据 + * @access public + * @param Model $model 关联模型对象 + * @return Model + */ + public function associate($model) + { + $foreignKey = $this->foreignKey; + $pk = $model->getPk(); + + $this->parent->setAttr($foreignKey, $model->$pk); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, $model); + } + + /** + * 注销关联数据 + * @access public + * @return Model + */ + public function dissociate() + { + $foreignKey = $this->foreignKey; + + $this->parent->setAttr($foreignKey, null); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, null); + } + + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery)) { + if (isset($this->parent->{$this->foreignKey})) { + // 关联查询带入关联条件 + $this->query->where($this->localKey, '=', $this->parent->{$this->foreignKey}); + } + + $this->baseQuery = true; + } + } +} diff --git a/source/thinkphp/library/think/model/relation/BelongsToMany.php b/source/thinkphp/library/think/model/relation/BelongsToMany.php new file mode 100644 index 0000000..a41c45c --- /dev/null +++ b/source/thinkphp/library/think/model/relation/BelongsToMany.php @@ -0,0 +1,644 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\Collection; +use think\Db; +use think\db\Query; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Pivot; +use think\model\Relation; +use think\Paginator; + +class BelongsToMany extends Relation +{ + // 中间表表名 + protected $middle; + // 中间表模型名称 + protected $pivotName; + // 中间表模型对象 + protected $pivot; + // 中间表数据名称 + protected $pivotDataName = 'pivot'; + + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $table 中间表名 + * @param string $foreignKey 关联模型外键 + * @param string $localKey 当前模型关联键 + */ + public function __construct(Model $parent, $model, $table, $foreignKey, $localKey) + { + $this->parent = $parent; + $this->model = $model; + $this->foreignKey = $foreignKey; + $this->localKey = $localKey; + if (false !== strpos($table, '\\')) { + $this->pivotName = $table; + $this->middle = basename(str_replace('\\', '/', $table)); + } else { + $this->middle = $table; + } + $this->query = (new $model)->db(); + $this->pivot = $this->newPivot(); + + if ('think\model\Pivot' == get_class($this->pivot)) { + $this->pivot->name($this->middle); + } + } + + /** + * 设置中间表模型 + * @param $pivot + * @return $this + */ + public function pivot($pivot) + { + $this->pivotName = $pivot; + return $this; + } + + /** + * 设置中间表数据名称 + * @access public + * @param string $name + * @return $this + */ + public function pivotDataName($name) + { + $this->pivotDataName = $name; + return $this; + } + + /** + * 获取中间表更新条件 + * @param $data + * @return array + */ + protected function getUpdateWhere($data) + { + return [ + $this->localKey => $data[$this->localKey], + $this->foreignKey => $data[$this->foreignKey], + ]; + } + + /** + * 实例化中间表模型 + * @param array $data + * @param bool $isUpdate + * @return Pivot + * @throws Exception + */ + protected function newPivot($data = [], $isUpdate = false) + { + $class = $this->pivotName ?: '\\think\\model\\Pivot'; + $pivot = new $class($data, $this->parent, $this->middle); + if ($pivot instanceof Pivot) { + return $isUpdate ? $pivot->isUpdate(true, $this->getUpdateWhere($data)) : $pivot; + } else { + throw new Exception('pivot model must extends: \think\model\Pivot'); + } + } + + /** + * 合成中间表模型 + * @param array|Collection|Paginator $models + */ + protected function hydratePivot($models) + { + foreach ($models as $model) { + $pivot = []; + foreach ($model->getData() as $key => $val) { + if (strpos($key, '__')) { + list($name, $attr) = explode('__', $key, 2); + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($model->$key); + } + } + } + $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); + } + } + + /** + * 创建关联查询Query对象 + * @return Query + */ + protected function buildQuery() + { + $foreignKey = $this->foreignKey; + $localKey = $this->localKey; + $pk = $this->parent->getPk(); + // 关联查询 + $condition['pivot.' . $localKey] = $this->parent->$pk; + return $this->belongsToManyQuery($foreignKey, $localKey, $condition); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection + */ + public function getRelation($subRelation = '', $closure = null) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $result = $this->buildQuery()->relation($subRelation)->select(); + $this->hydratePivot($result); + return $result; + } + + /** + * 重载select方法 + * @param null $data + * @return false|\PDOStatement|string|Collection + */ + public function select($data = null) + { + $result = $this->buildQuery()->select($data); + $this->hydratePivot($result); + return $result; + } + + /** + * 重载paginate方法 + * @param null $listRows + * @param bool $simple + * @param array $config + * @return Paginator + */ + public function paginate($listRows = null, $simple = false, $config = []) + { + $result = $this->buildQuery()->paginate($listRows, $simple, $config); + $this->hydratePivot($result); + return $result; + } + + /** + * 重载find方法 + * @param null $data + * @return array|false|\PDOStatement|string|Model + */ + public function find($data = null) + { + $result = $this->buildQuery()->find($data); + if ($result) { + $this->hydratePivot([$result]); + } + return $result; + } + + /** + * 查找多条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return array|\PDOStatement|string|Model + */ + public function selectOrFail($data = null) + { + return $this->failException(true)->select($data); + } + + /** + * 查找单条记录 如果不存在则抛出异常 + * @access public + * @param array|string|Query|\Closure $data + * @return array|\PDOStatement|string|Model + */ + public function findOrFail($data = null) + { + return $this->failException(true)->find($data); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + * @throws Exception + */ + public function hasWhere($where = [], $fields = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 设置中间表的查询条件 + * @param $field + * @param null $op + * @param null $condition + * @return $this + */ + public function wherePivot($field, $op = null, $condition = null) + { + $field = 'pivot.' . $field; + $this->query->where($field, $op, $condition); + return $this; + } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $pk = $resultSet[0]->getPk(); + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$pk)) { + $range[] = $result->$pk; + } + } + + if (!empty($range)) { + // 查询关联数据 + $data = $this->eagerlyManyToMany([ + 'pivot.' . $localKey => [ + 'in', + $range, + ], + ], $relation, $subRelation); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$pk])) { + $data[$result->$pk] = []; + } + + $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); + } + } + } + + /** + * 预载入关联查询(单个数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + $pk = $result->getPk(); + if (isset($result->$pk)) { + $pk = $result->$pk; + // 查询管理数据 + $data = $this->eagerlyManyToMany(['pivot.' . $this->localKey => $pk], $relation, $subRelation); + + // 关联数据封装 + if (!isset($data[$pk])) { + $data[$pk] = []; + } + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk])); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $pk = $result->getPk(); + $count = 0; + if (isset($result->$pk)) { + $pk = $result->$pk; + $count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count(); + } + return $count; + } + + /** + * 获取关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + if ($closure) { + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ + 'pivot.' . $this->localKey => [ + 'exp', + Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), + ], + ])->fetchSql()->count(); + } + + /** + * 多对多 关联模型预查询 + * @access public + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @return array + */ + protected function eagerlyManyToMany($where, $relation, $subRelation = '') + { + // 预载入关联查询 支持嵌套预载入 + $list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)->with($subRelation)->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $pivot = []; + foreach ($set->getData() as $key => $val) { + if (strpos($key, '__')) { + list($name, $attr) = explode('__', $key, 2); + if ('pivot' == $name) { + $pivot[$attr] = $val; + unset($set->$key); + } + } + } + $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true)); + $data[$pivot[$this->localKey]][] = $set; + } + return $data; + } + + /** + * BELONGS TO MANY 关联查询 + * @access public + * @param string $foreignKey 关联模型关联键 + * @param string $localKey 当前模型关联键 + * @param array $condition 关联查询条件 + * @return Query + */ + protected function belongsToManyQuery($foreignKey, $localKey, $condition = []) + { + // 关联查询封装 + $tableName = $this->query->getTable(); + $table = $this->pivot->getTable(); + $fields = $this->getQueryFields($tableName); + + $query = $this->query->field($fields) + ->field(true, false, $table, 'pivot', 'pivot__'); + + if (empty($this->baseQuery)) { + $relationFk = $this->query->getPk(); + $query->join([$table => 'pivot'], 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk) + ->where($condition); + } + return $query; + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @param array $pivot 中间表额外数据 + * @return integer + */ + public function save($data, array $pivot = []) + { + // 保存关联表/中间表数据 + return $this->attach($data, $pivot); + } + + /** + * 批量保存当前关联数据对象 + * @access public + * @param array $dataSet 数据集 + * @param array $pivot 中间表额外数据 + * @param bool $samePivot 额外数据是否相同 + * @return integer + */ + public function saveAll(array $dataSet, array $pivot = [], $samePivot = false) + { + $result = false; + foreach ($dataSet as $key => $data) { + if (!$samePivot) { + $pivotData = isset($pivot[$key]) ? $pivot[$key] : []; + } else { + $pivotData = $pivot; + } + $result = $this->attach($data, $pivotData); + } + return $result; + } + + /** + * 附加关联的一个中间表数据 + * @access public + * @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键 + * @param array $pivot 中间表额外数据 + * @return array|Pivot + * @throws Exception + */ + public function attach($data, $pivot = []) + { + if (is_array($data)) { + if (key($data) === 0) { + $id = $data; + } else { + // 保存关联表数据 + $model = new $this->model; + $model->save($data); + $id = $model->getLastInsID(); + } + } elseif (is_numeric($data) || is_string($data)) { + // 根据关联表主键直接写入中间表 + $id = $data; + } elseif ($data instanceof Model) { + // 根据关联表主键直接写入中间表 + $relationFk = $data->getPk(); + $id = $data->$relationFk; + } + + if ($id) { + // 保存中间表数据 + $pk = $this->parent->getPk(); + $pivot[$this->localKey] = $this->parent->$pk; + $ids = (array) $id; + foreach ($ids as $id) { + $pivot[$this->foreignKey] = $id; + $this->pivot->insert($pivot, true); + $result[] = $this->newPivot($pivot, true); + } + if (count($result) == 1) { + // 返回中间表模型对象 + $result = $result[0]; + } + return $result; + } else { + throw new Exception('miss relation data'); + } + } + + /** + * 判断是否存在关联数据 + * @access public + * @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键 + * @return Pivot + * @throws Exception + */ + public function attached($data) + { + if ($data instanceof Model) { + $relationFk = $data->getPk(); + $id = $data->$relationFk; + } else { + $id = $data; + } + + $pk = $this->parent->getPk(); + + $pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find(); + + return $pivot ?: false; + } + + /** + * 解除关联的一个中间表数据 + * @access public + * @param integer|array $data 数据 可以使用关联对象的主键 + * @param bool $relationDel 是否同时删除关联表数据 + * @return integer + */ + public function detach($data = null, $relationDel = false) + { + if (is_array($data)) { + $id = $data; + } elseif (is_numeric($data) || is_string($data)) { + // 根据关联表主键直接写入中间表 + $id = $data; + } elseif ($data instanceof Model) { + // 根据关联表主键直接写入中间表 + $relationFk = $data->getPk(); + $id = $data->$relationFk; + } + // 删除中间表数据 + $pk = $this->parent->getPk(); + $pivot[$this->localKey] = $this->parent->$pk; + if (isset($id)) { + $pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id; + } + $this->pivot->where($pivot)->delete(); + // 删除关联表数据 + if (isset($id) && $relationDel) { + $model = $this->model; + $model::destroy($id); + } + } + + /** + * 数据同步 + * @param array $ids + * @param bool $detaching + * @return array + */ + public function sync($ids, $detaching = true) + { + $changes = [ + 'attached' => [], + 'detached' => [], + 'updated' => [], + ]; + $pk = $this->parent->getPk(); + $current = $this->pivot->where($this->localKey, $this->parent->$pk) + ->column($this->foreignKey); + $records = []; + + foreach ($ids as $key => $value) { + if (!is_array($value)) { + $records[$value] = []; + } else { + $records[$key] = $value; + } + } + + $detach = array_diff($current, array_keys($records)); + + if ($detaching && count($detach) > 0) { + $this->detach($detach); + + $changes['detached'] = $detach; + } + + foreach ($records as $id => $attributes) { + if (!in_array($id, $current)) { + $this->attach($id, $attributes); + $changes['attached'][] = $id; + } elseif (count($attributes) > 0 && + $this->attach($id, $attributes) + ) { + $changes['updated'][] = $id; + } + } + + return $changes; + + } + + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery) && $this->parent->getData()) { + $pk = $this->parent->getPk(); + $table = $this->pivot->getTable(); + $this->query->join([$table => 'pivot'], 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk); + $this->baseQuery = true; + } + } + +} diff --git a/source/thinkphp/library/think/model/relation/HasMany.php b/source/thinkphp/library/think/model/relation/HasMany.php new file mode 100644 index 0000000..ebab051 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/HasMany.php @@ -0,0 +1,318 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Loader; +use think\Model; +use think\model\Relation; + +class HasMany extends Relation +{ + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 + */ + public function __construct(Model $parent, $model, $foreignKey, $localKey) + { + $this->parent = $parent; + $this->model = $model; + $this->foreignKey = $foreignKey; + $this->localKey = $localKey; + $this->query = (new $model)->db(); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection + */ + public function getRelation($subRelation = '', $closure = null) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $list = $this->relation($subRelation)->select(); + $parent = clone $this->parent; + + foreach ($list as &$model) { + $model->setParent($parent); + } + + return $list; + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $data = $this->eagerlyOneToMany($this->query, [ + $this->foreignKey => [ + 'in', + $range, + ], + ], $relation, $subRelation, $closure); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + + foreach ($data[$result->$localKey] as &$relationModel) { + $relationModel->setParent(clone $result); + } + + $result->setRelation($attr, $this->resultSetBuild($data[$result->$localKey])); + } + } + } + + /** + * 预载入关联查询 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + + if (isset($result->$localKey)) { + $data = $this->eagerlyOneToMany($this->query, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure); + // 关联数据封装 + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + + foreach ($data[$result->$localKey] as &$relationModel) { + $relationModel->setParent(clone $result); + } + + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey])); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $localKey = $this->localKey; + $count = 0; + if (isset($result->$localKey)) { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $count = $this->query->where($this->foreignKey, $result->$localKey)->count(); + } + return $count; + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + if ($closure) { + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } + } + $localKey = $this->localKey ?: $this->parent->getPk(); + return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count(); + } + + /** + * 一对多 关联模型预查询 + * @access public + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool $closure + * @return array + */ + protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false) + { + $foreignKey = $this->foreignKey; + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $model]); + } + $list = $model->removeWhereField($foreignKey)->where($where)->with($subRelation)->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$foreignKey][] = $set; + } + return $data; + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return Model|false + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + + $model = new $this->model(); + return $model->save($data) ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + + return new $this->model($data); + } + + /** + * 批量保存当前关联数据对象 + * @access public + * @param array $dataSet 数据集 + * @return integer + */ + public function saveAll(array $dataSet) + { + $result = false; + foreach ($dataSet as $key => $data) { + $result = $this->save($data); + } + return $result; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + + return $this->parent->db() + ->alias($model) + ->field($model . '.*') + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType) + ->group($relation . '.' . $this->foreignKey) + ->having('count(' . $id . ')' . $operator . $count); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + + if (is_array($where)) { + foreach ($where as $key => $val) { + if (false === strpos($key, '.')) { + $where[$relation . '.' . $key] = $val; + unset($where[$key]); + } + } + } + + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db()->alias($model) + ->field($fields) + ->group($model . '.' . $this->localKey) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey) + ->where($where); + } + + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery)) { + if (isset($this->parent->{$this->localKey})) { + // 关联查询带入关联条件 + $this->query->where($this->foreignKey, $this->parent->{$this->localKey}); + } + $this->baseQuery = true; + } + } + +} diff --git a/source/thinkphp/library/think/model/relation/HasManyThrough.php b/source/thinkphp/library/think/model/relation/HasManyThrough.php new file mode 100644 index 0000000..3a9a548 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/HasManyThrough.php @@ -0,0 +1,157 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Relation; + +class HasManyThrough extends Relation +{ + // 中间关联表外键 + protected $throughKey; + // 中间表模型 + protected $through; + + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $through 中间模型名 + * @param string $foreignKey 关联外键 + * @param string $throughKey 关联外键 + * @param string $localKey 关联主键 + */ + public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey) + { + $this->parent = $parent; + $this->model = $model; + $this->through = $through; + $this->foreignKey = $foreignKey; + $this->throughKey = $throughKey; + $this->localKey = $localKey; + $this->query = (new $model)->db(); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection + */ + public function getRelation($subRelation = '', $closure = null) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + + return $this->relation($subRelation)->select(); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + {} + + /** + * 预载入关联查询 返回模型对象 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + {} + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + {} + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } + + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery) && $this->parent->getData()) { + $through = $this->through; + $alias = Loader::parseName(basename(str_replace('\\', '/', $this->model))); + $throughTable = $through::getTable(); + $pk = (new $through)->getPk(); + $throughKey = $this->throughKey; + $modelTable = $this->parent->getTable(); + $this->query->field($alias . '.*')->alias($alias) + ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) + ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) + ->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey}); + $this->baseQuery = true; + } + } + +} diff --git a/source/thinkphp/library/think/model/relation/HasOne.php b/source/thinkphp/library/think/model/relation/HasOne.php new file mode 100644 index 0000000..db74e4a --- /dev/null +++ b/source/thinkphp/library/think/model/relation/HasOne.php @@ -0,0 +1,215 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Loader; +use think\Model; + +class HasOne extends OneToOne +{ + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $foreignKey 关联外键 + * @param string $localKey 当前模型主键 + * @param string $joinType JOIN类型 + */ + public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER') + { + $this->parent = $parent; + $this->model = $model; + $this->foreignKey = $foreignKey; + $this->localKey = $localKey; + $this->joinType = $joinType; + $this->query = (new $model)->db(); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return array|false|\PDOStatement|string|Model + */ + public function getRelation($subRelation = '', $closure = null) + { + // 执行关联定义方法 + $localKey = $this->localKey; + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + // 判断关联类型执行查询 + $relationModel = $this->query + ->removeWhereField($this->foreignKey) + ->where($this->foreignKey, $this->parent->$localKey) + ->relation($subRelation) + ->find(); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + + return $relationModel; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @return Query + */ + public function has() + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + return $this->parent->db() + ->alias($model) + ->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) { + $query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey); + }); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + $table = $this->query->getTable(); + $model = basename(str_replace('\\', '/', get_class($this->parent))); + $relation = basename(str_replace('\\', '/', $this->model)); + + if (is_array($where)) { + foreach ($where as $key => $val) { + if (false === strpos($key, '.')) { + $where[$relation . '.' . $key] = $val; + unset($where[$key]); + } + } + } + $fields = $this->getRelationQueryFields($fields, $model); + + return $this->parent->db()->alias($model) + ->field($fields) + ->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType) + ->where($where); + } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $this->query->removeWhereField($foreignKey); + $data = $this->eagerlyWhere($this->query, [ + $foreignKey => [ + 'in', + $range, + ], + ], $foreignKey, $relation, $subRelation, $closure); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + // 设置关联属性 + $result->setRelation($attr, $relationModel); + } + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $this->query->removeWhereField($foreignKey); + $data = $this->eagerlyWhere($this->query, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); + + // 关联模型 + if (!isset($data[$result->$localKey])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$localKey]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + if (!empty($this->bindAttr)) { + // 绑定关联属性 + $this->bindAttr($relationModel, $result, $this->bindAttr); + } else { + $result->setRelation(Loader::parseName($relation), $relationModel); + } + } + + /** + * 执行基础查询(仅执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery)) { + if (isset($this->parent->{$this->localKey})) { + // 关联查询带入关联条件 + $this->query->where($this->foreignKey, '=', $this->parent->{$this->localKey}); + } + + $this->baseQuery = true; + } + } +} diff --git a/source/thinkphp/library/think/model/relation/MorphMany.php b/source/thinkphp/library/think/model/relation/MorphMany.php new file mode 100644 index 0000000..2755d57 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/MorphMany.php @@ -0,0 +1,314 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\Db; +use think\db\Query; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Relation; + +class MorphMany extends Relation +{ + // 多态字段 + protected $morphKey; + protected $morphType; + // 多态类型 + protected $type; + + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 + */ + public function __construct(Model $parent, $model, $morphKey, $morphType, $type) + { + $this->parent = $parent; + $this->model = $model; + $this->type = $type; + $this->morphKey = $morphKey; + $this->morphType = $morphType; + $this->query = (new $model)->db(); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection + */ + public function getRelation($subRelation = '', $closure = null) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $list = $this->relation($subRelation)->select(); + $parent = clone $this->parent; + + foreach ($list as &$model) { + $model->setParent($parent); + } + + return $list; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + throw new Exception('relation not support: has'); + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + $morphType = $this->morphType; + $morphKey = $this->morphKey; + $type = $this->type; + $range = []; + foreach ($resultSet as $result) { + $pk = $result->getPk(); + // 获取关联外键列表 + if (isset($result->$pk)) { + $range[] = $result->$pk; + } + } + + if (!empty($range)) { + $data = $this->eagerlyMorphToMany([ + $morphKey => ['in', $range], + $morphType => $type, + ], $relation, $subRelation, $closure); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$pk])) { + $data[$result->$pk] = []; + } + foreach ($data[$result->$pk] as &$relationModel) { + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + $result->setRelation($attr, $this->resultSetBuild($data[$result->$pk])); + } + } + } + + /** + * 预载入关联查询 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + $pk = $result->getPk(); + if (isset($result->$pk)) { + $data = $this->eagerlyMorphToMany([ + $this->morphKey => $result->$pk, + $this->morphType => $this->type, + ], $relation, $subRelation, $closure); + + if (!isset($data[$result->$pk])) { + $data[$result->$pk] = []; + } + + foreach ($data[$result->$pk] as &$relationModel) { + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + + $result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk])); + } + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + $pk = $result->getPk(); + $count = 0; + if (isset($result->$pk)) { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count(); + } + return $count; + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + if ($closure) { + $return = call_user_func_array($closure, [ & $this->query]); + if ($return && is_string($return)) { + $name = $return; + } + } + + return $this->query->where([ + $this->morphKey => [ + 'exp', + Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()), + ], + $this->morphType => $this->type, + ])->fetchSql()->count(); + } + + /** + * 多态一对多 关联模型预查询 + * @access public + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool|\Closure $closure 闭包 + * @return array + */ + protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false) + { + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $this]); + } + $list = $this->query->where($where)->with($subRelation)->select(); + $morphKey = $this->morphKey; + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$morphKey][] = $set; + } + return $data; + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return Model|false + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + $model = new $this->model(); + + return $model->save() ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + return new $this->model($data); + } + + /** + * 批量保存当前关联数据对象 + * @access public + * @param array $dataSet 数据集 + * @return integer + */ + public function saveAll(array $dataSet) + { + $result = false; + foreach ($dataSet as $key => $data) { + $result = $this->save($data); + } + return $result; + } + + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery) && $this->parent->getData()) { + $pk = $this->parent->getPk(); + $map[$this->morphKey] = $this->parent->$pk; + $map[$this->morphType] = $this->type; + $this->query->where($map); + $this->baseQuery = true; + } + } + +} diff --git a/source/thinkphp/library/think/model/relation/MorphOne.php b/source/thinkphp/library/think/model/relation/MorphOne.php new file mode 100644 index 0000000..5ec7172 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/MorphOne.php @@ -0,0 +1,263 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Relation; + +class MorphOne extends Relation +{ + // 多态字段 + protected $morphKey; + protected $morphType; + // 多态类型 + protected $type; + + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $model 模型名 + * @param string $morphKey 关联外键 + * @param string $morphType 多态字段名 + * @param string $type 多态类型 + */ + public function __construct(Model $parent, $model, $morphKey, $morphType, $type) + { + $this->parent = $parent; + $this->model = $model; + $this->type = $type; + $this->morphKey = $morphKey; + $this->morphType = $morphType; + $this->query = (new $model)->db(); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return false|\PDOStatement|string|\think\Collection + */ + public function getRelation($subRelation = '', $closure = null) + { + if ($closure) { + call_user_func_array($closure, [ & $this->query]); + } + $relationModel = $this->relation($subRelation)->find(); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + + return $relationModel; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + $morphType = $this->morphType; + $morphKey = $this->morphKey; + $type = $this->type; + $range = []; + foreach ($resultSet as $result) { + $pk = $result->getPk(); + // 获取关联外键列表 + if (isset($result->$pk)) { + $range[] = $result->$pk; + } + } + + if (!empty($range)) { + $data = $this->eagerlyMorphToOne([ + $morphKey => ['in', $range], + $morphType => $type, + ], $relation, $subRelation, $closure); + // 关联属性名 + $attr = Loader::parseName($relation); + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$pk])) { + $relationModel = null; + } else { + $relationModel = $data[$result->$pk]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } + + $result->setRelation($attr, $relationModel); + } + } + } + + /** + * 预载入关联查询 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + $pk = $result->getPk(); + if (isset($result->$pk)) { + $pk = $result->$pk; + $data = $this->eagerlyMorphToOne([ + $this->morphKey => $pk, + $this->morphType => $this->type, + ], $relation, $subRelation, $closure); + + if (isset($data[$pk])) { + $relationModel = $data[$pk]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + } else { + $relationModel = null; + } + + $result->setRelation(Loader::parseName($relation), $relationModel); + } + } + + /** + * 多态一对一 关联模型预查询 + * @access public + * @param array $where 关联预查询条件 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool|\Closure $closure 闭包 + * @return array + */ + protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = false) + { + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $this]); + } + $list = $this->query->where($where)->with($subRelation)->find(); + $morphKey = $this->morphKey; + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$morphKey][] = $set; + } + return $data; + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return Model|false + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + $model = new $this->model(); + + return $model->save() ? $model : false; + } + + /** + * 创建关联对象实例 + * @param array $data + * @return Model + */ + public function make($data = []) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + // 保存关联表数据 + $pk = $this->parent->getPk(); + + $data[$this->morphKey] = $this->parent->$pk; + $data[$this->morphType] = $this->type; + + return new $this->model($data); + } + + /** + * 执行基础查询(进执行一次) + * @access protected + * @return void + */ + protected function baseQuery() + { + if (empty($this->baseQuery) && $this->parent->getData()) { + $pk = $this->parent->getPk(); + $map[$this->morphKey] = $this->parent->$pk; + $map[$this->morphType] = $this->type; + $this->query->where($map); + $this->baseQuery = true; + } + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } +} diff --git a/source/thinkphp/library/think/model/relation/MorphTo.php b/source/thinkphp/library/think/model/relation/MorphTo.php new file mode 100644 index 0000000..7d45265 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/MorphTo.php @@ -0,0 +1,299 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Relation; + +class MorphTo extends Relation +{ + // 多态字段 + protected $morphKey; + protected $morphType; + // 多态别名 + protected $alias; + protected $relation; + + /** + * 构造函数 + * @access public + * @param Model $parent 上级模型对象 + * @param string $morphType 多态字段名 + * @param string $morphKey 外键名 + * @param array $alias 多态别名定义 + * @param string $relation 关联名 + */ + public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null) + { + $this->parent = $parent; + $this->morphType = $morphType; + $this->morphKey = $morphKey; + $this->alias = $alias; + $this->relation = $relation; + } + + /** + * 获取当前的关联模型类的实例 + * @access public + * @return Model + */ + public function getModel() + { + $morphType = $this->morphType; + $model = $this->parseModel($this->parent->$morphType); + return (new $model); + } + + /** + * 延迟获取关联数据 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包查询条件 + * @return mixed + */ + public function getRelation($subRelation = '', $closure = null) + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + // 多态模型 + $model = $this->parseModel($this->parent->$morphType); + // 主键数据 + $pk = $this->parent->$morphKey; + $relationModel = (new $model)->relation($subRelation)->find($pk); + + if ($relationModel) { + $relationModel->setParent(clone $this->parent); + } + return $relationModel; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param string $operator 比较操作符 + * @param integer $count 个数 + * @param string $id 关联表的统计字段 + * @param string $joinType JOIN类型 + * @return Query + */ + public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER') + { + return $this->parent; + } + + /** + * 根据关联条件查询当前模型 + * @access public + * @param mixed $where 查询条件(数组或者闭包) + * @param mixed $fields 字段 + * @return Query + */ + public function hasWhere($where = [], $fields = null) + { + throw new Exception('relation not support: hasWhere'); + } + + /** + * 解析模型的完整命名空间 + * @access protected + * @param string $model 模型名(或者完整类名) + * @return string + */ + protected function parseModel($model) + { + if (isset($this->alias[$model])) { + $model = $this->alias[$model]; + } + if (false === strpos($model, '\\')) { + $path = explode('\\', get_class($this->parent)); + array_pop($path); + array_push($path, Loader::parseName($model, 1)); + $model = implode('\\', $path); + } + return $model; + } + + /** + * 设置多态别名 + * @access public + * @param array $alias 别名定义 + * @return $this + */ + public function setAlias($alias) + { + $this->alias = $alias; + return $this; + } + + /** + * 移除关联查询参数 + * @access public + * @return $this + */ + public function removeOption() + { + return $this; + } + + /** + * 预载入关联查询 + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + * @throws Exception + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (!empty($result->$morphKey)) { + $range[$result->$morphType][] = $result->$morphKey; + } + } + + if (!empty($range)) { + // 关联属性名 + $attr = Loader::parseName($relation); + foreach ($range as $key => $val) { + // 多态类型映射 + $model = $this->parseModel($key); + $obj = new $model; + $pk = $obj->getPk(); + $list = $obj->all($val, $subRelation); + $data = []; + foreach ($list as $k => $vo) { + $data[$vo->$pk] = $vo; + } + foreach ($resultSet as $result) { + if ($key == $result->$morphType) { + // 关联模型 + if (!isset($data[$result->$morphKey])) { + throw new Exception('relation data not exists :' . $this->model); + } else { + $relationModel = $data[$result->$morphKey]; + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + + $result->setRelation($attr, $relationModel); + } + } + } + } + } + } + + /** + * 预载入关联查询 + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + // 多态类型映射 + $model = $this->parseModel($result->{$this->morphType}); + $this->eagerlyMorphToOne($model, $relation, $result, $subRelation); + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + } + + /** + * 多态MorphTo 关联模型预查询 + * @access public + * @param object $model 关联模型对象 + * @param string $relation 关联名 + * @param $result + * @param string $subRelation 子关联 + * @return void + */ + protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '') + { + // 预载入关联查询 支持嵌套预载入 + $pk = $this->parent->{$this->morphKey}; + $data = (new $model)->with($subRelation)->find($pk); + if ($data) { + $data->setParent(clone $result); + $data->isUpdate(true); + } + $result->setRelation(Loader::parseName($relation), $data ?: null); + } + + /** + * 添加关联数据 + * @access public + * @param Model $model 关联模型对象 + * @param string $type 多态类型 + * @return Model + */ + public function associate($model, $type = '') + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + $pk = $model->getPk(); + + $this->parent->setAttr($morphKey, $model->$pk); + $this->parent->setAttr($morphType, $type ?: get_class($model)); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, $model); + } + + /** + * 注销关联数据 + * @access public + * @return Model + */ + public function dissociate() + { + $morphKey = $this->morphKey; + $morphType = $this->morphType; + + $this->parent->setAttr($morphKey, null); + $this->parent->setAttr($morphType, null); + $this->parent->save(); + + return $this->parent->setRelation($this->relation, null); + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } +} diff --git a/source/thinkphp/library/think/model/relation/OneToOne.php b/source/thinkphp/library/think/model/relation/OneToOne.php new file mode 100644 index 0000000..353ce21 --- /dev/null +++ b/source/thinkphp/library/think/model/relation/OneToOne.php @@ -0,0 +1,337 @@ + +// +---------------------------------------------------------------------- + +namespace think\model\relation; + +use think\db\Query; +use think\Exception; +use think\Loader; +use think\Model; +use think\model\Relation; + +/** + * Class OneToOne + * @package think\model\relation + * + */ +abstract class OneToOne extends Relation +{ + // 预载入方式 0 -JOIN 1 -IN + protected $eagerlyType = 1; + // 当前关联的JOIN类型 + protected $joinType; + // 要绑定的属性 + protected $bindAttr = []; + // 关联方法名 + protected $relation; + + /** + * 设置join类型 + * @access public + * @param string $type JOIN类型 + * @return $this + */ + public function joinType($type) + { + $this->joinType = $type; + return $this; + } + + /** + * 预载入关联查询(JOIN方式) + * @access public + * @param Query $query 查询对象 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param \Closure $closure 闭包条件 + * @param bool $first + * @return void + */ + public function eagerly(Query $query, $relation, $subRelation, $closure, $first) + { + $name = Loader::parseName(basename(str_replace('\\', '/', get_class($query->getModel())))); + + if ($first) { + $table = $query->getTable(); + $query->table([$table => $name]); + if ($query->getOptions('field')) { + $field = $query->getOptions('field'); + $query->removeOption('field'); + } else { + $field = true; + } + $query->field($field, false, $table, $name); + $field = null; + } + + // 预载入封装 + $joinTable = $this->query->getTable(); + $joinAlias = $relation; + $query->via($joinAlias); + + if ($this instanceof BelongsTo) { + $query->join([$joinTable => $joinAlias], $name . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType); + } else { + $query->join([$joinTable => $joinAlias], $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType); + } + + if ($closure) { + // 执行闭包查询 + call_user_func_array($closure, [ & $query]); + // 使用withField指定获取关联的字段,如 + // $query->where(['id'=>1])->withField('id,name'); + if ($query->getOptions('with_field')) { + $field = $query->getOptions('with_field'); + $query->removeOption('with_field'); + } + } elseif (isset($this->option['field'])) { + $field = $this->option['field']; + } + $query->field(isset($field) ? $field : true, false, $joinTable, $joinAlias, $relation . '__'); + } + + /** + * 预载入关联查询(数据集) + * @param array $resultSet + * @param string $relation + * @param string $subRelation + * @param \Closure $closure + * @return mixed + */ + abstract protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure); + + /** + * 预载入关联查询(数据) + * @param Model $result + * @param string $relation + * @param string $subRelation + * @param \Closure $closure + * @return mixed + */ + abstract protected function eagerlyOne(&$result, $relation, $subRelation, $closure); + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure) + { + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlySet($resultSet, $relation, $subRelation, $closure); + } else { + // 模型关联组装 + foreach ($resultSet as $result) { + $this->match($this->model, $relation, $result); + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @return void + */ + public function eagerlyResult(&$result, $relation, $subRelation, $closure) + { + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlyOne($result, $relation, $subRelation, $closure); + } else { + // 模型关联组装 + $this->match($this->model, $relation, $result); + } + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return Model|false + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + $model = new $this->model; + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + return $model->save($data) ? $model : false; + } + + /** + * 设置预载入方式 + * @access public + * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 + * @return $this + */ + public function setEagerlyType($type) + { + $this->eagerlyType = $type; + return $this; + } + + /** + * 获取预载入方式 + * @access public + * @return integer + */ + public function getEagerlyType() + { + return $this->eagerlyType; + } + + /** + * 绑定关联表的属性到父模型属性 + * @access public + * @param mixed $attr 要绑定的属性列表 + * @return $this + */ + public function bind($attr) + { + if (is_string($attr)) { + $attr = explode(',', $attr); + } + $this->bindAttr = $attr; + return $this; + } + + /** + * 获取绑定属性 + * @access public + * @return array + */ + public function getBindAttr() + { + return $this->bindAttr; + } + + /** + * 关联统计 + * @access public + * @param Model $result 数据对象 + * @param \Closure $closure 闭包 + * @return integer + */ + public function relationCount($result, $closure) + { + } + + /** + * 一对一 关联模型预查询拼装 + * @access public + * @param string $model 模型名称 + * @param string $relation 关联名 + * @param Model $result 模型对象实例 + * @return void + */ + protected function match($model, $relation, &$result) + { + // 重新组装模型数据 + foreach ($result->getData() as $key => $val) { + if (strpos($key, '__')) { + list($name, $attr) = explode('__', $key, 2); + if ($name == $relation) { + $list[$name][$attr] = $val; + unset($result->$key); + } + } + } + + if (isset($list[$relation])) { + $relationModel = new $model($list[$relation]); + $relationModel->setParent(clone $result); + $relationModel->isUpdate(true); + + if (!empty($this->bindAttr)) { + $this->bindAttr($relationModel, $result, $this->bindAttr); + } + } else { + $relationModel = null; + } + $result->setRelation(Loader::parseName($relation), $relationModel); + } + + /** + * 绑定关联属性到父模型 + * @access protected + * @param Model $model 关联模型对象 + * @param Model $result 父模型对象 + * @param array $bindAttr 绑定属性 + * @return void + * @throws Exception + */ + protected function bindAttr($model, &$result, $bindAttr) + { + foreach ($bindAttr as $key => $attr) { + $key = is_numeric($key) ? $attr : $key; + if (isset($result->$key)) { + throw new Exception('bind attr has exists:' . $key); + } else { + $result->setAttr($key, $model ? $model->$attr : null); + } + } + } + + /** + * 一对一 关联模型预查询(IN方式) + * @access public + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool|\Closure $closure + * @return array + */ + protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) + { + $this->baseQuery = true; + + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $model]); + if ($field = $model->getOptions('with_field')) { + $model->field($field)->removeOption('with_field'); + } + } + $list = $model->where($where)->with($subRelation)->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$key] = $set; + } + return $data; + } + + /** + * 创建关联统计子查询 + * @access public + * @param \Closure $closure 闭包 + * @param string $name 统计数据别名 + * @return string + */ + public function getRelationCountQuery($closure, &$name = null) + { + throw new Exception('relation not support: withCount'); + } +} diff --git a/source/thinkphp/library/think/paginator/driver/Bootstrap.php b/source/thinkphp/library/think/paginator/driver/Bootstrap.php new file mode 100644 index 0000000..c5ac60d --- /dev/null +++ b/source/thinkphp/library/think/paginator/driver/Bootstrap.php @@ -0,0 +1,205 @@ + +// +---------------------------------------------------------------------- + +namespace think\paginator\driver; + +use think\Paginator; + +class Bootstrap extends Paginator +{ + + /** + * 上一页按钮 + * @param string $text + * @return string + */ + protected function getPreviousButton($text = "«") + { + + if ($this->currentPage() <= 1) { + return $this->getDisabledTextWrapper($text); + } + + $url = $this->url( + $this->currentPage() - 1 + ); + + return $this->getPageLinkWrapper($url, $text); + } + + /** + * 下一页按钮 + * @param string $text + * @return string + */ + protected function getNextButton($text = '»') + { + if (!$this->hasMore) { + return $this->getDisabledTextWrapper($text); + } + + $url = $this->url($this->currentPage() + 1); + + return $this->getPageLinkWrapper($url, $text); + } + + /** + * 页码按钮 + * @return string + */ + protected function getLinks() + { + if ($this->simple) + return ''; + + $block = [ + 'first' => null, + 'slider' => null, + 'last' => null + ]; + + $side = 3; + $window = $side * 2; + + if ($this->lastPage < $window + 6) { + $block['first'] = $this->getUrlRange(1, $this->lastPage); + } elseif ($this->currentPage <= $window) { + $block['first'] = $this->getUrlRange(1, $window + 2); + $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); + } elseif ($this->currentPage > ($this->lastPage - $window)) { + $block['first'] = $this->getUrlRange(1, 2); + $block['last'] = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage); + } else { + $block['first'] = $this->getUrlRange(1, 2); + $block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side); + $block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage); + } + + $html = ''; + + if (is_array($block['first'])) { + $html .= $this->getUrlLinks($block['first']); + } + + if (is_array($block['slider'])) { + $html .= $this->getDots(); + $html .= $this->getUrlLinks($block['slider']); + } + + if (is_array($block['last'])) { + $html .= $this->getDots(); + $html .= $this->getUrlLinks($block['last']); + } + + return $html; + } + + /** + * 渲染分页html + * @return mixed + */ + public function render() + { + if ($this->hasPages()) { + if ($this->simple) { + return sprintf( + '
    %s %s
', + $this->getPreviousButton(), + $this->getNextButton() + ); + } else { + return sprintf( + '
    %s %s %s
', + $this->getPreviousButton(), + $this->getLinks(), + $this->getNextButton() + ); + } + } + } + + /** + * 生成一个可点击的按钮 + * + * @param string $url + * @param int $page + * @return string + */ + protected function getAvailablePageWrapper($url, $page) + { + return '
  • ' . $page . '
  • '; + } + + /** + * 生成一个禁用的按钮 + * + * @param string $text + * @return string + */ + protected function getDisabledTextWrapper($text) + { + return '
  • ' . $text . '
  • '; + } + + /** + * 生成一个激活的按钮 + * + * @param string $text + * @return string + */ + protected function getActivePageWrapper($text) + { + return '
  • ' . $text . '
  • '; + } + + /** + * 生成省略号按钮 + * + * @return string + */ + protected function getDots() + { + return $this->getDisabledTextWrapper('...'); + } + + /** + * 批量生成页码按钮. + * + * @param array $urls + * @return string + */ + protected function getUrlLinks(array $urls) + { + $html = ''; + + foreach ($urls as $page => $url) { + $html .= $this->getPageLinkWrapper($url, $page); + } + + return $html; + } + + /** + * 生成普通页码按钮 + * + * @param string $url + * @param int $page + * @return string + */ + protected function getPageLinkWrapper($url, $page) + { + if ($page == $this->currentPage()) { + return $this->getActivePageWrapper($page); + } + + return $this->getAvailablePageWrapper($url, $page); + } +} diff --git a/source/thinkphp/library/think/process/Builder.php b/source/thinkphp/library/think/process/Builder.php new file mode 100644 index 0000000..da56163 --- /dev/null +++ b/source/thinkphp/library/think/process/Builder.php @@ -0,0 +1,233 @@ + +// +---------------------------------------------------------------------- + +namespace think\process; + +use think\Process; + +class Builder +{ + private $arguments; + private $cwd; + private $env = null; + private $input; + private $timeout = 60; + private $options = []; + private $inheritEnv = true; + private $prefix = []; + private $outputDisabled = false; + + /** + * 构造方法 + * @param string[] $arguments 参数 + */ + public function __construct(array $arguments = []) + { + $this->arguments = $arguments; + } + + /** + * 创建一个实例 + * @param string[] $arguments 参数 + * @return self + */ + public static function create(array $arguments = []) + { + return new static($arguments); + } + + /** + * 添加一个参数 + * @param string $argument 参数 + * @return self + */ + public function add($argument) + { + $this->arguments[] = $argument; + + return $this; + } + + /** + * 添加一个前缀 + * @param string|array $prefix + * @return self + */ + public function setPrefix($prefix) + { + $this->prefix = is_array($prefix) ? $prefix : [$prefix]; + + return $this; + } + + /** + * 设置参数 + * @param string[] $arguments + * @return self + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + + /** + * 设置工作目录 + * @param null|string $cwd + * @return self + */ + public function setWorkingDirectory($cwd) + { + $this->cwd = $cwd; + + return $this; + } + + /** + * 是否初始化环境变量 + * @param bool $inheritEnv + * @return self + */ + public function inheritEnvironmentVariables($inheritEnv = true) + { + $this->inheritEnv = $inheritEnv; + + return $this; + } + + /** + * 设置环境变量 + * @param string $name + * @param null|string $value + * @return self + */ + public function setEnv($name, $value) + { + $this->env[$name] = $value; + + return $this; + } + + /** + * 添加环境变量 + * @param array $variables + * @return self + */ + public function addEnvironmentVariables(array $variables) + { + $this->env = array_replace($this->env, $variables); + + return $this; + } + + /** + * 设置输入 + * @param mixed $input + * @return self + */ + public function setInput($input) + { + $this->input = Utils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input); + + return $this; + } + + /** + * 设置超时时间 + * @param float|null $timeout + * @return self + */ + public function setTimeout($timeout) + { + if (null === $timeout) { + $this->timeout = null; + + return $this; + } + + $timeout = (float) $timeout; + + if ($timeout < 0) { + throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); + } + + $this->timeout = $timeout; + + return $this; + } + + /** + * 设置proc_open选项 + * @param string $name + * @param string $value + * @return self + */ + public function setOption($name, $value) + { + $this->options[$name] = $value; + + return $this; + } + + /** + * 禁止输出 + * @return self + */ + public function disableOutput() + { + $this->outputDisabled = true; + + return $this; + } + + /** + * 开启输出 + * @return self + */ + public function enableOutput() + { + $this->outputDisabled = false; + + return $this; + } + + /** + * 创建一个Process实例 + * @return Process + */ + public function getProcess() + { + if (0 === count($this->prefix) && 0 === count($this->arguments)) { + throw new \LogicException('You must add() command arguments before calling getProcess().'); + } + + $options = $this->options; + + $arguments = array_merge($this->prefix, $this->arguments); + $script = implode(' ', array_map([__NAMESPACE__ . '\\Utils', 'escapeArgument'], $arguments)); + + if ($this->inheritEnv) { + // include $_ENV for BC purposes + $env = array_replace($_ENV, $_SERVER, $this->env); + } else { + $env = $this->env; + } + + $process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options); + + if ($this->outputDisabled) { + $process->disableOutput(); + } + + return $process; + } +} diff --git a/source/thinkphp/library/think/process/Utils.php b/source/thinkphp/library/think/process/Utils.php new file mode 100644 index 0000000..f94c648 --- /dev/null +++ b/source/thinkphp/library/think/process/Utils.php @@ -0,0 +1,75 @@ + +// +---------------------------------------------------------------------- + +namespace think\process; + +class Utils +{ + + /** + * 转义字符串 + * @param string $argument + * @return string + */ + public static function escapeArgument($argument) + { + + if ('' === $argument) { + return escapeshellarg($argument); + } + $escapedArgument = ''; + $quote = false; + foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { + if ('"' === $part) { + $escapedArgument .= '\\"'; + } elseif (self::isSurroundedBy($part, '%')) { + // Avoid environment variable expansion + $escapedArgument .= '^%"' . substr($part, 1, -1) . '"^%'; + } else { + // escape trailing backslash + if ('\\' === substr($part, -1)) { + $part .= '\\'; + } + $quote = true; + $escapedArgument .= $part; + } + } + if ($quote) { + $escapedArgument = '"' . $escapedArgument . '"'; + } + return $escapedArgument; + } + + /** + * 验证并进行规范化Process输入。 + * @param string $caller + * @param mixed $input + * @return string + * @throws \InvalidArgumentException + */ + public static function validateInput($caller, $input) + { + if (null !== $input) { + if (is_resource($input)) { + return $input; + } + if (is_scalar($input)) { + return (string) $input; + } + throw new \InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller)); + } + return $input; + } + + private static function isSurroundedBy($arg, $char) + { + return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; + } + +} diff --git a/source/thinkphp/library/think/process/exception/Failed.php b/source/thinkphp/library/think/process/exception/Failed.php new file mode 100644 index 0000000..5295082 --- /dev/null +++ b/source/thinkphp/library/think/process/exception/Failed.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\exception; + +use think\Process; + +class Failed extends \RuntimeException +{ + + private $process; + + public function __construct(Process $process) + { + if ($process->isSuccessful()) { + throw new \InvalidArgumentException('Expected a failed process, but the given process was successful.'); + } + + $error = sprintf('The command "%s" failed.' . "\nExit Code: %s(%s)", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText()); + + if (!$process->isOutputDisabled()) { + $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput()); + } + + parent::__construct($error); + + $this->process = $process; + } + + public function getProcess() + { + return $this->process; + } +} diff --git a/source/thinkphp/library/think/process/exception/Timeout.php b/source/thinkphp/library/think/process/exception/Timeout.php new file mode 100644 index 0000000..d5f1162 --- /dev/null +++ b/source/thinkphp/library/think/process/exception/Timeout.php @@ -0,0 +1,61 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\exception; + +use think\Process; + +class Timeout extends \RuntimeException +{ + + const TYPE_GENERAL = 1; + const TYPE_IDLE = 2; + + private $process; + private $timeoutType; + + public function __construct(Process $process, $timeoutType) + { + $this->process = $process; + $this->timeoutType = $timeoutType; + + parent::__construct(sprintf('The process "%s" exceeded the timeout of %s seconds.', $process->getCommandLine(), $this->getExceededTimeout())); + } + + public function getProcess() + { + return $this->process; + } + + public function isGeneralTimeout() + { + return $this->timeoutType === self::TYPE_GENERAL; + } + + public function isIdleTimeout() + { + return $this->timeoutType === self::TYPE_IDLE; + } + + public function getExceededTimeout() + { + switch ($this->timeoutType) { + case self::TYPE_GENERAL: + return $this->process->getTimeout(); + + case self::TYPE_IDLE: + return $this->process->getIdleTimeout(); + + default: + throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); + } + } +} diff --git a/source/thinkphp/library/think/process/pipes/Pipes.php b/source/thinkphp/library/think/process/pipes/Pipes.php new file mode 100644 index 0000000..82396b8 --- /dev/null +++ b/source/thinkphp/library/think/process/pipes/Pipes.php @@ -0,0 +1,93 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\pipes; + +abstract class Pipes +{ + + /** @var array */ + public $pipes = []; + + /** @var string */ + protected $inputBuffer = ''; + /** @var resource|null */ + protected $input; + + /** @var bool */ + private $blocked = true; + + const CHUNK_SIZE = 16384; + + /** + * 返回用于 proc_open 描述符的数组 + * @return array + */ + abstract public function getDescriptors(); + + /** + * 返回一个数组的索引由其相关的流,以防这些管道使用的临时文件的文件名。 + * @return string[] + */ + abstract public function getFiles(); + + /** + * 文件句柄和管道中读取数据。 + * @param bool $blocking 是否使用阻塞调用 + * @param bool $close 是否要关闭管道,如果他们已经到达 EOF。 + * @return string[] + */ + abstract public function readAndWrite($blocking, $close = false); + + /** + * 返回当前状态如果有打开的文件句柄或管道。 + * @return bool + */ + abstract public function areOpen(); + + /** + * {@inheritdoc} + */ + public function close() + { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + $this->pipes = []; + } + + /** + * 检查系统调用已被中断 + * @return bool + */ + protected function hasSystemCallBeenInterrupted() + { + $lastError = error_get_last(); + + return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); + } + + protected function unblock() + { + if (!$this->blocked) { + return; + } + + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, 0); + } + if (null !== $this->input) { + stream_set_blocking($this->input, 0); + } + + $this->blocked = false; + } +} diff --git a/source/thinkphp/library/think/process/pipes/Unix.php b/source/thinkphp/library/think/process/pipes/Unix.php new file mode 100644 index 0000000..fd99a5d --- /dev/null +++ b/source/thinkphp/library/think/process/pipes/Unix.php @@ -0,0 +1,196 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\pipes; + +use think\Process; + +class Unix extends Pipes +{ + + /** @var bool */ + private $ttyMode; + /** @var bool */ + private $ptyMode; + /** @var bool */ + private $disableOutput; + + public function __construct($ttyMode, $ptyMode, $input, $disableOutput) + { + $this->ttyMode = (bool) $ttyMode; + $this->ptyMode = (bool) $ptyMode; + $this->disableOutput = (bool) $disableOutput; + + if (is_resource($input)) { + $this->input = $input; + } else { + $this->inputBuffer = (string) $input; + } + } + + public function __destruct() + { + $this->close(); + } + + /** + * {@inheritdoc} + */ + public function getDescriptors() + { + if ($this->disableOutput) { + $nullstream = fopen('/dev/null', 'c'); + + return [ + ['pipe', 'r'], + $nullstream, + $nullstream, + ]; + } + + if ($this->ttyMode) { + return [ + ['file', '/dev/tty', 'r'], + ['file', '/dev/tty', 'w'], + ['file', '/dev/tty', 'w'], + ]; + } + + if ($this->ptyMode && Process::isPtySupported()) { + return [ + ['pty'], + ['pty'], + ['pty'], + ]; + } + + return [ + ['pipe', 'r'], + ['pipe', 'w'], // stdout + ['pipe', 'w'], // stderr + ]; + } + + /** + * {@inheritdoc} + */ + public function getFiles() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function readAndWrite($blocking, $close = false) + { + + if (1 === count($this->pipes) && [0] === array_keys($this->pipes)) { + fclose($this->pipes[0]); + unset($this->pipes[0]); + } + + if (empty($this->pipes)) { + return []; + } + + $this->unblock(); + + $read = []; + + if (null !== $this->input) { + $r = array_merge($this->pipes, ['input' => $this->input]); + } else { + $r = $this->pipes; + } + + unset($r[0]); + + $w = isset($this->pipes[0]) ? [$this->pipes[0]] : null; + $e = null; + + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + + if (!$this->hasSystemCallBeenInterrupted()) { + $this->pipes = []; + } + + return $read; + } + + if (0 === $n) { + return $read; + } + + foreach ($r as $pipe) { + + $type = (false !== $found = array_search($pipe, $this->pipes)) ? $found : 'input'; + $data = ''; + while ('' !== $dataread = (string) fread($pipe, self::CHUNK_SIZE)) { + $data .= $dataread; + } + + if ('' !== $data) { + if ('input' === $type) { + $this->inputBuffer .= $data; + } else { + $read[$type] = $data; + } + } + + if (false === $data || (true === $close && feof($pipe) && '' === $data)) { + if ('input' === $type) { + $this->input = null; + } else { + fclose($this->pipes[$type]); + unset($this->pipes[$type]); + } + } + } + + if (null !== $w && 0 < count($w)) { + while (strlen($this->inputBuffer)) { + $written = fwrite($w[0], $this->inputBuffer, 2 << 18); // write 512k + if ($written > 0) { + $this->inputBuffer = (string) substr($this->inputBuffer, $written); + } else { + break; + } + } + } + + if ('' === $this->inputBuffer && null === $this->input && isset($this->pipes[0])) { + fclose($this->pipes[0]); + unset($this->pipes[0]); + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function areOpen() + { + return (bool) $this->pipes; + } + + /** + * 创建一个新的 UnixPipes 实例 + * @param Process $process + * @param string|resource $input + * @return self + */ + public static function create(Process $process, $input) + { + return new static($process->isTty(), $process->isPty(), $input, $process->isOutputDisabled()); + } +} diff --git a/source/thinkphp/library/think/process/pipes/Windows.php b/source/thinkphp/library/think/process/pipes/Windows.php new file mode 100644 index 0000000..bba7e9b --- /dev/null +++ b/source/thinkphp/library/think/process/pipes/Windows.php @@ -0,0 +1,228 @@ + +// +---------------------------------------------------------------------- + +namespace think\process\pipes; + +use think\Process; + +class Windows extends Pipes +{ + + /** @var array */ + private $files = []; + /** @var array */ + private $fileHandles = []; + /** @var array */ + private $readBytes = [ + Process::STDOUT => 0, + Process::STDERR => 0, + ]; + /** @var bool */ + private $disableOutput; + + public function __construct($disableOutput, $input) + { + $this->disableOutput = (bool) $disableOutput; + + if (!$this->disableOutput) { + + $this->files = [ + Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'), + Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'), + ]; + foreach ($this->files as $offset => $file) { + $this->fileHandles[$offset] = fopen($this->files[$offset], 'rb'); + if (false === $this->fileHandles[$offset]) { + throw new \RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); + } + } + } + + if (is_resource($input)) { + $this->input = $input; + } else { + $this->inputBuffer = $input; + } + } + + public function __destruct() + { + $this->close(); + $this->removeFiles(); + } + + /** + * {@inheritdoc} + */ + public function getDescriptors() + { + if ($this->disableOutput) { + $nullstream = fopen('NUL', 'c'); + + return [ + ['pipe', 'r'], + $nullstream, + $nullstream, + ]; + } + + return [ + ['pipe', 'r'], + ['file', 'NUL', 'w'], + ['file', 'NUL', 'w'], + ]; + } + + /** + * {@inheritdoc} + */ + public function getFiles() + { + return $this->files; + } + + /** + * {@inheritdoc} + */ + public function readAndWrite($blocking, $close = false) + { + $this->write($blocking, $close); + + $read = []; + $fh = $this->fileHandles; + foreach ($fh as $type => $fileHandle) { + if (0 !== fseek($fileHandle, $this->readBytes[$type])) { + continue; + } + $data = ''; + $dataread = null; + while (!feof($fileHandle)) { + if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) { + $data .= $dataread; + } + } + if (0 < $length = strlen($data)) { + $this->readBytes[$type] += $length; + $read[$type] = $data; + } + + if (false === $dataread || (true === $close && feof($fileHandle) && '' === $data)) { + fclose($this->fileHandles[$type]); + unset($this->fileHandles[$type]); + } + } + + return $read; + } + + /** + * {@inheritdoc} + */ + public function areOpen() + { + return (bool) $this->pipes && (bool) $this->fileHandles; + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + foreach ($this->fileHandles as $handle) { + fclose($handle); + } + $this->fileHandles = []; + } + + /** + * 创建一个新的 WindowsPipes 实例。 + * @param Process $process + * @param $input + * @return self + */ + public static function create(Process $process, $input) + { + return new static($process->isOutputDisabled(), $input); + } + + /** + * 删除临时文件 + */ + private function removeFiles() + { + foreach ($this->files as $filename) { + if (file_exists($filename)) { + @unlink($filename); + } + } + $this->files = []; + } + + /** + * 写入到 stdin 输入 + * @param bool $blocking + * @param bool $close + */ + private function write($blocking, $close) + { + if (empty($this->pipes)) { + return; + } + + $this->unblock(); + + $r = null !== $this->input ? ['input' => $this->input] : null; + $w = isset($this->pipes[0]) ? [$this->pipes[0]] : null; + $e = null; + + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + if (!$this->hasSystemCallBeenInterrupted()) { + $this->pipes = []; + } + + return; + } + + if (0 === $n) { + return; + } + + if (null !== $w && 0 < count($r)) { + $data = ''; + while ($dataread = fread($r['input'], self::CHUNK_SIZE)) { + $data .= $dataread; + } + + $this->inputBuffer .= $data; + + if (false === $data || (true === $close && feof($r['input']) && '' === $data)) { + $this->input = null; + } + } + + if (null !== $w && 0 < count($w)) { + while (strlen($this->inputBuffer)) { + $written = fwrite($w[0], $this->inputBuffer, 2 << 18); + if ($written > 0) { + $this->inputBuffer = (string) substr($this->inputBuffer, $written); + } else { + break; + } + } + } + + if ('' === $this->inputBuffer && null === $this->input && isset($this->pipes[0])) { + fclose($this->pipes[0]); + unset($this->pipes[0]); + } + } +} diff --git a/source/thinkphp/library/think/response/Json.php b/source/thinkphp/library/think/response/Json.php new file mode 100644 index 0000000..c906bfc --- /dev/null +++ b/source/thinkphp/library/think/response/Json.php @@ -0,0 +1,51 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Response; + +class Json extends Response +{ + // 输出参数 + protected $options = [ + 'json_encode_param' => JSON_UNESCAPED_UNICODE, + ]; + + protected $contentType = 'application/json'; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + try { + // 返回JSON数据格式到客户端 包含状态信息 + $data = json_encode($data, $this->options['json_encode_param']); + + if ($data === false) { + throw new \InvalidArgumentException(json_last_error_msg()); + } + + return $data; + } catch (\Exception $e) { + if ($e->getPrevious()) { + throw $e->getPrevious(); + } + throw $e; + } + } + +} diff --git a/source/thinkphp/library/think/response/Jsonp.php b/source/thinkphp/library/think/response/Jsonp.php new file mode 100644 index 0000000..404bacb --- /dev/null +++ b/source/thinkphp/library/think/response/Jsonp.php @@ -0,0 +1,58 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Request; +use think\Response; + +class Jsonp extends Response +{ + // 输出参数 + protected $options = [ + 'var_jsonp_handler' => 'callback', + 'default_jsonp_handler' => 'jsonpReturn', + 'json_encode_param' => JSON_UNESCAPED_UNICODE, + ]; + + protected $contentType = 'application/javascript'; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + * @throws \Exception + */ + protected function output($data) + { + try { + // 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的,故使用Request来获取] + $var_jsonp_handler = Request::instance()->param($this->options['var_jsonp_handler'], ""); + $handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler']; + + $data = json_encode($data, $this->options['json_encode_param']); + + if ($data === false) { + throw new \InvalidArgumentException(json_last_error_msg()); + } + + $data = $handler . '(' . $data . ');'; + return $data; + } catch (\Exception $e) { + if ($e->getPrevious()) { + throw $e->getPrevious(); + } + throw $e; + } + } + +} diff --git a/source/thinkphp/library/think/response/Redirect.php b/source/thinkphp/library/think/response/Redirect.php new file mode 100644 index 0000000..91694cb --- /dev/null +++ b/source/thinkphp/library/think/response/Redirect.php @@ -0,0 +1,105 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Request; +use think\Response; +use think\Session; +use think\Url; + +class Redirect extends Response +{ + + protected $options = []; + + // URL参数 + protected $params = []; + + public function __construct($data = '', $code = 302, array $header = [], array $options = []) + { + parent::__construct($data, $code, $header, $options); + $this->cacheControl('no-cache,must-revalidate'); + } + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + */ + protected function output($data) + { + $this->header['Location'] = $this->getTargetUrl(); + return; + } + + /** + * 重定向传值(通过Session) + * @access protected + * @param string|array $name 变量名或者数组 + * @param mixed $value 值 + * @return $this + */ + public function with($name, $value = null) + { + if (is_array($name)) { + foreach ($name as $key => $val) { + Session::flash($key, $val); + } + } else { + Session::flash($name, $value); + } + return $this; + } + + /** + * 获取跳转地址 + * @return string + */ + public function getTargetUrl() + { + if (strpos($this->data, '://') || (0 === strpos($this->data, '/') && empty($this->params))) { + return $this->data; + } else { + return Url::build($this->data, $this->params); + } + } + + public function params($params = []) + { + $this->params = $params; + return $this; + } + + /** + * 记住当前url后跳转 + * @return $this + */ + public function remember() + { + Session::set('redirect_url', Request::instance()->url()); + return $this; + } + + /** + * 跳转到上次记住的url + * @return $this + */ + public function restore() + { + if (Session::has('redirect_url')) { + $this->data = Session::get('redirect_url'); + Session::delete('redirect_url'); + } + return $this; + } +} diff --git a/source/thinkphp/library/think/response/View.php b/source/thinkphp/library/think/response/View.php new file mode 100644 index 0000000..48f944a --- /dev/null +++ b/source/thinkphp/library/think/response/View.php @@ -0,0 +1,89 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Config; +use think\Response; +use think\View as ViewTemplate; + +class View extends Response +{ + // 输出参数 + protected $options = []; + protected $vars = []; + protected $replace = []; + protected $contentType = 'text/html'; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + */ + protected function output($data) + { + // 渲染模板输出 + return ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) + ->fetch($data, $this->vars, $this->replace); + } + + /** + * 获取视图变量 + * @access public + * @param string $name 模板变量 + * @return mixed + */ + public function getVars($name = null) + { + if (is_null($name)) { + return $this->vars; + } else { + return isset($this->vars[$name]) ? $this->vars[$name] : null; + } + } + + /** + * 模板变量赋值 + * @access public + * @param mixed $name 变量名 + * @param mixed $value 变量值 + * @return $this + */ + public function assign($name, $value = '') + { + if (is_array($name)) { + $this->vars = array_merge($this->vars, $name); + return $this; + } else { + $this->vars[$name] = $value; + } + return $this; + } + + /** + * 视图内容替换 + * @access public + * @param string|array $content 被替换内容(支持批量替换) + * @param string $replace 替换内容 + * @return $this + */ + public function replace($content, $replace = '') + { + if (is_array($content)) { + $this->replace = array_merge($this->replace, $content); + } else { + $this->replace[$content] = $replace; + } + return $this; + } + +} diff --git a/source/thinkphp/library/think/response/Xml.php b/source/thinkphp/library/think/response/Xml.php new file mode 100644 index 0000000..3bdc052 --- /dev/null +++ b/source/thinkphp/library/think/response/Xml.php @@ -0,0 +1,102 @@ + +// +---------------------------------------------------------------------- + +namespace think\response; + +use think\Collection; +use think\Model; +use think\Response; + +class Xml extends Response +{ + // 输出参数 + protected $options = [ + // 根节点名 + 'root_node' => 'think', + // 根节点属性 + 'root_attr' => '', + //数字索引的子节点名 + 'item_node' => 'item', + // 数字索引子节点key转换的属性名 + 'item_key' => 'id', + // 数据编码 + 'encoding' => 'utf-8', + ]; + + protected $contentType = 'text/xml'; + + /** + * 处理数据 + * @access protected + * @param mixed $data 要处理的数据 + * @return mixed + */ + protected function output($data) + { + // XML数据转换 + return $this->xmlEncode($data, $this->options['root_node'], $this->options['item_node'], $this->options['root_attr'], $this->options['item_key'], $this->options['encoding']); + } + + /** + * XML编码 + * @param mixed $data 数据 + * @param string $root 根节点名 + * @param string $item 数字索引的子节点名 + * @param string $attr 根节点属性 + * @param string $id 数字索引子节点key转换的属性名 + * @param string $encoding 数据编码 + * @return string + */ + protected function xmlEncode($data, $root, $item, $attr, $id, $encoding) + { + if (is_array($attr)) { + $array = []; + foreach ($attr as $key => $value) { + $array[] = "{$key}=\"{$value}\""; + } + $attr = implode(' ', $array); + } + $attr = trim($attr); + $attr = empty($attr) ? '' : " {$attr}"; + $xml = ""; + $xml .= "<{$root}{$attr}>"; + $xml .= $this->dataToXml($data, $item, $id); + $xml .= ""; + return $xml; + } + + /** + * 数据XML编码 + * @param mixed $data 数据 + * @param string $item 数字索引时的节点名称 + * @param string $id 数字索引key转换为的属性名 + * @return string + */ + protected function dataToXml($data, $item, $id) + { + $xml = $attr = ''; + + if ($data instanceof Collection || $data instanceof Model) { + $data = $data->toArray(); + } + + foreach ($data as $key => $val) { + if (is_numeric($key)) { + $id && $attr = " {$id}=\"{$key}\""; + $key = $item; + } + $xml .= "<{$key}{$attr}>"; + $xml .= (is_array($val) || is_object($val)) ? $this->dataToXml($val, $item, $id) : $val; + $xml .= ""; + } + return $xml; + } +} diff --git a/source/thinkphp/library/think/session/driver/Memcache.php b/source/thinkphp/library/think/session/driver/Memcache.php new file mode 100644 index 0000000..877d7bd --- /dev/null +++ b/source/thinkphp/library/think/session/driver/Memcache.php @@ -0,0 +1,118 @@ + +// +---------------------------------------------------------------------- + +namespace think\session\driver; + +use SessionHandler; +use think\Exception; + +class Memcache extends SessionHandler +{ + protected $handler = null; + protected $config = [ + 'host' => '127.0.0.1', // memcache主机 + 'port' => 11211, // memcache端口 + 'expire' => 3600, // session有效期 + 'timeout' => 0, // 连接超时时间(单位:毫秒) + 'persistent' => true, // 长连接 + 'session_name' => '', // memcache key前缀 + ]; + + public function __construct($config = []) + { + $this->config = array_merge($this->config, $config); + } + + /** + * 打开Session + * @access public + * @param string $savePath + * @param mixed $sessName + */ + public function open($savePath, $sessName) + { + // 检测php环境 + if (!extension_loaded('memcache')) { + throw new Exception('not support:memcache'); + } + $this->handler = new \Memcache; + // 支持集群 + $hosts = explode(',', $this->config['host']); + $ports = explode(',', $this->config['port']); + if (empty($ports[0])) { + $ports[0] = 11211; + } + // 建立连接 + foreach ((array) $hosts as $i => $host) { + $port = isset($ports[$i]) ? $ports[$i] : $ports[0]; + $this->config['timeout'] > 0 ? + $this->handler->addServer($host, $port, $this->config['persistent'], 1, $this->config['timeout']) : + $this->handler->addServer($host, $port, $this->config['persistent'], 1); + } + return true; + } + + /** + * 关闭Session + * @access public + */ + public function close() + { + $this->gc(ini_get('session.gc_maxlifetime')); + $this->handler->close(); + $this->handler = null; + return true; + } + + /** + * 读取Session + * @access public + * @param string $sessID + */ + public function read($sessID) + { + return (string) $this->handler->get($this->config['session_name'] . $sessID); + } + + /** + * 写入Session + * @access public + * @param string $sessID + * @param String $sessData + * @return bool + */ + public function write($sessID, $sessData) + { + return $this->handler->set($this->config['session_name'] . $sessID, $sessData, 0, $this->config['expire']); + } + + /** + * 删除Session + * @access public + * @param string $sessID + * @return bool + */ + public function destroy($sessID) + { + return $this->handler->delete($this->config['session_name'] . $sessID); + } + + /** + * Session 垃圾回收 + * @access public + * @param string $sessMaxLifeTime + * @return true + */ + public function gc($sessMaxLifeTime) + { + return true; + } +} diff --git a/source/thinkphp/library/think/session/driver/Memcached.php b/source/thinkphp/library/think/session/driver/Memcached.php new file mode 100644 index 0000000..2994a07 --- /dev/null +++ b/source/thinkphp/library/think/session/driver/Memcached.php @@ -0,0 +1,126 @@ + +// +---------------------------------------------------------------------- + +namespace think\session\driver; + +use SessionHandler; +use think\Exception; + +class Memcached extends SessionHandler +{ + protected $handler = null; + protected $config = [ + 'host' => '127.0.0.1', // memcache主机 + 'port' => 11211, // memcache端口 + 'expire' => 3600, // session有效期 + 'timeout' => 0, // 连接超时时间(单位:毫秒) + 'session_name' => '', // memcache key前缀 + 'username' => '', //账号 + 'password' => '', //密码 + ]; + + public function __construct($config = []) + { + $this->config = array_merge($this->config, $config); + } + + /** + * 打开Session + * @access public + * @param string $savePath + * @param mixed $sessName + */ + public function open($savePath, $sessName) + { + // 检测php环境 + if (!extension_loaded('memcached')) { + throw new Exception('not support:memcached'); + } + $this->handler = new \Memcached; + // 设置连接超时时间(单位:毫秒) + if ($this->config['timeout'] > 0) { + $this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->config['timeout']); + } + // 支持集群 + $hosts = explode(',', $this->config['host']); + $ports = explode(',', $this->config['port']); + if (empty($ports[0])) { + $ports[0] = 11211; + } + // 建立连接 + $servers = []; + foreach ((array) $hosts as $i => $host) { + $servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1]; + } + $this->handler->addServers($servers); + if ('' != $this->config['username']) { + $this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + $this->handler->setSaslAuthData($this->config['username'], $this->config['password']); + } + return true; + } + + /** + * 关闭Session + * @access public + */ + public function close() + { + $this->gc(ini_get('session.gc_maxlifetime')); + $this->handler->quit(); + $this->handler = null; + return true; + } + + /** + * 读取Session + * @access public + * @param string $sessID + */ + public function read($sessID) + { + return (string) $this->handler->get($this->config['session_name'] . $sessID); + } + + /** + * 写入Session + * @access public + * @param string $sessID + * @param String $sessData + * @return bool + */ + public function write($sessID, $sessData) + { + return $this->handler->set($this->config['session_name'] . $sessID, $sessData, $this->config['expire']); + } + + /** + * 删除Session + * @access public + * @param string $sessID + * @return bool + */ + public function destroy($sessID) + { + return $this->handler->delete($this->config['session_name'] . $sessID); + } + + /** + * Session 垃圾回收 + * @access public + * @param string $sessMaxLifeTime + * @return true + */ + public function gc($sessMaxLifeTime) + { + return true; + } +} diff --git a/source/thinkphp/library/think/session/driver/Redis.php b/source/thinkphp/library/think/session/driver/Redis.php new file mode 100644 index 0000000..8d05126 --- /dev/null +++ b/source/thinkphp/library/think/session/driver/Redis.php @@ -0,0 +1,128 @@ + +// +---------------------------------------------------------------------- + +namespace think\session\driver; + +use SessionHandler; +use think\Exception; + +class Redis extends SessionHandler +{ + /** @var \Redis */ + protected $handler = null; + protected $config = [ + 'host' => '127.0.0.1', // redis主机 + 'port' => 6379, // redis端口 + 'password' => '', // 密码 + 'select' => 0, // 操作库 + 'expire' => 3600, // 有效期(秒) + 'timeout' => 0, // 超时时间(秒) + 'persistent' => true, // 是否长连接 + 'session_name' => '', // sessionkey前缀 + ]; + + public function __construct($config = []) + { + $this->config = array_merge($this->config, $config); + } + + /** + * 打开Session + * @access public + * @param string $savePath + * @param mixed $sessName + * @return bool + * @throws Exception + */ + public function open($savePath, $sessName) + { + // 检测php环境 + if (!extension_loaded('redis')) { + throw new Exception('not support:redis'); + } + $this->handler = new \Redis; + + // 建立连接 + $func = $this->config['persistent'] ? 'pconnect' : 'connect'; + $this->handler->$func($this->config['host'], $this->config['port'], $this->config['timeout']); + + if ('' != $this->config['password']) { + $this->handler->auth($this->config['password']); + } + + if (0 != $this->config['select']) { + $this->handler->select($this->config['select']); + } + + return true; + } + + /** + * 关闭Session + * @access public + */ + public function close() + { + $this->gc(ini_get('session.gc_maxlifetime')); + $this->handler->close(); + $this->handler = null; + return true; + } + + /** + * 读取Session + * @access public + * @param string $sessID + * @return string + */ + public function read($sessID) + { + return (string) $this->handler->get($this->config['session_name'] . $sessID); + } + + /** + * 写入Session + * @access public + * @param string $sessID + * @param String $sessData + * @return bool + */ + public function write($sessID, $sessData) + { + if ($this->config['expire'] > 0) { + return $this->handler->setex($this->config['session_name'] . $sessID, $this->config['expire'], $sessData); + } else { + return $this->handler->set($this->config['session_name'] . $sessID, $sessData); + } + } + + /** + * 删除Session + * @access public + * @param string $sessID + * @return bool + */ + public function destroy($sessID) + { + return $this->handler->delete($this->config['session_name'] . $sessID) > 0; + } + + /** + * Session 垃圾回收 + * @access public + * @param string $sessMaxLifeTime + * @return bool + */ + public function gc($sessMaxLifeTime) + { + return true; + } +} diff --git a/source/thinkphp/library/think/template/TagLib.php b/source/thinkphp/library/think/template/TagLib.php new file mode 100644 index 0000000..c5b72f9 --- /dev/null +++ b/source/thinkphp/library/think/template/TagLib.php @@ -0,0 +1,334 @@ + +// +---------------------------------------------------------------------- + +namespace think\template; + +use think\Exception; + +/** + * ThinkPHP标签库TagLib解析基类 + * @category Think + * @package Think + * @subpackage Template + * @author liu21st + */ +class TagLib +{ + + /** + * 标签库定义XML文件 + * @var string + * @access protected + */ + protected $xml = ''; + protected $tags = []; // 标签定义 + /** + * 标签库名称 + * @var string + * @access protected + */ + protected $tagLib = ''; + + /** + * 标签库标签列表 + * @var array + * @access protected + */ + protected $tagList = []; + + /** + * 标签库分析数组 + * @var array + * @access protected + */ + protected $parse = []; + + /** + * 标签库是否有效 + * @var bool + * @access protected + */ + protected $valid = false; + + /** + * 当前模板对象 + * @var object + * @access protected + */ + protected $tpl; + + protected $comparison = [' nheq ' => ' !== ', ' heq ' => ' === ', ' neq ' => ' != ', ' eq ' => ' == ', ' egt ' => ' >= ', ' gt ' => ' > ', ' elt ' => ' <= ', ' lt ' => ' < ']; + + /** + * 构造函数 + * @access public + * @param \stdClass $template 模板引擎对象 + */ + public function __construct($template) + { + $this->tpl = $template; + } + + /** + * 按签标库替换页面中的标签 + * @access public + * @param string $content 模板内容 + * @param string $lib 标签库名 + * @return void + */ + public function parseTag(&$content, $lib = '') + { + $tags = []; + $lib = $lib ? strtolower($lib) . ':' : ''; + foreach ($this->tags as $name => $val) { + $close = !isset($val['close']) || $val['close'] ? 1 : 0; + $tags[$close][$lib . $name] = $name; + if (isset($val['alias'])) { + // 别名设置 + $array = (array) $val['alias']; + foreach (explode(',', $array[0]) as $v) { + $tags[$close][$lib . $v] = $name; + } + } + } + + // 闭合标签 + if (!empty($tags[1])) { + $nodes = []; + $regex = $this->getRegex(array_keys($tags[1]), 1); + if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { + $right = []; + foreach ($matches as $match) { + if ('' == $match[1][0]) { + $name = strtolower($match[2][0]); + // 如果有没闭合的标签头则取出最后一个 + if (!empty($right[$name])) { + // $match[0][1]为标签结束符在模板中的位置 + $nodes[$match[0][1]] = [ + 'name' => $name, + 'begin' => array_pop($right[$name]), // 标签开始符 + 'end' => $match[0], // 标签结束符 + ]; + } + } else { + // 标签头压入栈 + $right[strtolower($match[1][0])][] = $match[0]; + } + } + unset($right, $matches); + // 按标签在模板中的位置从后向前排序 + krsort($nodes); + } + + $break = ''; + if ($nodes) { + $beginArray = []; + // 标签替换 从后向前 + foreach ($nodes as $pos => $node) { + // 对应的标签名 + $name = $tags[1][$node['name']]; + $alias = $lib . $name != $node['name'] ? ($lib ? strstr($node['name'], $lib) : $node['name']) : ''; + // 解析标签属性 + $attrs = $this->parseAttr($node['begin'][0], $name, $alias); + $method = 'tag' . $name; + // 读取标签库中对应的标签内容 replace[0]用来替换标签头,replace[1]用来替换标签尾 + $replace = explode($break, $this->$method($attrs, $break)); + if (count($replace) > 1) { + while ($beginArray) { + $begin = end($beginArray); + // 判断当前标签尾的位置是否在栈中最后一个标签头的后面,是则为子标签 + if ($node['end'][1] > $begin['pos']) { + break; + } else { + // 不为子标签时,取出栈中最后一个标签头 + $begin = array_pop($beginArray); + // 替换标签头部 + $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']); + } + } + // 替换标签尾部 + $content = substr_replace($content, $replace[1], $node['end'][1], strlen($node['end'][0])); + // 把标签头压入栈 + $beginArray[] = ['pos' => $node['begin'][1], 'len' => strlen($node['begin'][0]), 'str' => $replace[0]]; + } + } + while ($beginArray) { + $begin = array_pop($beginArray); + // 替换标签头部 + $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']); + } + } + } + // 自闭合标签 + if (!empty($tags[0])) { + $regex = $this->getRegex(array_keys($tags[0]), 0); + $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) { + // 对应的标签名 + $name = $tags[0][strtolower($matches[1])]; + $alias = $lib . $name != $matches[1] ? ($lib ? strstr($matches[1], $lib) : $matches[1]) : ''; + // 解析标签属性 + $attrs = $this->parseAttr($matches[0], $name, $alias); + $method = 'tag' . $name; + return $this->$method($attrs, ''); + }, $content); + } + return; + } + + /** + * 按标签生成正则 + * @access private + * @param array|string $tags 标签名 + * @param boolean $close 是否为闭合标签 + * @return string + */ + public function getRegex($tags, $close) + { + $begin = $this->tpl->config('taglib_begin'); + $end = $this->tpl->config('taglib_end'); + $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false; + $tagName = is_array($tags) ? implode('|', $tags) : $tags; + if ($single) { + if ($close) { + // 如果是闭合标签 + $regex = $begin . '(?:(' . $tagName . ')\b(?>[^' . $end . ']*)|\/(' . $tagName . '))' . $end; + } else { + $regex = $begin . '(' . $tagName . ')\b(?>[^' . $end . ']*)' . $end; + } + } else { + if ($close) { + // 如果是闭合标签 + $regex = $begin . '(?:(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)|\/(' . $tagName . '))' . $end; + } else { + $regex = $begin . '(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)' . $end; + } + } + return '/' . $regex . '/is'; + } + + /** + * 分析标签属性 正则方式 + * @access public + * @param string $str 标签属性字符串 + * @param string $name 标签名 + * @param string $alias 别名 + * @return array + */ + public function parseAttr($str, $name, $alias = '') + { + $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; + $result = []; + if (preg_match_all($regex, $str, $matches)) { + foreach ($matches['name'] as $key => $val) { + $result[$val] = $matches['value'][$key]; + } + if (!isset($this->tags[$name])) { + // 检测是否存在别名定义 + foreach ($this->tags as $key => $val) { + if (isset($val['alias'])) { + $array = (array) $val['alias']; + if (in_array($name, explode(',', $array[0]))) { + $tag = $val; + $type = !empty($array[1]) ? $array[1] : 'type'; + $result[$type] = $name; + break; + } + } + } + } else { + $tag = $this->tags[$name]; + // 设置了标签别名 + if (!empty($alias) && isset($tag['alias'])) { + $type = !empty($tag['alias'][1]) ? $tag['alias'][1] : 'type'; + $result[$type] = $alias; + } + } + if (!empty($tag['must'])) { + $must = explode(',', $tag['must']); + foreach ($must as $name) { + if (!isset($result[$name])) { + throw new Exception('tag attr must:' . $name); + } + } + } + } else { + // 允许直接使用表达式的标签 + if (!empty($this->tags[$name]['expression'])) { + static $_taglibs; + if (!isset($_taglibs[$name])) { + $_taglibs[$name][0] = strlen($this->tpl->config('taglib_begin_origin') . $name); + $_taglibs[$name][1] = strlen($this->tpl->config('taglib_end_origin')); + } + $result['expression'] = substr($str, $_taglibs[$name][0], -$_taglibs[$name][1]); + // 清除自闭合标签尾部/ + $result['expression'] = rtrim($result['expression'], '/'); + $result['expression'] = trim($result['expression']); + } elseif (empty($this->tags[$name]) || !empty($this->tags[$name]['attr'])) { + throw new Exception('tag error:' . $name); + } + } + return $result; + } + + /** + * 解析条件表达式 + * @access public + * @param string $condition 表达式标签内容 + * @return string + */ + public function parseCondition($condition) + { + if (strpos($condition, ':')) { + $condition = ' ' . substr(strstr($condition, ':'), 1); + } + $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition); + $this->tpl->parseVar($condition); + // $this->tpl->parseVarFunction($condition); // XXX: 此句能解析表达式中用|分隔的函数,但表达式中如果有|、||这样的逻辑运算就产生了歧异 + return $condition; + } + + /** + * 自动识别构建变量 + * @access public + * @param string $name 变量描述 + * @return string + */ + public function autoBuildVar(&$name) + { + $flag = substr($name, 0, 1); + if (':' == $flag) { + // 以:开头为函数调用,解析前去掉: + $name = substr($name, 1); + } elseif ('$' != $flag && preg_match('/[a-zA-Z_]/', $flag)) { + // XXX: 这句的写法可能还需要改进 + // 常量不需要解析 + if (defined($name)) { + return $name; + } + // 不以$开头并且也不是常量,自动补上$前缀 + $name = '$' . $name; + } + $this->tpl->parseVar($name); + $this->tpl->parseVarFunction($name); + return $name; + } + + /** + * 获取标签列表 + * @access public + * @return array + */ + // 获取标签定义 + public function getTags() + { + return $this->tags; + } +} diff --git a/source/thinkphp/library/think/template/driver/File.php b/source/thinkphp/library/think/template/driver/File.php new file mode 100644 index 0000000..a9a86bf --- /dev/null +++ b/source/thinkphp/library/think/template/driver/File.php @@ -0,0 +1,74 @@ + +// +---------------------------------------------------------------------- + +namespace think\template\driver; + +use think\Exception; + +class File +{ + protected $cacheFile; + + /** + * 写入编译缓存 + * @param string $cacheFile 缓存的文件名 + * @param string $content 缓存的内容 + * @return void|array + */ + public function write($cacheFile, $content) + { + // 检测模板目录 + $dir = dirname($cacheFile); + if (!is_dir($dir)) { + mkdir($dir, 0755, true); + } + // 生成模板缓存文件 + if (false === file_put_contents($cacheFile, $content)) { + throw new Exception('cache write error:' . $cacheFile, 11602); + } + } + + /** + * 读取编译编译 + * @param string $cacheFile 缓存的文件名 + * @param array $vars 变量数组 + * @return void + */ + public function read($cacheFile, $vars = []) + { + $this->cacheFile = $cacheFile; + if (!empty($vars) && is_array($vars)) { + // 模板阵列变量分解成为独立变量 + extract($vars, EXTR_OVERWRITE); + } + //载入模版缓存文件 + include $this->cacheFile; + } + + /** + * 检查编译缓存是否有效 + * @param string $cacheFile 缓存的文件名 + * @param int $cacheTime 缓存时间 + * @return boolean + */ + public function check($cacheFile, $cacheTime) + { + // 缓存文件不存在, 直接返回false + if (!file_exists($cacheFile)) { + return false; + } + if (0 != $cacheTime && $_SERVER['REQUEST_TIME'] > filemtime($cacheFile) + $cacheTime) { + // 缓存是否在有效期 + return false; + } + return true; + } +} diff --git a/source/thinkphp/library/think/template/taglib/Cx.php b/source/thinkphp/library/think/template/taglib/Cx.php new file mode 100644 index 0000000..31e0698 --- /dev/null +++ b/source/thinkphp/library/think/template/taglib/Cx.php @@ -0,0 +1,673 @@ + +// +---------------------------------------------------------------------- + +namespace think\template\taglib; + +use think\template\TagLib; + +/** + * CX标签库解析类 + * @category Think + * @package Think + * @subpackage Driver.Taglib + * @author liu21st + */ +class Cx extends Taglib +{ + + // 标签定义 + protected $tags = [ + // 标签定义: attr 属性列表 close 是否闭合(0 或者1 默认1) alias 标签别名 level 嵌套层次 + 'php' => ['attr' => ''], + 'volist' => ['attr' => 'name,id,offset,length,key,mod', 'alias' => 'iterate'], + 'foreach' => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true], + 'if' => ['attr' => 'condition', 'expression' => true], + 'elseif' => ['attr' => 'condition', 'close' => 0, 'expression' => true], + 'else' => ['attr' => '', 'close' => 0], + 'switch' => ['attr' => 'name', 'expression' => true], + 'case' => ['attr' => 'value,break', 'expression' => true], + 'default' => ['attr' => '', 'close' => 0], + 'compare' => ['attr' => 'name,value,type', 'alias' => ['eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq', 'type']], + 'range' => ['attr' => 'name,value,type', 'alias' => ['in,notin,between,notbetween', 'type']], + 'empty' => ['attr' => 'name'], + 'notempty' => ['attr' => 'name'], + 'present' => ['attr' => 'name'], + 'notpresent' => ['attr' => 'name'], + 'defined' => ['attr' => 'name'], + 'notdefined' => ['attr' => 'name'], + 'load' => ['attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => ['import,css,js', 'type']], + 'assign' => ['attr' => 'name,value', 'close' => 0], + 'define' => ['attr' => 'name,value', 'close' => 0], + 'for' => ['attr' => 'start,end,name,comparison,step'], + 'url' => ['attr' => 'link,vars,suffix,domain', 'close' => 0, 'expression' => true], + 'function' => ['attr' => 'name,vars,use,call'], + ]; + + /** + * php标签解析 + * 格式: + * {php}echo $name{/php} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagPhp($tag, $content) + { + $parseStr = ''; + return $parseStr; + } + + /** + * volist标签解析 循环输出数据集 + * 格式: + * {volist name="userList" id="user" empty=""} + * {user.username} + * {user.email} + * {/volist} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string|void + */ + public function tagVolist($tag, $content) + { + $name = $tag['name']; + $id = $tag['id']; + $empty = isset($tag['empty']) ? $tag['empty'] : ''; + $key = !empty($tag['key']) ? $tag['key'] : 'i'; + $mod = isset($tag['mod']) ? $tag['mod'] : '2'; + $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; + $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; + // 允许使用函数设定数据集 {$vo.name} + $parseStr = 'autoBuildVar($name); + $parseStr .= '$_result=' . $name . ';'; + $name = '$_result'; + } else { + $name = $this->autoBuildVar($name); + } + + $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): $' . $key . ' = 0;'; + // 设置了输出数组长度 + if (0 != $offset || 'null' != $length) { + $parseStr .= '$__LIST__ = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; + } else { + $parseStr .= ' $__LIST__ = ' . $name . ';'; + } + $parseStr .= 'if( count($__LIST__)==0 ) : echo "' . $empty . '" ;'; + $parseStr .= 'else: '; + $parseStr .= 'foreach($__LIST__ as $key=>$' . $id . '): '; + $parseStr .= '$mod = ($' . $key . ' % ' . $mod . ' );'; + $parseStr .= '++$' . $key . ';?>'; + $parseStr .= $content; + $parseStr .= ''; + + if (!empty($parseStr)) { + return $parseStr; + } + return; + } + + /** + * foreach标签解析 循环输出数据集 + * 格式: + * {foreach name="userList" id="user" key="key" index="i" mod="2" offset="3" length="5" empty=""} + * {user.username} + * {/foreach} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string|void + */ + public function tagForeach($tag, $content) + { + // 直接使用表达式 + if (!empty($tag['expression'])) { + $expression = ltrim(rtrim($tag['expression'], ')'), '('); + $expression = $this->autoBuildVar($expression); + $parseStr = ''; + $parseStr .= $content; + $parseStr .= ''; + return $parseStr; + } + $name = $tag['name']; + $key = !empty($tag['key']) ? $tag['key'] : 'key'; + $item = !empty($tag['id']) ? $tag['id'] : $tag['item']; + $empty = isset($tag['empty']) ? $tag['empty'] : ''; + $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0; + $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null'; + + $parseStr = 'autoBuildVar($name); + $parseStr .= $var . '=' . $name . '; '; + $name = $var; + } else { + $name = $this->autoBuildVar($name); + } + $parseStr .= 'if(is_array(' . $name . ') || ' . $name . ' instanceof \think\Collection || ' . $name . ' instanceof \think\Paginator): '; + // 设置了输出数组长度 + if (0 != $offset || 'null' != $length) { + if (!isset($var)) { + $var = '$_' . uniqid(); + } + $parseStr .= $var . ' = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); '; + } else { + $var = &$name; + } + + $parseStr .= 'if( count(' . $var . ')==0 ) : echo "' . $empty . '" ;'; + $parseStr .= 'else: '; + + // 设置了索引项 + if (isset($tag['index'])) { + $index = $tag['index']; + $parseStr .= '$' . $index . '=0; '; + } + $parseStr .= 'foreach(' . $var . ' as $' . $key . '=>$' . $item . '): '; + // 设置了索引项 + if (isset($tag['index'])) { + $index = $tag['index']; + if (isset($tag['mod'])) { + $mod = (int) $tag['mod']; + $parseStr .= '$mod = ($' . $index . ' % ' . $mod . '); '; + } + $parseStr .= '++$' . $index . '; '; + } + $parseStr .= '?>'; + // 循环体中的内容 + $parseStr .= $content; + $parseStr .= ''; + + if (!empty($parseStr)) { + return $parseStr; + } + return; + } + + /** + * if标签解析 + * 格式: + * {if condition=" $a eq 1"} + * {elseif condition="$a eq 2" /} + * {else /} + * {/if} + * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || && + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagIf($tag, $content) + { + $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; + $condition = $this->parseCondition($condition); + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * elseif标签解析 + * 格式:见if标签 + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagElseif($tag, $content) + { + $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition']; + $condition = $this->parseCondition($condition); + $parseStr = ''; + return $parseStr; + } + + /** + * else标签解析 + * 格式:见if标签 + * @access public + * @param array $tag 标签属性 + * @return string + */ + public function tagElse($tag) + { + $parseStr = ''; + return $parseStr; + } + + /** + * switch标签解析 + * 格式: + * {switch name="a.name"} + * {case value="1" break="false"}1{/case} + * {case value="2" }2{/case} + * {default /}other + * {/switch} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagSwitch($tag, $content) + { + $name = !empty($tag['expression']) ? $tag['expression'] : $tag['name']; + $name = $this->autoBuildVar($name); + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * case标签解析 需要配合switch才有效 + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagCase($tag, $content) + { + $value = isset($tag['expression']) ? $tag['expression'] : $tag['value']; + $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($value); + $value = 'case ' . $value . ':'; + } elseif (strpos($value, '|')) { + $values = explode('|', $value); + $value = ''; + foreach ($values as $val) { + $value .= 'case "' . addslashes($val) . '":'; + } + } else { + $value = 'case "' . $value . '":'; + } + $parseStr = '' . $content; + $isBreak = isset($tag['break']) ? $tag['break'] : ''; + if ('' == $isBreak || $isBreak) { + $parseStr .= ''; + } + return $parseStr; + } + + /** + * default标签解析 需要配合switch才有效 + * 使用: {default /}ddfdf + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagDefault($tag) + { + $parseStr = ''; + return $parseStr; + } + + /** + * compare标签解析 + * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq + * 格式: {compare name="" type="eq" value="" }content{/compare} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagCompare($tag, $content) + { + $name = $tag['name']; + $value = $tag['value']; + $type = isset($tag['type']) ? $tag['type'] : 'eq'; // 比较类型 + $name = $this->autoBuildVar($name); + $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($value); + } else { + $value = '\'' . $value . '\''; + } + switch ($type) { + case 'equal': + $type = 'eq'; + break; + case 'notequal': + $type = 'neq'; + break; + } + $type = $this->parseCondition(' ' . $type . ' '); + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * range标签解析 + * 如果某个变量存在于某个范围 则输出内容 type= in 表示在范围内 否则表示在范围外 + * 格式: {range name="var|function" value="val" type='in|notin' }content{/range} + * example: {range name="a" value="1,2,3" type='in' }content{/range} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagRange($tag, $content) + { + $name = $tag['name']; + $value = $tag['value']; + $type = isset($tag['type']) ? $tag['type'] : 'in'; // 比较类型 + + $name = $this->autoBuildVar($name); + $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($value); + $str = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')'; + } else { + $value = '"' . $value . '"'; + $str = 'explode(\',\',' . $value . ')'; + } + if ('between' == $type) { + $parseStr = '= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . ''; + } elseif ('notbetween' == $type) { + $parseStr = '$_RANGE_VAR_[1]):?>' . $content . ''; + } else { + $fun = ('in' == $type) ? 'in_array' : '!in_array'; + $parseStr = '' . $content . ''; + } + return $parseStr; + } + + /** + * present标签解析 + * 如果某个变量已经设置 则输出内容 + * 格式: {present name="" }content{/present} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagPresent($tag, $content) + { + $name = $tag['name']; + $name = $this->autoBuildVar($name); + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * notpresent标签解析 + * 如果某个变量没有设置,则输出内容 + * 格式: {notpresent name="" }content{/notpresent} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagNotpresent($tag, $content) + { + $name = $tag['name']; + $name = $this->autoBuildVar($name); + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * empty标签解析 + * 如果某个变量为empty 则输出内容 + * 格式: {empty name="" }content{/empty} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagEmpty($tag, $content) + { + $name = $tag['name']; + $name = $this->autoBuildVar($name); + $parseStr = 'isEmpty())): ?>' . $content . ''; + return $parseStr; + } + + /** + * notempty标签解析 + * 如果某个变量不为empty 则输出内容 + * 格式: {notempty name="" }content{/notempty} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagNotempty($tag, $content) + { + $name = $tag['name']; + $name = $this->autoBuildVar($name); + $parseStr = 'isEmpty()))): ?>' . $content . ''; + return $parseStr; + } + + /** + * 判断是否已经定义了该常量 + * {defined name='TXT'}已定义{/defined} + * @param array $tag + * @param string $content + * @return string + */ + public function tagDefined($tag, $content) + { + $name = $tag['name']; + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * 判断是否没有定义了该常量 + * {notdefined name='TXT'}已定义{/notdefined} + * @param array $tag + * @param string $content + * @return string + */ + public function tagNotdefined($tag, $content) + { + $name = $tag['name']; + $parseStr = '' . $content . ''; + return $parseStr; + } + + /** + * load 标签解析 {load file="/static/js/base.js" /} + * 格式:{load file="/static/css/base.css" /} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagLoad($tag, $content) + { + $file = isset($tag['file']) ? $tag['file'] : $tag['href']; + $type = isset($tag['type']) ? strtolower($tag['type']) : ''; + $parseStr = ''; + $endStr = ''; + // 判断是否存在加载条件 允许使用函数判断(默认为isset) + if (isset($tag['value'])) { + $name = $tag['value']; + $name = $this->autoBuildVar($name); + $name = 'isset(' . $name . ')'; + $parseStr .= ''; + $endStr = ''; + } + + // 文件方式导入 + $array = explode(',', $file); + foreach ($array as $val) { + $type = strtolower(substr(strrchr($val, '.'), 1)); + switch ($type) { + case 'js': + $parseStr .= ''; + break; + case 'css': + $parseStr .= ''; + break; + case 'php': + $parseStr .= ''; + break; + } + } + return $parseStr . $endStr; + } + + /** + * assign标签解析 + * 在模板中给某个变量赋值 支持变量赋值 + * 格式: {assign name="" value="" /} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagAssign($tag, $content) + { + $name = $this->autoBuildVar($tag['name']); + $flag = substr($tag['value'], 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($tag['value']); + } else { + $value = '\'' . $tag['value'] . '\''; + } + $parseStr = ''; + return $parseStr; + } + + /** + * define标签解析 + * 在模板中定义常量 支持变量赋值 + * 格式: {define name="" value="" /} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagDefine($tag, $content) + { + $name = '\'' . $tag['name'] . '\''; + $flag = substr($tag['value'], 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($tag['value']); + } else { + $value = '\'' . $tag['value'] . '\''; + } + $parseStr = ''; + return $parseStr; + } + + /** + * for标签解析 + * 格式: + * {for start="" end="" comparison="" step="" name=""} + * content + * {/for} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagFor($tag, $content) + { + //设置默认值 + $start = 0; + $end = 0; + $step = 1; + $comparison = 'lt'; + $name = 'i'; + $rand = rand(); //添加随机数,防止嵌套变量冲突 + //获取属性 + foreach ($tag as $key => $value) { + $value = trim($value); + $flag = substr($value, 0, 1); + if ('$' == $flag || ':' == $flag) { + $value = $this->autoBuildVar($value); + } + + switch ($key) { + case 'start': + $start = $value; + break; + case 'end': + $end = $value; + break; + case 'step': + $step = $value; + break; + case 'comparison': + $comparison = $value; + break; + case 'name': + $name = $value; + break; + } + } + + $parseStr = 'parseCondition('$' . $name . ' ' . $comparison . ' $__FOR_END_' . $rand . '__') . ';$' . $name . '+=' . $step . '){ ?>'; + $parseStr .= $content; + $parseStr .= ''; + return $parseStr; + } + + /** + * url函数的tag标签 + * 格式:{url link="模块/控制器/方法" vars="参数" suffix="true或者false 是否带有后缀" domain="true或者false 是否携带域名" /} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagUrl($tag, $content) + { + $url = isset($tag['link']) ? $tag['link'] : ''; + $vars = isset($tag['vars']) ? $tag['vars'] : ''; + $suffix = isset($tag['suffix']) ? $tag['suffix'] : 'true'; + $domain = isset($tag['domain']) ? $tag['domain'] : 'false'; + return ''; + } + + /** + * function标签解析 匿名函数,可实现递归 + * 使用: + * {function name="func" vars="$data" call="$list" use="&$a,&$b"} + * {if is_array($data)} + * {foreach $data as $val} + * {~func($val) /} + * {/foreach} + * {else /} + * {$data} + * {/if} + * {/function} + * @access public + * @param array $tag 标签属性 + * @param string $content 标签内容 + * @return string + */ + public function tagFunction($tag, $content) + { + $name = !empty($tag['name']) ? $tag['name'] : 'func'; + $vars = !empty($tag['vars']) ? $tag['vars'] : ''; + $call = !empty($tag['call']) ? $tag['call'] : ''; + $use = ['&$' . $name]; + if (!empty($tag['use'])) { + foreach (explode(',', $tag['use']) as $val) { + $use[] = '&' . ltrim(trim($val), '&'); + } + } + $parseStr = '' . $content . '' : '?>'; + return $parseStr; + } +} diff --git a/source/thinkphp/library/think/view/driver/Php.php b/source/thinkphp/library/think/view/driver/Php.php new file mode 100644 index 0000000..f594a43 --- /dev/null +++ b/source/thinkphp/library/think/view/driver/Php.php @@ -0,0 +1,160 @@ + +// +---------------------------------------------------------------------- + +namespace think\view\driver; + +use think\App; +use think\exception\TemplateNotFoundException; +use think\Loader; +use think\Log; +use think\Request; + +class Php +{ + // 模板引擎参数 + protected $config = [ + // 视图基础目录(集中式) + 'view_base' => '', + // 模板起始路径 + 'view_path' => '', + // 模板文件后缀 + 'view_suffix' => 'php', + // 模板文件名分隔符 + 'view_depr' => DS, + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, + ]; + protected $template; + protected $content; + + public function __construct($config = []) + { + $this->config = array_merge($this->config, $config); + } + + /** + * 检测是否存在模板文件 + * @access public + * @param string $template 模板文件或者模板规则 + * @return bool + */ + public function exists($template) + { + if ('' == pathinfo($template, PATHINFO_EXTENSION)) { + // 获取模板文件名 + $template = $this->parseTemplate($template); + } + return is_file($template); + } + + /** + * 渲染模板文件 + * @access public + * @param string $template 模板文件 + * @param array $data 模板变量 + * @return void + */ + public function fetch($template, $data = []) + { + if ('' == pathinfo($template, PATHINFO_EXTENSION)) { + // 获取模板文件名 + $template = $this->parseTemplate($template); + } + // 模板不存在 抛出异常 + if (!is_file($template)) { + throw new TemplateNotFoundException('template not exists:' . $template, $template); + } + $this->template = $template; + // 记录视图信息 + App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); + + extract($data, EXTR_OVERWRITE); + include $this->template; + } + + /** + * 渲染模板内容 + * @access public + * @param string $content 模板内容 + * @param array $data 模板变量 + * @return void + */ + public function display($content, $data = []) + { + $this->content = $content; + + extract($data, EXTR_OVERWRITE); + eval('?>' . $this->content); + } + + /** + * 自动定位模板文件 + * @access private + * @param string $template 模板文件规则 + * @return string + */ + private function parseTemplate($template) + { + if (empty($this->config['view_path'])) { + $this->config['view_path'] = App::$modulePath . 'view' . DS; + } + + $request = Request::instance(); + // 获取视图根目录 + if (strpos($template, '@')) { + // 跨模块调用 + list($module, $template) = explode('@', $template); + } + if ($this->config['view_base']) { + // 基础视图目录 + $module = isset($module) ? $module : $request->module(); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); + } else { + $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; + } + + $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { + $template = str_replace(['/', ':'], $depr, $template); + $controller = Loader::parseName($request->controller()); + if ($controller) { + if ('' == $template) { + // 如果模板文件名为空 按照默认规则定位 + $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action()); + } elseif (false === strpos($template, $depr)) { + $template = str_replace('.', DS, $controller) . $depr . $template; + } + } + } else { + $template = str_replace(['/', ':'], $depr, substr($template, 1)); + } + return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); + } + + /** + * 配置模板引擎 + * @access private + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @return void + */ + public function config($name, $value = null) + { + if (is_array($name)) { + $this->config = array_merge($this->config, $name); + } elseif (is_null($value)) { + return isset($this->config[$name]) ? $this->config[$name] : null; + } else { + $this->config[$name] = $value; + } + } + +} diff --git a/source/thinkphp/library/think/view/driver/Think.php b/source/thinkphp/library/think/view/driver/Think.php new file mode 100644 index 0000000..a314ad6 --- /dev/null +++ b/source/thinkphp/library/think/view/driver/Think.php @@ -0,0 +1,167 @@ + +// +---------------------------------------------------------------------- + +namespace think\view\driver; + +use think\App; +use think\exception\TemplateNotFoundException; +use think\Loader; +use think\Log; +use think\Request; +use think\Template; + +class Think +{ + // 模板引擎实例 + private $template; + // 模板引擎参数 + protected $config = [ + // 视图基础目录(集中式) + 'view_base' => '', + // 模板起始路径 + 'view_path' => '', + // 模板文件后缀 + 'view_suffix' => 'html', + // 模板文件名分隔符 + 'view_depr' => DS, + // 是否开启模板编译缓存,设为false则每次都会重新编译 + 'tpl_cache' => true, + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 + 'auto_rule' => 1, + ]; + + public function __construct($config = []) + { + $this->config = array_merge($this->config, $config); + if (empty($this->config['view_path'])) { + $this->config['view_path'] = App::$modulePath . 'view' . DS; + } + + $this->template = new Template($this->config); + } + + /** + * 检测是否存在模板文件 + * @access public + * @param string $template 模板文件或者模板规则 + * @return bool + */ + public function exists($template) + { + if ('' == pathinfo($template, PATHINFO_EXTENSION)) { + // 获取模板文件名 + $template = $this->parseTemplate($template); + } + return is_file($template); + } + + /** + * 渲染模板文件 + * @access public + * @param string $template 模板文件 + * @param array $data 模板变量 + * @param array $config 模板参数 + * @return void + */ + public function fetch($template, $data = [], $config = []) + { + if ('' == pathinfo($template, PATHINFO_EXTENSION)) { + // 获取模板文件名 + $template = $this->parseTemplate($template); + } + // 模板不存在 抛出异常 + if (!is_file($template)) { + throw new TemplateNotFoundException('template not exists:' . $template, $template); + } + // 记录视图信息 + App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); + $this->template->fetch($template, $data, $config); + } + + /** + * 渲染模板内容 + * @access public + * @param string $template 模板内容 + * @param array $data 模板变量 + * @param array $config 模板参数 + * @return void + */ + public function display($template, $data = [], $config = []) + { + $this->template->display($template, $data, $config); + } + + /** + * 自动定位模板文件 + * @access private + * @param string $template 模板文件规则 + * @return string + */ + private function parseTemplate($template) + { + // 分析模板文件规则 + $request = Request::instance(); + // 获取视图根目录 + if (strpos($template, '@')) { + // 跨模块调用 + list($module, $template) = explode('@', $template); + } + if ($this->config['view_base']) { + // 基础视图目录 + $module = isset($module) ? $module : $request->module(); + $path = $this->config['view_base'] . ($module ? $module . DS : ''); + } else { + $path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path']; + } + + $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { + $template = str_replace(['/', ':'], $depr, $template); + $controller = Loader::parseName($request->controller()); + if ($controller) { + if ('' == $template) { + // 如果模板文件名为空 按照默认规则定位 + $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action()); + } elseif (false === strpos($template, $depr)) { + $template = str_replace('.', DS, $controller) . $depr . $template; + } + } + } else { + $template = str_replace(['/', ':'], $depr, substr($template, 1)); + } + return $path . ltrim($template, '/') . '.' . ltrim($this->config['view_suffix'], '.'); + } + + /** + * 配置或者获取模板引擎参数 + * @access private + * @param string|array $name 参数名 + * @param mixed $value 参数值 + * @return mixed + */ + public function config($name, $value = null) + { + if (is_array($name)) { + $this->template->config($name); + $this->config = array_merge($this->config, $name); + } elseif (is_null($value)) { + return $this->template->config($name); + } else { + $this->template->$name = $value; + $this->config[$name] = $value; + } + } + + public function __call($method, $params) + { + return call_user_func_array([$this->template, $method], $params); + } +} diff --git a/source/thinkphp/library/traits/controller/Jump.php b/source/thinkphp/library/traits/controller/Jump.php new file mode 100644 index 0000000..6a57224 --- /dev/null +++ b/source/thinkphp/library/traits/controller/Jump.php @@ -0,0 +1,167 @@ +error(); + * $this->redirect(); + * } + * } + */ +namespace traits\controller; + +use think\Config; +use think\exception\HttpResponseException; +use think\Request; +use think\Response; +use think\response\Redirect; +use think\Url; +use think\View as ViewTemplate; + +trait Jump +{ + /** + * 操作成功跳转的快捷方法 + * @access protected + * @param mixed $msg 提示信息 + * @param string $url 跳转的 URL 地址 + * @param mixed $data 返回的数据 + * @param int $wait 跳转等待时间 + * @param array $header 发送的 Header 信息 + * @return void + * @throws HttpResponseException + */ + protected function success($msg = '', $url = null, $data = '', $wait = 3, array $header = []) + { + if (is_null($url) && !is_null(Request::instance()->server('HTTP_REFERER'))) { + $url = Request::instance()->server('HTTP_REFERER'); + } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) { + $url = Url::build($url); + } + + $type = $this->getResponseType(); + $result = [ + 'code' => 1, + 'msg' => $msg, + 'data' => $data, + 'url' => $url, + 'wait' => $wait, + ]; + + if ('html' == strtolower($type)) { + $template = Config::get('template'); + $view = Config::get('view_replace_str'); + + $result = ViewTemplate::instance($template, $view) + ->fetch(Config::get('dispatch_success_tmpl'), $result); + } + + $response = Response::create($result, $type)->header($header); + + throw new HttpResponseException($response); + } + + /** + * 操作错误跳转的快捷方法 + * @access protected + * @param mixed $msg 提示信息 + * @param string $url 跳转的 URL 地址 + * @param mixed $data 返回的数据 + * @param int $wait 跳转等待时间 + * @param array $header 发送的 Header 信息 + * @return void + * @throws HttpResponseException + */ + protected function error($msg = '', $url = null, $data = '', $wait = 3, array $header = []) + { + if (is_null($url)) { + $url = Request::instance()->isAjax() ? '' : 'javascript:history.back(-1);'; + } elseif ('' !== $url && !strpos($url, '://') && 0 !== strpos($url, '/')) { + $url = Url::build($url); + } + + $type = $this->getResponseType(); + $result = [ + 'code' => 0, + 'msg' => $msg, + 'data' => $data, + 'url' => $url, + 'wait' => $wait, + ]; + + if ('html' == strtolower($type)) { + $template = Config::get('template'); + $view = Config::get('view_replace_str'); + + $result = ViewTemplate::instance($template, $view) + ->fetch(Config::get('dispatch_error_tmpl'), $result); + } + + $response = Response::create($result, $type)->header($header); + + throw new HttpResponseException($response); + } + + /** + * 返回封装后的 API 数据到客户端 + * @access protected + * @param mixed $data 要返回的数据 + * @param int $code 返回的 code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @param array $header 发送的 Header 信息 + * @return void + * @throws HttpResponseException + */ + protected function result($data, $code = 0, $msg = '', $type = '', array $header = []) + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => Request::instance()->server('REQUEST_TIME'), + 'data' => $data, + ]; + $type = $type ?: $this->getResponseType(); + $response = Response::create($result, $type)->header($header); + + throw new HttpResponseException($response); + } + + /** + * URL 重定向 + * @access protected + * @param string $url 跳转的 URL 表达式 + * @param array|int $params 其它 URL 参数 + * @param int $code http code + * @param array $with 隐式传参 + * @return void + * @throws HttpResponseException + */ + protected function redirect($url, $params = [], $code = 302, $with = []) + { + if (is_integer($params)) { + $code = $params; + $params = []; + } + + $response = new Redirect($url); + $response->code($code)->params($params)->with($with); + + throw new HttpResponseException($response); + } + + /** + * 获取当前的 response 输出类型 + * @access protected + * @return string + */ + protected function getResponseType() + { + return Request::instance()->isAjax() + ? Config::get('default_ajax_return') + : Config::get('default_return_type'); + } +} diff --git a/source/thinkphp/library/traits/model/SoftDelete.php b/source/thinkphp/library/traits/model/SoftDelete.php new file mode 100644 index 0000000..70f31ba --- /dev/null +++ b/source/thinkphp/library/traits/model/SoftDelete.php @@ -0,0 +1,200 @@ +getDeleteTimeField(); + + if ($field && !empty($this->data[$field])) { + return true; + } + return false; + } + + /** + * 查询包含软删除的数据 + * @access public + * @return Query + */ + public static function withTrashed() + { + return (new static )->getQuery(); + } + + /** + * 只查询软删除数据 + * @access public + * @return Query + */ + public static function onlyTrashed() + { + $model = new static(); + $field = $model->getDeleteTimeField(true); + + if ($field) { + return $model->getQuery()->useSoftDelete($field, ['not null', '']); + } else { + return $model->getQuery(); + } + } + + /** + * 删除当前的记录 + * @access public + * @param bool $force 是否强制删除 + * @return integer + */ + public function delete($force = false) + { + if (false === $this->trigger('before_delete', $this)) { + return false; + } + + $name = $this->getDeleteTimeField(); + if ($name && !$force) { + // 软删除 + $this->data[$name] = $this->autoWriteTimestamp($name); + $result = $this->isUpdate()->save(); + } else { + // 强制删除当前模型数据 + $result = $this->getQuery()->where($this->getWhere())->delete(); + } + + // 关联删除 + if (!empty($this->relationWrite)) { + foreach ($this->relationWrite as $key => $name) { + $name = is_numeric($key) ? $name : $key; + $result = $this->getRelation($name); + if ($result instanceof Model) { + $result->delete(); + } elseif ($result instanceof Collection || is_array($result)) { + foreach ($result as $model) { + $model->delete(); + } + } + } + } + + $this->trigger('after_delete', $this); + + // 清空原始数据 + $this->origin = []; + + return $result; + } + + /** + * 删除记录 + * @access public + * @param mixed $data 主键列表(支持闭包查询条件) + * @param bool $force 是否强制删除 + * @return integer 成功删除的记录数 + */ + public static function destroy($data, $force = false) + { + if (is_null($data)) { + return 0; + } + + // 包含软删除数据 + $query = (new static())->db(false); + if (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $query]); + $data = null; + } + + $count = 0; + if ($resultSet = $query->select($data)) { + foreach ($resultSet as $data) { + $result = $data->delete($force); + $count += $result; + } + } + + return $count; + } + + /** + * 恢复被软删除的记录 + * @access public + * @param array $where 更新条件 + * @return integer + */ + public function restore($where = []) + { + if (empty($where)) { + $pk = $this->getPk(); + $where[$pk] = $this->getData($pk); + } + + $name = $this->getDeleteTimeField(); + + if ($name) { + // 恢复删除 + return $this->getQuery() + ->useSoftDelete($name, ['not null', '']) + ->where($where) + ->update([$name => null]); + } else { + return 0; + } + } + + /** + * 查询默认不包含软删除数据 + * @access protected + * @param Query $query 查询对象 + * @return Query + */ + protected function base($query) + { + $field = $this->getDeleteTimeField(true); + return $field ? $query->useSoftDelete($field) : $query; + } + + /** + * 获取软删除字段 + * @access public + * @param bool $read 是否查询操作(写操作的时候会自动去掉表别名) + * @return string + */ + protected function getDeleteTimeField($read = false) + { + $field = property_exists($this, 'deleteTime') && isset($this->deleteTime) ? + $this->deleteTime : + 'delete_time'; + + if (false === $field) { + return false; + } + + if (!strpos($field, '.')) { + $field = '__TABLE__.' . $field; + } + + if (!$read && strpos($field, '.')) { + $array = explode('.', $field); + $field = array_pop($array); + } + + return $field; + } +} diff --git a/source/thinkphp/library/traits/think/Instance.php b/source/thinkphp/library/traits/think/Instance.php new file mode 100644 index 0000000..428c8fd --- /dev/null +++ b/source/thinkphp/library/traits/think/Instance.php @@ -0,0 +1,54 @@ + +// +---------------------------------------------------------------------- + +namespace traits\think; + +use think\Exception; + +trait Instance +{ + /** + * @var null|static 实例对象 + */ + protected static $instance = null; + + /** + * 获取示例 + * @param array $options 实例配置 + * @return static + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) self::$instance = new self($options); + + return self::$instance; + } + + /** + * 静态调用 + * @param string $method 调用方法 + * @param array $params 调用参数 + * @return mixed + * @throws Exception + */ + public static function __callStatic($method, array $params) + { + if (is_null(self::$instance)) self::$instance = new self(); + + $call = substr($method, 1); + + if (0 !== strpos($method, '_') || !is_callable([self::$instance, $call])) { + throw new Exception("method not exists:" . $method); + } + + return call_user_func_array([self::$instance, $call], $params); + } +} diff --git a/source/thinkphp/logo.png b/source/thinkphp/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..25fd0593688de5c9f4cd321da1a72ab9566fe331 GIT binary patch literal 6995 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 literal 0 HcmV?d00001 diff --git a/source/thinkphp/phpunit.xml b/source/thinkphp/phpunit.xml new file mode 100644 index 0000000..7c6ef03 --- /dev/null +++ b/source/thinkphp/phpunit.xml @@ -0,0 +1,35 @@ + + + + + ./tests/thinkphp/ + + + + + + + + ./ + + tests + vendor + + + + + + + + + + diff --git a/source/thinkphp/start.php b/source/thinkphp/start.php new file mode 100644 index 0000000..adb1bc6 --- /dev/null +++ b/source/thinkphp/start.php @@ -0,0 +1,19 @@ + +// +---------------------------------------------------------------------- + +namespace think; + +// ThinkPHP 引导文件 +// 1. 加载基础文件 +require __DIR__ . '/base.php'; + +// 2. 执行应用 +App::run()->send(); diff --git a/source/thinkphp/tpl/default_index.tpl b/source/thinkphp/tpl/default_index.tpl new file mode 100644 index 0000000..8538b4d --- /dev/null +++ b/source/thinkphp/tpl/default_index.tpl @@ -0,0 +1,10 @@ +*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }

    :)

    ThinkPHP V5
    十年磨一剑 - 为API开发设计的高性能框架

    [ V5.0 版本由 七牛云 独家赞助发布 ]
    '; + } +} diff --git a/source/thinkphp/tpl/dispatch_jump.tpl b/source/thinkphp/tpl/dispatch_jump.tpl new file mode 100644 index 0000000..583376b --- /dev/null +++ b/source/thinkphp/tpl/dispatch_jump.tpl @@ -0,0 +1,49 @@ +{__NOLAYOUT__} + + + + + 跳转提示 + + + +
    + + +

    :)

    +

    + + +

    :(

    +

    + + +

    +

    + 页面自动 跳转 等待时间: +

    +
    + + + diff --git a/source/thinkphp/tpl/page_trace.tpl b/source/thinkphp/tpl/page_trace.tpl new file mode 100644 index 0000000..7c5df6f --- /dev/null +++ b/source/thinkphp/tpl/page_trace.tpl @@ -0,0 +1,71 @@ +
    + + +
    +
    +
    + +
    + + diff --git a/source/thinkphp/tpl/think_exception.tpl b/source/thinkphp/tpl/think_exception.tpl new file mode 100644 index 0000000..21bbafc --- /dev/null +++ b/source/thinkphp/tpl/think_exception.tpl @@ -0,0 +1,537 @@ +'.end($names).''; + } + } + + if(!function_exists('parse_file')){ + function parse_file($file, $line) + { + return ''.basename($file)." line {$line}".''; + } + } + + if(!function_exists('parse_args')){ + function parse_args($args) + { + $result = []; + + foreach ($args as $key => $item) { + switch (true) { + case is_object($item): + $value = sprintf('object(%s)', parse_class(get_class($item))); + break; + case is_array($item): + if(count($item) > 3){ + $value = sprintf('[%s, ...]', parse_args(array_slice($item, 0, 3))); + } else { + $value = sprintf('[%s]', parse_args($item)); + } + break; + case is_string($item): + if(strlen($item) > 20){ + $value = sprintf( + '\'%s...\'', + htmlentities($item), + htmlentities(substr($item, 0, 20)) + ); + } else { + $value = sprintf("'%s'", htmlentities($item)); + } + break; + case is_int($item): + case is_float($item): + $value = $item; + break; + case is_null($item): + $value = 'null'; + break; + case is_bool($item): + $value = '' . ($item ? 'true' : 'false') . ''; + break; + case is_resource($item): + $value = 'resource'; + break; + default: + $value = htmlentities(str_replace("\n", '', var_export(strval($item), true))); + break; + } + + $result[] = is_int($key) ? $value : "'{$key}' => {$value}"; + } + + return implode(', ', $result); + } + } +?> + + + + + <?php echo \think\Lang::get('System Error'); ?> + + + + + +
    + +
    + +
    +
    + +
    +
    +

    [

    +
    +

    +
    + +
    + +
    +
      $value) { ?>
    +
    + +
    +

    Call Stack

    +
      +
    1. + +
    2. + +
    3. + +
    +
    +
    + +
    + +

    + +
    + + + +
    +

    Exception Datas

    + $value) { ?> + + + + + + + $val) { ?> + + + + + + + +
    empty
    + +
    + +
    + + + +
    +

    Environment Variables

    + $value) { ?> +
    + +
    +
    +
    empty
    +
    + +

    +
    + $val) { ?> +
    +
    +
    + +
    +
    + +
    + +
    + +
    + + + + + + + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/.coveralls.yml b/source/vendor/aliyuncs/oss-sdk-php/.coveralls.yml new file mode 100644 index 0000000..850cc59 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/.coveralls.yml @@ -0,0 +1,2 @@ +coverage_clover: coverage.xml +json_path: coverage.json diff --git a/source/vendor/aliyuncs/oss-sdk-php/.gitignore b/source/vendor/aliyuncs/oss-sdk-php/.gitignore new file mode 100644 index 0000000..7cdb514 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/.gitignore @@ -0,0 +1,8 @@ +vendor +composer.lock +doc +output +.idea +.buildpath +.project +.settings diff --git a/source/vendor/aliyuncs/oss-sdk-php/.travis.yml b/source/vendor/aliyuncs/oss-sdk-php/.travis.yml new file mode 100644 index 0000000..0b40ba2 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/.travis.yml @@ -0,0 +1,21 @@ +language: php +php: + - 7.1 + - 7.0 + - 5.6 + - 5.5 + - 5.4 +install: + - composer self-update + - composer install --no-interaction +script: + - php vendor/bin/phpunit +after_success: + - php vendor/bin/coveralls -v +env: + global: + - secure: SzmQ854lQmhV6ZkAG7lQNTY3CkazrXnDSb6VMwPU/sdaLGxPO159AW3fJS5d0sO/XN1P8x5WNkoA4i9soDlLBRibEEISNUM/2EMnpszsRymZ9o97PrS2IgORXTUL/OF+rpATzyNVB2p+2l9hBLiGf17exMSA5iOeY7W6E+VKPGi8TFykgbGUnLKU0h1hV3rzmtfGjOXcSpvYU/hxeZD/J/+6m5Gic9b/pNS+AbfTj7Y7Ru9tNsnyUP29V/vtEYtpQir3ZxQiSiUv9idybgGnJBOMYydJofb/mpFYHhYLSWqtMKGNLpeawmqs4z8S1Tvx5U5uzW5+h/mpzhvBaFlWGpm8t89BQxun5LVX5NiYCrV7TqaLitGp1cSpMjMDnrnSTNzk1exVz+rWZZcWS7yB9ULYA681GA8StXWk167qB7Y30iK1dFK3+2mDN2cEY+qLs8+bupDowQ4eOM+eqfhxX8F8+ouKcKomETsjiIwL+CUsIe6wjvnYFWb1YlRhbsI75bblHApflohnt6gVSJ78ZPqID+u2oUMjmIWXLTnRR2Y2tgEW8uqHeIoQ8BBntLdQDmv0BO4FpnGQIwrUUwQYeNzEM0DOr3hWZhyDR6Xvl+9H0l52xjANaSqpuTZfC3zmeFTG7kIjydvxNePRrony6XAawL9BvI7aKWuVF6YVjPM= + - secure: nEhsU8aUQqsAJeuger+boh51oTpeo4YNG7vUWbKxdwVDIrcLb+l7r7RvTlxU7mt8IZTWwicgri18mh+Wi04BwX4ulBA1SCs8jPbL51KEo5izoDGGtLSd2fuPHdslYSrwagrvq90EPnDT/7fHWn/TAoT+rueZzjNyCu5IGSgL3GnXaUThsJ82NMePL2YRdP4Q1qmtZPRFBOkOQ6F0heuV8fw8sLyTO3txaCQum9YneGxrWxOl/E8zB0qtlnPwLE8ogaHZMQh2/jThmTbI5UqwRTxV4f0qoD5eJYH+j0fslsSAjsg/HPnSuVcnccK3zSU+s2sV4dPCcISzECJvZEObwipfxOGhdqt5gMcxHhn8qVsbT97iIh106pG/BJCDgQd2EeVW8WfCi6cCuCKIMipvVkMypkmjQHWU1XaqPzILl7g5diW9Ctp2C4Akq5dYdrdu8IrnVK1ShtkQVaWU+S/Bht8VU5gYP7olPW/GdTz7sceU1NOIC4NPXqmWKbfavR98U5dkHMLMvzABYL1Q87h+KhPD1c14NUyw3YENUW7REiF/X5lERRm5H0kJ/1JqAa+AgeHQEGmPVuZV2s/na4b0S1479QRVmSM/6ZzXQpU+Y8jCRfETpUFA4S331369kirHgCqDlxyIntuEKrzivD02/O+5C3eJ0WHRz6QsN2/R4qg= + - secure: ZTvzNXEZP4efl+a/3VGMmdabfUQp83v5/lecMns039Ro7UuZYPdtbPtpPnpjaTI6Htd22A4Rva5BU/3JCQJAyQvpbKNn5sGou2SmfQu3o0SyhggSB7gWjYAf707aW1j4bHYfP8IjDS5NjuVk3AqXeNSUuLRUXRmwSOB0lSYiRhiTJY+pUdBl382Hx4NbhIU/gmOzRoJCs7coTip8IURXYEHPi5dnDWluajxI+TgNXFccSgEleeQDJajYgXmpLb2EhSj8piipOnVgaCEE5bh5fbp32024Qq38SGHKcbfnwj2IInpZpZESJknRKoqAlFjdOJhork82dBcvAr5JxCBZKx5IuwXcTjxkQ6tRtBeqhPLPFuX3MQ8WrtU+wniPM0RCH/VoFkUKO7JGQDwmoi2AKago4PsuDs4P6Y6CeuOVpcso731GwwMNhIJcyrJJivXprQNEGsEw+9wLjU1qNYs6IIA3S/gPzFrNbdX5Wf8vxt0vLpgYvBNtPnLMejMtknuyfVzf5iKuVVoGPDTEz+ajs06+jfoPfm/4sLTaLghuVH7Adm74OpF769JQNnQYKwJuu4bNlcbLJChulCEMBOx7myqo/9O6RCTuqzHaGmVWNot4RGqRFHgJGl/JJf0WpAVitbhbRH3kGoyKb6jFM74CJbPsE7OORlJLDC3cdD3C8Pk= + - secure: Qr5NR4CVzBKCQgRgMH0x772TPJ1+brx3UCvtRNu8fi4j3p8bz+HDMjBaBDSFmEB09nunLI55/8mj88/5GXmnpFs8+CPTkcW+QZOcxg3cxpI4SNmxoB12/ZawlFHAqSUaRRE7RUWVkY3KL8tIGjEZcFyUBQ1DVNX3OMpiKs3NLtHa7oUIknyBxdSokm4kpLhSXYe7WmO0vhuZbMZE0S1EISToiBS6AdhGUEbTLJ/vNsIDY07fu6+Vh3HxVbyUFPqUZGlkZpQ+2xMJ3kiqPBMrXtRF/IhhPjORDil6Ns9SQ8/AAlaCddvYvRaT4Pjv2/aX+t3l28qI1qmryPtWXpce5UXecWGYqdRpSJc6Y/pEt4m4FeeGoEFWnSPGIs7FRmeiis8q2rojGZ18i4vI/k4iHmqEBnTlMp3SWnRb9L1adJ8ZAWln8aC88gkQXm67w7+1CxLycerbYj9H1ugqHENuHcxv4uHUcZgEENX3EWatu8i9+K2IUuU/2zcmpu7qtsziYcoyW8DOOmYpJfXGMLtmF9+pqp/Tp6i0tltFSEfmY3N8o7xvv3enLvFHsjL+3ElFdd1blUPSrvZJHgA9M3lJ+QF1RJZCpJqgPlQ0XOZK1Bf4P46zpEj01wKaK4JQrkLPRXhbBOuIJn5O6WlFJyPX4+SaBfwTzb4AvM4aUg2TgTg= + - secure: Inw5ftA8fxvhMHRZwoZzATxn4WICJsCq7ZX4y2gI+b/8u0JQIsbLgY9WTYV+XdSxOoNwuVa1oUxEWI0aDORtXKC3XaIXXKrwndag0zxS77JEYwWvQsjM7BhEbF7MF7MYk8rRXpn6mbfGAT/NfqEOx91RCY8UKeMzD0oPkpkBnJ9Ekuod6JBBq+7j3v4mYUItA8pxvw7b4Pdd4z2xzjgOwNhJYMOCpts50DWZI+WXj0HvTYaMXe5mJJtORK5lsr0a9cbsBqAzE6l+3zGI8XkgHn130ux5XH3DE7hZBeti3ZNaO3d2Vv+496/1EObG0rSFk+z3dmNKqjMz4nh3bYIkdLMegwmgCWs2mvQhkwYhzmnPRHVSERrgZjSWnuKn2PKnBar6tui9KaLNgpo2j3jWpwMLJ3bGAfE5JtMppxAxNqj/q+YB2UZo7Mn7EDjkTDjgxCuazTJwWqH7xxCOykWPABBI17P3JaOXQJEK39LavpfSMm3kdmU0ocpUs7FniLuFm6xL71VxY1wHG10yskczEcFHZ3iyIyGM+xum4vbt5y6Yg+zfdExYQsbrxHDDZ3HbHY3tEU0WhM55vrC42NIXRWqXqJ8OAxpl4nivfx96eoBAThiUU9xXtZmh7WRFVYsstoGtxZwfk5+bi+oeVO9kih4xabwbgHgL9BTc1TR1C4U= diff --git a/source/vendor/aliyuncs/oss-sdk-php/CHANGELOG.md b/source/vendor/aliyuncs/oss-sdk-php/CHANGELOG.md new file mode 100644 index 0000000..042a72a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/CHANGELOG.md @@ -0,0 +1,92 @@ +# ChangeLog - Aliyun OSS SDK for PHP + +## v2.3.0 / 2018-01-05 + +* 修复:putObject支持创建空文件 +* 修复:createBucket支持IA/Archive +* 增加:支持restoreObject +* 增加:支持Symlink功能 +* 增加:支持getBucketLocation +* 增加:支持getBucketMeta +* 增加:支持代理服务器Proxy + +## v2.2.4 / 2017-04-25 + +* fix getObject to local file bug + +## v2.2.3 / 2017-04-14 + +* fix md5 check + +## v2.2.2 / 2017-01-18 + +* 解决在php7上运行连接数和内存bug + +## v2.2.1 / 2016-12-01 + +* 禁止http curl自动填充Accept-Encoding + +## v2.2.0 / 2016-11-22 + +* 修复PutObject/CompleteMultipartUpload的返回值问题(#26) + +## v2.1.0 / 2016-11-12 + +* 增加[RTMP](https://help.aliyun.com/document_detail/44297.html)接口 +* 增加支持[图片服务](https://help.aliyun.com/document_detail/44686.html) + +## v2.0.7 / 2016-06-17 + +* Support append object + +## v2.0.6 + +* Trim access key id/secret and endpoint +* Refine tests and setup travis CI + +## v2.0.5 + +* 增加Add/Delete/Get BucketCname接口 + +## v2.0.4 + +* 增加Put/Get Object Acl接口 + +## v2.0.3 + +* 修复Util中的常量定义在低于5.6的PHP版本中报错的问题 + +## v2.0.2 + +* 修复multipart上传时无法指定Content-Type的问题 + +## v2.0.1 + +* 增加对ListObjects/ListMultipartUploads时特殊字符的处理 +* 提供接口获取OssException中的详细信息 + + +## 2015.11.25 + +* **大版本升级,不再兼容以前接口,新版本对易用性做了很大的改进,建议用户迁移到新版本。** + +## 修改内容 + +* 不再支持PHP 5.2版本 + +### 新增内容 + +* 引入命名空间 +* 接口命名修正,采用驼峰式命名 +* 接口入参修改,把常用参数从Options参数中提出来 +* 接口返回结果修改,对返回结果进行处理,用户可以直接得到容易处理的数据结构  +* OssClient的构造函数变更 +* 支持CNAME和IP格式的Endpoint地址 +* 重新整理sample文件组织结构,使用function组织功能点 +* 增加设置连接超时,请求超时的接口 +* 去掉Object Group相关的已经过时的接口 +* OssException中的message改为英文 + +### 问题修复 + +* object名称校验不完备 diff --git a/source/vendor/aliyuncs/oss-sdk-php/LICENSE.md b/source/vendor/aliyuncs/oss-sdk-php/LICENSE.md new file mode 100644 index 0000000..3183de8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/LICENSE.md @@ -0,0 +1,21 @@ +#The MIT License (MIT) + +Copyright (c) ali-sdk and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/README-CN.md b/source/vendor/aliyuncs/oss-sdk-php/README-CN.md new file mode 100644 index 0000000..8c0cf84 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/README-CN.md @@ -0,0 +1,149 @@ +# Aliyun OSS SDK for PHP + +[![Latest Stable Version](https://poser.pugx.org/aliyuncs/oss-sdk-php/v/stable)](https://packagist.org/packages/aliyuncs/oss-sdk-php) +[![Build Status](https://travis-ci.org/aliyun/aliyun-oss-php-sdk.svg?branch=master)](https://travis-ci.org/aliyun/aliyun-oss-php-sdk) +[![Coverage Status](https://coveralls.io/repos/github/aliyun/aliyun-oss-php-sdk/badge.svg?branch=master)](https://coveralls.io/github/aliyun/aliyun-oss-php-sdk?branch=master) + +## [README of English](https://github.com/aliyun/aliyun-oss-php-sdk/blob/master/README.md) + +## 概述 + +阿里云对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量、安全、低成本、高可靠的云存储服务。用户可以通过调用API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过用户Web控制台对数据进行简单的管理。OSS适合存放任意文件类型,适合各种网站、开发企业及开发者使用。 + + +## 运行环境 +- PHP 5.3+ +- cURL extension + +提示: + +- Ubuntu下可以使用apt-get包管理器安装php的cURL扩展 `sudo apt-get install php5-curl` + +## 安装方法 + +1. 如果您通过composer管理您的项目依赖,可以在你的项目根目录运行: + + $ composer require aliyuncs/oss-sdk-php + + 或者在你的`composer.json`中声明对Aliyun OSS SDK for PHP的依赖: + + "require": { + "aliyuncs/oss-sdk-php": "~2.0" + } + + 然后通过`composer install`安装依赖。composer安装完成后,在您的PHP代码中引入依赖即可: + + require_once __DIR__ . '/vendor/autoload.php'; + +2. 您也可以直接下载已经打包好的[phar文件][releases-page],然后在你 + 的代码中引入这个文件即可: + + require_once '/path/to/oss-sdk-php.phar'; + +3. 下载SDK源码,在您的代码中引入SDK目录下的`autoload.php`文件: + + require_once '/path/to/oss-sdk/autoload.php'; + +## 快速使用 + +### 常用类 + +| 类名 | 解释 | +|:------------------|:------------------------------------| +|OSS\OssClient | OSS客户端类,用户通过OssClient的实例调用接口 | +|OSS\Core\OssException | OSS异常类,用户在使用的过程中,只需要注意这个异常| + +### OssClient初始化 + +SDK的OSS操作通过OssClient类完成的,下面代码创建一个OssClient对象: + +```php +"; ; +$accessKeySecret = "<您从OSS获得的AccessKeySecret>"; +$endpoint = "<您选定的OSS数据中心访问域名,例如oss-cn-hangzhou.aliyuncs.com>"; +try { + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### 文件操作 + +文件(又称对象,Object)是OSS中最基本的数据单元,您可以把它简单地理解为文件,用下面代码可以实现一个Object的上传: + +```php +"; +$object = "<您使用的Object名字,注意命名规范>"; +$content = "Hello, OSS!"; // 上传的文件内容 +try { + $ossClient->putObject($bucket, $object, $content); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### 存储空间操作 + +存储空间(又称Bucket)是一个用户用来管理所存储Object的存储空间,对于用户来说是一个管理Object的单元,所有的Object都必须隶属于某个Bucket。您可以按照下面的代码新建一个Bucket: + +```php +"; +try { + $ossClient->createBucket($bucket); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### 返回结果处理 + +OssClient提供的接口返回返回数据分为两种: + +* Put,Delete类接口,接口返回null,如果没有OssException,即可认为操作成功 +* Get,List类接口,接口返回对应的数据,如果没有OssException,即可认为操作成功,举个例子: + +```php +listBuckets(); +$bucketList = $bucketListInfo->getBucketList(); +foreach($bucketList as $bucket) { + print($bucket->getLocation() . "\t" . $bucket->getName() . "\t" . $bucket->getCreatedate() . "\n"); +} +``` +上面代码中的$bucketListInfo的数据类型是 `OSS\Model\BucketListInfo` + + +### 运行Sample程序 + +1. 修改 `samples/Config.php`, 补充配置信息 +2. 执行 `cd samples/ && php RunAll.php` + +### 运行单元测试 + +1. 执行`composer install`下载依赖的库 +2. 设置环境变量 + + export OSS_ACCESS_KEY_ID=access-key-id + export OSS_ACCESS_KEY_SECRET=access-key-secret + export OSS_ENDPOINT=endpoint + export OSS_BUCKET=bucket-name + +3. 执行 `php vendor/bin/phpunit` + +## License + +- MIT + +## 联系我们 + +- [阿里云OSS官方网站](http://oss.aliyun.com) +- [阿里云OSS官方论坛](http://bbs.aliyun.com) +- [阿里云OSS官方文档中心](http://www.aliyun.com/product/oss#Docs) +- 阿里云官方技术支持:[提交工单](https://workorder.console.aliyun.com/#/ticket/createIndex) + +[releases-page]: https://github.com/aliyun/aliyun-oss-php-sdk/releases +[phar-composer]: https://github.com/clue/phar-composer diff --git a/source/vendor/aliyuncs/oss-sdk-php/README.md b/source/vendor/aliyuncs/oss-sdk-php/README.md new file mode 100644 index 0000000..3c1da26 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/README.md @@ -0,0 +1,150 @@ +# Alibaba Cloud OSS SDK for PHP + +[![Latest Stable Version](https://poser.pugx.org/aliyuncs/oss-sdk-php/v/stable)](https://packagist.org/packages/aliyuncs/oss-sdk-php) +[![Build Status](https://travis-ci.org/aliyun/aliyun-oss-php-sdk.svg?branch=master)](https://travis-ci.org/aliyun/aliyun-oss-php-sdk) +[![Coverage Status](https://coveralls.io/repos/github/aliyun/aliyun-oss-php-sdk/badge.svg?branch=master)](https://coveralls.io/github/aliyun/aliyun-oss-php-sdk?branch=master) + +## [README of Chinese](https://github.com/aliyun/aliyun-oss-php-sdk/blob/master/README-CN.md) + +## Overview + +Alibaba Cloud Object Storage Service (OSS) is a cloud storage service provided by Alibaba Cloud, featuring a massive capacity, security, a low cost, and high reliability. You can upload and download data on any application anytime and anywhere by calling APIs, and perform simple management of data through the web console. The OSS can store any type of files and therefore applies to various websites, development enterprises and developers. + + +## Run environment +- PHP 5.3+. +- cURL extension. + +Tips: + +- In Ubuntu, you can use the ***apt-get*** package manager to install the *PHP cURL extension*: `sudo apt-get install php5-curl`. + +## Install OSS PHP SDK + +- If you use the ***composer*** to manage project dependencies, run the following command in your project's root directory: + + composer require aliyuncs/oss-sdk-php + + You can also declare the dependency on Alibaba Cloud OSS SDK for PHP in the `composer.json` file. + + "require": { + "aliyuncs/oss-sdk-php": "~2.0" + } + + Then run `composer install` to install the dependency. After the Composer Dependency Manager is installed, import the dependency in your PHP code: + + require_once __DIR__ . '/vendor/autoload.php'; + +- You can also directly download the packaged [PHAR File][releases-page], and + introduce the file to your code: + + require_once '/path/to/oss-sdk-php.phar'; + +- Download the SDK source code, and introduce the `autoload.php` file under the SDK directory to your code: + + require_once '/path/to/oss-sdk/autoload.php'; + +## Quick use + +### Common classes + +| Class | Explanation | +|:------------------|:------------------------------------| +|OSS\OSSClient | OSS client class. An OSSClient instance can be used to call the interface. | +|OSS\Core\OSSException |OSS Exception class . You only need to pay attention to this exception when you use the OSSClient. | + +### Initialize an OSSClient + +The SDK's operations for the OSS are performed through the OSSClient class. The code below creates an OSSClient object: + +```php +"; +$accessKeySecret = ""; +$endpoint = ""; +try { + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### Operations on objects + +Objects are the most basic data units on the OSS. You can simply consider objects as files. The following code uploads an object: + +```php +"; +$object = ""; +$content = "Hello, OSS!"; // Content of the uploaded file +try { + $ossClient->putObject($bucket, $object, $content); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### Operations on buckets + +Buckets are the space that you use to manage the stored objects. It is an object management unit for users. Each object must belong to a bucket. You can create a bucket with the following code: + +```php +"; +try { + $ossClient->createBucket($bucket); +} catch (OssException $e) { + print $e->getMessage(); +} +``` + +### Handle returned results + +The OSSClient provides the following two types of returned data from interfaces: + +- Put and Delete interfaces: The *PUT* and *DELETE* operations are deemed successful if *null* is returned by the interfaces without *OSSException*. +- Get and List interfaces: The *GET* and *LIST* operations are deemed successful if the desired data is returned by the interfaces without *OSSException*. For example, + + ```php + listBuckets(); + $bucketList = $bucketListInfo->getBucketList(); + foreach($bucketList as $bucket) { + print($bucket->getLocation() . "\t" . $bucket->getName() . "\t" . $bucket->getCreatedate() . "\n"); + } + ``` +In the above code, $bucketListInfo falls into the 'OSS\Model\BucketListInfo' data type. + + +### Run a sample project + +- Modify `samples/Config.php` to complete the configuration information. +- Run `cd samples/ && php RunAll.php`. + +### Run a unit test + +- Run `composer install` to download the dependent libraries. +- Set the environment variable. + + export OSS_ACCESS_KEY_ID=access-key-id + export OSS_ACCESS_KEY_SECRET=access-key-secret + export OSS_ENDPOINT=endpoint + export OSS_BUCKET=bucket-name + +- Run `php vendor/bin/phpunit` + +## License + +- MIT + +## Contact us + +- [Alibaba Cloud OSS official website](http://oss.aliyun.com). +- [Alibaba Cloud OSS official forum](http://bbs.aliyun.com). +- [Alibaba Cloud OSS official documentation center](http://www.aliyun.com/product/oss#Docs). +- Alibaba Cloud official technical support: [Submit a ticket](https://workorder.console.aliyun.com/#/ticket/createIndex). + +[releases-page]: https://github.com/aliyun/aliyun-oss-php-sdk/releases +[phar-composer]: https://github.com/clue/phar-composer + diff --git a/source/vendor/aliyuncs/oss-sdk-php/autoload.php b/source/vendor/aliyuncs/oss-sdk-php/autoload.php new file mode 100644 index 0000000..ec13201 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/autoload.php @@ -0,0 +1,11 @@ +=5.3" + }, + "require-dev" : { + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "~1.0" + }, + "minimum-stability": "stable", + "autoload": { + "psr-4": {"OSS\\": "src/OSS"} + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/example.jpg b/source/vendor/aliyuncs/oss-sdk-php/example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffd46a2f81b1fe020ed0673f27711124110eed3f GIT binary patch literal 21839 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 + + + + + + ./src + + + + + + + + ./tests + ./tests/OSS/Tests/BucketCnameTest.php + + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/Bucket.php b/source/vendor/aliyuncs/oss-sdk-php/samples/Bucket.php new file mode 100644 index 0000000..bd16e65 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/Bucket.php @@ -0,0 +1,167 @@ +createBucket($bucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); +Common::println("bucket $bucket created"); + +// 判断Bucket是否存在 +$doesExist = $ossClient->doesBucketExist($bucket); +Common::println("bucket $bucket exist? " . ($doesExist ? "yes" : "no")); + +// 获取Bucket列表 +$bucketListInfo = $ossClient->listBuckets(); + +// 设置bucket的ACL +$ossClient->putBucketAcl($bucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); +Common::println("bucket $bucket acl put"); +// 获取bucket的ACL +$acl = $ossClient->getBucketAcl($bucket); +Common::println("bucket $bucket acl get: " . $acl); + + +//******************************* 完整用法参考下面函数 **************************************************** + +createBucket($ossClient, $bucket); +doesBucketExist($ossClient, $bucket); +deleteBucket($ossClient, $bucket); +putBucketAcl($ossClient, $bucket); +getBucketAcl($ossClient, $bucket); +listBuckets($ossClient); + +/** + * 创建一个存储空间 + * acl 指的是bucket的访问控制权限,有三种,私有读写,公共读私有写,公共读写。 + * 私有读写就是只有bucket的拥有者或授权用户才有权限操作 + * 三种权限分别对应 (OssClient::OSS_ACL_TYPE_PRIVATE,OssClient::OSS_ACL_TYPE_PUBLIC_READ, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE) + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 要创建的存储空间名称 + * @return null + */ +function createBucket($ossClient, $bucket) +{ + try { + $ossClient->createBucket($bucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 判断Bucket是否存在 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + */ +function doesBucketExist($ossClient, $bucket) +{ + try { + $res = $ossClient->doesBucketExist($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + if ($res === true) { + print(__FUNCTION__ . ": OK" . "\n"); + } else { + print(__FUNCTION__ . ": FAILED" . "\n"); + } +} + +/** + * 删除bucket,如果bucket不为空则bucket无法删除成功, 不为空表示bucket既没有object,也没有未完成的multipart上传时的parts + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 待删除的存储空间名称 + * @return null + */ +function deleteBucket($ossClient, $bucket) +{ + try { + $ossClient->deleteBucket($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 设置bucket的acl配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putBucketAcl($ossClient, $bucket) +{ + $acl = OssClient::OSS_ACL_TYPE_PRIVATE; + try { + $ossClient->putBucketAcl($bucket, $acl); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + + +/** + * 获取bucket的acl配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketAcl($ossClient, $bucket) +{ + try { + $res = $ossClient->getBucketAcl($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print('acl: ' . $res); +} + + +/** + * 列出用户所有的Bucket + * + * @param OssClient $ossClient OssClient实例 + * @return null + */ +function listBuckets($ossClient) +{ + $bucketList = null; + try { + $bucketListInfo = $ossClient->listBuckets(); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + $bucketList = $bucketListInfo->getBucketList(); + foreach ($bucketList as $bucket) { + print($bucket->getLocation() . "\t" . $bucket->getName() . "\t" . $bucket->getCreatedate() . "\n"); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/BucketCors.php b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketCors.php new file mode 100644 index 0000000..cc5c0b9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketCors.php @@ -0,0 +1,108 @@ +addAllowedHeader("x-oss-header"); +$rule->addAllowedOrigin("http://www.b.com"); +$rule->addAllowedMethod("POST"); +$rule->setMaxAgeSeconds(10); +$corsConfig->addRule($rule); +$ossClient->putBucketCors($bucket, $corsConfig); +Common::println("bucket $bucket corsConfig created:" . $corsConfig->serializeToXml()); + +// 获取cors配置 +$corsConfig = $ossClient->getBucketCors($bucket); +Common::println("bucket $bucket corsConfig fetched:" . $corsConfig->serializeToXml()); + +// 删除cors配置 +$ossClient->deleteBucketCors($bucket); +Common::println("bucket $bucket corsConfig deleted"); + +//******************************* 完整用法参考下面函数 ***************************************************** + +putBucketCors($ossClient, $bucket); +getBucketCors($ossClient, $bucket); +deleteBucketCors($ossClient, $bucket); +getBucketCors($ossClient, $bucket); + +/** + * 设置bucket的cors配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putBucketCors($ossClient, $bucket) +{ + $corsConfig = new CorsConfig(); + $rule = new CorsRule(); + $rule->addAllowedHeader("x-oss-header"); + $rule->addAllowedOrigin("http://www.b.com"); + $rule->addAllowedMethod("POST"); + $rule->setMaxAgeSeconds(10); + $corsConfig->addRule($rule); + + try { + $ossClient->putBucketCors($bucket, $corsConfig); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取并打印bucket的cors配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketCors($ossClient, $bucket) +{ + $corsConfig = null; + try { + $corsConfig = $ossClient->getBucketCors($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print($corsConfig->serializeToXml() . "\n"); +} + +/** + * 删除bucket的所有的cors配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteBucketCors($ossClient, $bucket) +{ + try { + $ossClient->deleteBucketCors($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLifecycle.php b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLifecycle.php new file mode 100644 index 0000000..ec0c37f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLifecycle.php @@ -0,0 +1,109 @@ +addRule($lifecycleRule); +$ossClient->putBucketLifecycle($bucket, $lifecycleConfig); +Common::println("bucket $bucket lifecycleConfig created:" . $lifecycleConfig->serializeToXml()); + +//获取lifecycle规则 +$lifecycleConfig = $ossClient->getBucketLifecycle($bucket); +Common::println("bucket $bucket lifecycleConfig fetched:" . $lifecycleConfig->serializeToXml()); + +//删除bucket的lifecycle配置 +$ossClient->deleteBucketLifecycle($bucket); +Common::println("bucket $bucket lifecycleConfig deleted"); + + +//***************************** 完整用法参考下面函数 *********************************************** + +putBucketLifecycle($ossClient, $bucket); +getBucketLifecycle($ossClient, $bucket); +deleteBucketLifecycle($ossClient, $bucket); +getBucketLifecycle($ossClient, $bucket); + +/** + * 设置bucket的生命周期配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putBucketLifecycle($ossClient, $bucket) +{ + $lifecycleConfig = new LifecycleConfig(); + $actions = array(); + $actions[] = new LifecycleAction(OssClient::OSS_LIFECYCLE_EXPIRATION, OssClient::OSS_LIFECYCLE_TIMING_DAYS, 3); + $lifecycleRule = new LifecycleRule("delete obsoleted files", "obsoleted/", "Enabled", $actions); + $lifecycleConfig->addRule($lifecycleRule); + $actions = array(); + $actions[] = new LifecycleAction(OssClient::OSS_LIFECYCLE_EXPIRATION, OssClient::OSS_LIFECYCLE_TIMING_DATE, '2022-10-12T00:00:00.000Z'); + $lifecycleRule = new LifecycleRule("delete temporary files", "temporary/", "Enabled", $actions); + $lifecycleConfig->addRule($lifecycleRule); + try { + $ossClient->putBucketLifecycle($bucket, $lifecycleConfig); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取bucket的生命周期配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketLifecycle($ossClient, $bucket) +{ + $lifecycleConfig = null; + try { + $lifecycleConfig = $ossClient->getBucketLifecycle($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print($lifecycleConfig->serializeToXml() . "\n"); +} + +/** + * 删除bucket的生命周期配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteBucketLifecycle($ossClient, $bucket) +{ + try { + $ossClient->deleteBucketLifecycle($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLogging.php b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLogging.php new file mode 100644 index 0000000..406e1d4 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketLogging.php @@ -0,0 +1,95 @@ +putBucketLogging($bucket, $bucket, "access.log", array()); +Common::println("bucket $bucket lifecycleConfig created"); + +// 获取Bucket访问日志记录规则 +$loggingConfig = $ossClient->getBucketLogging($bucket, array()); +Common::println("bucket $bucket lifecycleConfig fetched:" . $loggingConfig->serializeToXml()); + +// 删除Bucket访问日志记录规则 +$loggingConfig = $ossClient->getBucketLogging($bucket, array()); +Common::println("bucket $bucket lifecycleConfig deleted"); + +//******************************* 完整用法参考下面函数 **************************************************** + +putBucketLogging($ossClient, $bucket); +getBucketLogging($ossClient, $bucket); +deleteBucketLogging($ossClient, $bucket); +getBucketLogging($ossClient, $bucket); + +/** + * 设置bucket的Logging配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putBucketLogging($ossClient, $bucket) +{ + $option = array(); + //访问日志存放在本bucket下 + $targetBucket = $bucket; + $targetPrefix = "access.log"; + + try { + $ossClient->putBucketLogging($bucket, $targetBucket, $targetPrefix, $option); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取bucket的Logging配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketLogging($ossClient, $bucket) +{ + $loggingConfig = null; + $options = array(); + try { + $loggingConfig = $ossClient->getBucketLogging($bucket, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print($loggingConfig->serializeToXml() . "\n"); +} + +/** + * 删除bucket的Logging配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteBucketLogging($ossClient, $bucket) +{ + try { + $ossClient->deleteBucketLogging($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/BucketReferer.php b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketReferer.php new file mode 100644 index 0000000..3828df6 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketReferer.php @@ -0,0 +1,101 @@ +setAllowEmptyReferer(true); +$refererConfig->addReferer("www.aliiyun.com"); +$refererConfig->addReferer("www.aliiyuncs.com"); +$ossClient->putBucketReferer($bucket, $refererConfig); +Common::println("bucket $bucket refererConfig created:" . $refererConfig->serializeToXml()); +//获取Referer白名单 +$refererConfig = $ossClient->getBucketReferer($bucket); +Common::println("bucket $bucket refererConfig fetched:" . $refererConfig->serializeToXml()); + +//删除referer白名单 +$refererConfig = new RefererConfig(); +$ossClient->putBucketReferer($bucket, $refererConfig); +Common::println("bucket $bucket refererConfig deleted"); + + +//******************************* 完整用法参考下面函数 **************************************************** + +putBucketReferer($ossClient, $bucket); +getBucketReferer($ossClient, $bucket); +deleteBucketReferer($ossClient, $bucket); +getBucketReferer($ossClient, $bucket); + +/** + * 设置bucket的防盗链配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putBucketReferer($ossClient, $bucket) +{ + $refererConfig = new RefererConfig(); + $refererConfig->setAllowEmptyReferer(true); + $refererConfig->addReferer("www.aliiyun.com"); + $refererConfig->addReferer("www.aliiyuncs.com"); + try { + $ossClient->putBucketReferer($bucket, $refererConfig); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取bucket的防盗链配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketReferer($ossClient, $bucket) +{ + $refererConfig = null; + try { + $refererConfig = $ossClient->getBucketReferer($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print($refererConfig->serializeToXml() . "\n"); +} + +/** + * 删除bucket的防盗链配置 + * Referer白名单不能直接清空,只能通过重新设置来覆盖之前的规则。 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteBucketReferer($ossClient, $bucket) +{ + $refererConfig = new RefererConfig(); + try { + $ossClient->putBucketReferer($bucket, $refererConfig); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/BucketWebsite.php b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketWebsite.php new file mode 100644 index 0000000..54706f8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/BucketWebsite.php @@ -0,0 +1,92 @@ +putBucketWebsite($bucket, $websiteConfig); +Common::println("bucket $bucket websiteConfig created:" . $websiteConfig->serializeToXml()); + +// 查看Bucket的静态网站托管状态 +$websiteConfig = $ossClient->getBucketWebsite($bucket); +Common::println("bucket $bucket websiteConfig fetched:" . $websiteConfig->serializeToXml()); + +// 删除Bucket的静态网站托管模式 +$ossClient->deleteBucketWebsite($bucket); +Common::println("bucket $bucket websiteConfig deleted"); + +//******************************* 完整用法参考下面函数 **************************************************** + +putBucketWebsite($ossClient, $bucket); +getBucketWebsite($ossClient, $bucket); +deleteBucketWebsite($ossClient, $bucket); +getBucketWebsite($ossClient, $bucket); + +/** + * 设置bucket的静态网站托管模式配置 + * + * @param $ossClient OssClient + * @param $bucket string 存储空间名称 + * @return null + */ +function putBucketWebsite($ossClient, $bucket) +{ + $websiteConfig = new WebsiteConfig("index.html", "error.html"); + try { + $ossClient->putBucketWebsite($bucket, $websiteConfig); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取bucket的静态网站托管状态 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getBucketWebsite($ossClient, $bucket) +{ + $websiteConfig = null; + try { + $websiteConfig = $ossClient->getBucketWebsite($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + print($websiteConfig->serializeToXml() . "\n"); +} + +/** + * 删除bucket的静态网站托管模式配置 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteBucketWebsite($ossClient, $bucket) +{ + try { + $ossClient->deleteBucketWebsite($bucket); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/Callback.php b/source/vendor/aliyuncs/oss-sdk-php/samples/Callback.php new file mode 100644 index 0000000..8612a1c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/Callback.php @@ -0,0 +1,83 @@ + $url, + OssClient::OSS_CALLBACK_VAR => $var + ); +$result = $ossClient->putObject($bucket, "b.file", "random content", $options); +Common::println($result['body']); +Common::println($result['info']['http_code']); + +/** + * completeMultipartUpload 使用callback上传内容到oss文件 + * callbackurl参数指定请求回调的服务器url + * callbackbodytype参数可为application/json或application/x-www-form-urlencoded, 可选参数,默认为application/x-www-form-urlencoded + * OSS_CALLBACK_VAR参数可以不设置 + */ +$object = "multipart-callback-test.txt"; +$copiedObject = "multipart-callback-test.txt.copied"; +$ossClient->putObject($bucket, $copiedObject, file_get_contents(__FILE__)); + +/** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ +$upload_id = $ossClient->initiateMultipartUpload($bucket, $object); + +/** + * step 2. uploadPartCopy + */ +$copyId = 1; +$eTag = $ossClient->uploadPartCopy($bucket, $copiedObject, $bucket, $object, $copyId, $upload_id); +$upload_parts[] = array( + 'PartNumber' => $copyId, + 'ETag' => $eTag, + ); +$listPartsInfo = $ossClient->listParts($bucket, $object, $upload_id); + +/** + * step 3. + */ +$json = + '{ + "callbackUrl":"callback.oss-demo.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size},\"x:var1\":${x:var1},\"x:var2\":${x:var2}}", + "callbackBodyType":"application/json" + }'; +$var = + '{ + "x:var1":"value1", + "x:var2":"值2" + }'; +$options = array(OssClient::OSS_CALLBACK => $json, + OssClient::OSS_CALLBACK_VAR => $var); + +$result = $ossClient->completeMultipartUpload($bucket, $object, $upload_id, $upload_parts, $options); +Common::println($result['body']); +Common::println($result['info']['http_code']); diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/Common.php b/source/vendor/aliyuncs/oss-sdk-php/samples/Common.php new file mode 100644 index 0000000..f419d17 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/Common.php @@ -0,0 +1,84 @@ +getMessage() . "\n"); + return null; + } + return $ossClient; + } + + public static function getBucketName() + { + return self::bucket; + } + + /** + * 工具方法,创建一个存储空间,如果发生异常直接exit + */ + public static function createBucket() + { + $ossClient = self::getOssClient(); + if (is_null($ossClient)) exit(1); + $bucket = self::getBucketName(); + $acl = OssClient::OSS_ACL_TYPE_PUBLIC_READ; + try { + $ossClient->createBucket($bucket, $acl); + } catch (OssException $e) { + + $message = $e->getMessage(); + if (\OSS\Core\OssUtil::startsWith($message, 'http status: 403')) { + echo "Please Check your AccessKeyId and AccessKeySecret" . "\n"; + exit(0); + } elseif (strpos($message, "BucketAlreadyExists") !== false) { + echo "Bucket already exists. Please check whether the bucket belongs to you, or it was visited with correct endpoint. " . "\n"; + exit(0); + } + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + } + + public static function println($message) + { + if (!empty($message)) { + echo strval($message) . "\n"; + } + } +} + +Common::createBucket(); diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/Config.php b/source/vendor/aliyuncs/oss-sdk-php/samples/Config.php new file mode 100644 index 0000000..35c0dc7 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/Config.php @@ -0,0 +1,15 @@ +uploadFile($bucketName, $object, "example.jpg"); + +// 图片缩放 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/resize,m_fixed,h_100,w_100", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageResize",$download_file); + +// 图片裁剪 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/crop,w_100,h_100,x_100,y_100,r_1", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("iamgeCrop", $download_file); + +// 图片旋转 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/rotate,90", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageRotate", $download_file); + +// 图片锐化 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/sharpen,100", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageSharpen", $download_file); + +// 图片水印 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/watermark,text_SGVsbG8g5Zu-54mH5pyN5YqhIQ", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageWatermark", $download_file); + +// 图片格式转换 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/format,png", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageFormat", $download_file); + +// 获取图片信息 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => $download_file, + OssClient::OSS_PROCESS => "image/info", ); +$ossClient->getObject($bucketName, $object, $options); +printImage("imageInfo", $download_file); + + +/** + * 生成一个带签名的可用于浏览器直接打开的url, URL的有效期是3600秒 + */ + $timeout = 3600; +$options = array( + OssClient::OSS_PROCESS => "image/resize,m_lfit,h_100,w_100", + ); +$signedUrl = $ossClient->signUrl($bucketName, $object, $timeout, "GET", $options); +Common::println("rtmp url: \n" . $signedUrl); + +//最后删除上传的$object +$ossClient->deleteObject($bucketName, $object); + +function printImage($func, $imageFile) +{ + $array = getimagesize($imageFile); + Common::println("$func, image width: " . $array[0]); + Common::println("$func, image height: " . $array[1]); + Common::println("$func, image type: " . ($array[2] === 2 ? 'jpg' : 'png')); + Common::println("$func, image size: " . ceil(filesize($imageFile))); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/LiveChannel.php b/source/vendor/aliyuncs/oss-sdk-php/samples/LiveChannel.php new file mode 100644 index 0000000..2f7d3a8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/LiveChannel.php @@ -0,0 +1,125 @@ + 'live channel test', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); +$info = $ossClient->putBucketLiveChannel($bucket, 'test_rtmp_live', $config); +Common::println("bucket $bucket liveChannel created:\n" . +"live channel name: ". $info->getName() . "\n" . +"live channel description: ". $info->getDescription() . "\n" . +"publishurls: ". $info->getPublishUrls()[0] . "\n" . +"playurls: ". $info->getPlayUrls()[0] . "\n"); + +/** + 对创建好的频道,可以使用listBucketLiveChannels来进行列举已达到管理的目的。 + prefix可以按照前缀过滤list出来的频道。 + max_keys表示迭代器内部一次list出来的频道的最大数量,这个值最大不能超过1000,不填写的话默认为100。 + */ +$list = $ossClient->listBucketLiveChannels($bucket); +Common::println("bucket $bucket listLiveChannel:\n" . +"list live channel prefix: ". $list->getPrefix() . "\n" . +"list live channel marker: ". $list->getMarker() . "\n" . +"list live channel maxkey: ". $list->getMaxKeys() . "\n" . +"list live channel IsTruncated: ". $list->getIsTruncated() . "\n" . +"list live channel getNextMarker: ". $list->getNextMarker() . "\n"); + +foreach($list->getChannelList() as $list) +{ + Common::println("bucket $bucket listLiveChannel:\n" . + "list live channel IsTruncated: ". $list->getName() . "\n" . + "list live channel Description: ". $list->getDescription() . "\n" . + "list live channel Status: ". $list->getStatus() . "\n" . + "list live channel getNextMarker: ". $list->getLastModified() . "\n"); +} +/** + 创建直播频道之后拿到推流用的play_url(rtmp推流的url,如果Bucket不是公共读写权限那么还需要带上签名,见下文示例)和推流用的publish_url(推流产生的m3u8文件的url) + */ +$play_url = $ossClient->signRtmpUrl($bucket, "test_rtmp_live", 3600, array('params' => array('playlistName' => 'playlist.m3u8'))); +Common::println("bucket $bucket rtmp url: \n" . $play_url); +$play_url = $ossClient->signRtmpUrl($bucket, "test_rtmp_live", 3600); +Common::println("bucket $bucket rtmp url: \n" . $play_url); + +/** + 创建好直播频道,如果想把这个频道禁用掉(断掉正在推的流或者不再允许向一个地址推流),应该使用putLiveChannelStatus接口,将频道的status改成“Disabled”,如果要将一个禁用状态的频道启用,那么也是调用这个接口,将status改成“Enabled” + */ +$resp = $ossClient->putLiveChannelStatus($bucket, "test_rtmp_live", "enabled"); + +/** + 创建好直播频道之后调用getLiveChannelInfo可以得到频道相关的信息 + */ +$info = $ossClient->getLiveChannelInfo($bucket, 'test_rtmp_live'); +Common::println("bucket $bucket LiveChannelInfo:\n" . +"live channel info description: ". $info->getDescription() . "\n" . +"live channel info status: ". $info->getStatus() . "\n" . +"live channel info type: ". $info->getType() . "\n" . +"live channel info fragDuration: ". $info->getFragDuration() . "\n" . +"live channel info fragCount: ". $info->getFragCount() . "\n" . +"live channel info playListName: ". $info->getPlayListName() . "\n"); + +/** + 如果想查看一个频道历史推流记录,可以调用getLiveChannelHistory。目前最多可以看到10次推流的记录 + */ +$history = $ossClient->getLiveChannelHistory($bucket, "test_rtmp_live"); +if (count($history->getLiveRecordList()) != 0) +{ + foreach($history->getLiveRecordList() as $recordList) + { + Common::println("bucket $bucket liveChannelHistory:\n" . + "live channel history startTime: ". $recordList->getStartTime() . "\n" . + "live channel history endTime: ". $recordList->getEndTime() . "\n" . + "live channel history remoteAddr: ". $recordList->getRemoteAddr() . "\n"); + } +} + +/** + 对于正在推流的频道调用get_live_channel_stat可以获得流的状态信息。 + 如果频道正在推流,那么stat_result中的所有字段都有意义。 + 如果频道闲置或者处于“Disabled”状态,那么status为“Idle”或“Disabled”,其他字段无意义。 + */ +$status = $ossClient->getLiveChannelStatus($bucket, "test_rtmp_live"); +Common::println("bucket $bucket listLiveChannel:\n" . +"live channel status status: ". $status->getStatus() . "\n" . +"live channel status ConnectedTime: ". $status->getConnectedTime() . "\n" . +"live channel status VideoWidth: ". $status->getVideoWidth() . "\n" . +"live channel status VideoHeight: ". $status->getVideoHeight() . "\n" . +"live channel status VideoFrameRate: ". $status->getVideoFrameRate() . "\n" . +"live channel status VideoBandwidth: ". $status->getVideoBandwidth() . "\n" . +"live channel status VideoCodec: ". $status->getVideoCodec() . "\n" . +"live channel status AudioBandwidth: ". $status->getAudioBandwidth() . "\n" . +"live channel status AudioSampleRate: ". $status->getAudioSampleRate() . "\n" . +"live channel status AdioCodec: ". $status->getAudioCodec() . "\n"); + +/** + * 如果希望利用直播推流产生的ts文件生成一个点播列表,可以使用postVodPlaylist方法。 + * 指定起始时间为当前时间减去60秒,结束时间为当前时间,这意味着将生成一个长度为60秒的点播视频。 + * 播放列表指定为“vod_playlist.m3u8”,也就是说这个接口调用成功之后会在OSS上生成一个名叫“vod_playlist.m3u8”的播放列表文件。 + */ +$current_time = time(); +$ossClient->postVodPlaylist($bucket, + "test_rtmp_live", "vod_playlist.m3u8", + array('StartTime' => $current_time - 60, + 'EndTime' => $current_time) +); + +/** + * 如果一个直播频道已经不打算再使用了,那么可以调用delete_live_channel来删除频道。 + */ +$ossClient->deleteBucketLiveChannel($bucket, "test_rtmp_live"); diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/MultipartUpload.php b/source/vendor/aliyuncs/oss-sdk-php/samples/MultipartUpload.php new file mode 100644 index 0000000..e8d69a3 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/MultipartUpload.php @@ -0,0 +1,182 @@ +multiuploadFile($bucket, "file.php", __FILE__, array()); +Common::println("local file " . __FILE__ . " is uploaded to the bucket $bucket, file.php"); + + +// 上传本地目录到bucket内的targetdir子目录中 +$ossClient->uploadDir($bucket, "targetdir", __DIR__); +Common::println("local dir " . __DIR__ . " is uploaded to the bucket $bucket, targetdir/"); + + +// 列出当前未完成的分片上传 +$listMultipartUploadInfo = $ossClient->listMultipartUploads($bucket, array()); + + +//******************************* 完整用法参考下面函数 **************************************************** + +multiuploadFile($ossClient, $bucket); +putObjectByRawApis($ossClient, $bucket); +uploadDir($ossClient, $bucket); +listMultipartUploads($ossClient, $bucket); + +/** + * 通过multipart上传文件 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function multiuploadFile($ossClient, $bucket) +{ + $object = "test/multipart-test.txt"; + $file = __FILE__; + $options = array(); + + try { + $ossClient->multiuploadFile($bucket, $object, $file, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 使用基本的api分阶段进行分片上传 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @throws OssException + */ +function putObjectByRawApis($ossClient, $bucket) +{ + $object = "test/multipart-test.txt"; + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $uploadId = $ossClient->initiateMultipartUpload($bucket, $object); + } catch (OssException $e) { + printf(__FUNCTION__ . ": initiateMultipartUpload FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": initiateMultipartUpload OK" . "\n"); + /* + * step 2. 上传分片 + */ + $partSize = 10 * 1024 * 1024; + $uploadFile = __FILE__; + $uploadFileSize = filesize($uploadFile); + $pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize); + $responseUploadPart = array(); + $uploadPosition = 0; + $isCheckMd5 = true; + foreach ($pieces as $i => $piece) { + $fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO]; + $toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1; + $upOptions = array( + $ossClient::OSS_FILE_UPLOAD => $uploadFile, + $ossClient::OSS_PART_NUM => ($i + 1), + $ossClient::OSS_SEEK_TO => $fromPos, + $ossClient::OSS_LENGTH => $toPos - $fromPos + 1, + $ossClient::OSS_CHECK_MD5 => $isCheckMd5, + ); + if ($isCheckMd5) { + $contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos); + $upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5; + } + //2. 将每一分片上传到OSS + try { + $responseUploadPart[] = $ossClient->uploadPart($bucket, $object, $uploadId, $upOptions); + } catch (OssException $e) { + printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPart - part#{$i} FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + printf(__FUNCTION__ . ": initiateMultipartUpload, uploadPart - part#{$i} OK\n"); + } + $uploadParts = array(); + foreach ($responseUploadPart as $i => $eTag) { + $uploadParts[] = array( + 'PartNumber' => ($i + 1), + 'ETag' => $eTag, + ); + } + /** + * step 3. 完成上传 + */ + try { + $ossClient->completeMultipartUpload($bucket, $object, $uploadId, $uploadParts); + } catch (OssException $e) { + printf(__FUNCTION__ . ": completeMultipartUpload FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + printf(__FUNCTION__ . ": completeMultipartUpload OK\n"); +} + +/** + * 按照目录上传文件 + * + * @param OssClient $ossClient OssClient + * @param string $bucket 存储空间名称 + * + */ +function uploadDir($ossClient, $bucket) +{ + $localDirectory = "."; + $prefix = "samples/codes"; + try { + $ossClient->uploadDir($bucket, $prefix, $localDirectory); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + printf(__FUNCTION__ . ": completeMultipartUpload OK\n"); +} + +/** + * 获取当前未完成的分片上传列表 + * + * @param $ossClient OssClient + * @param $bucket string + */ +function listMultipartUploads($ossClient, $bucket) +{ + $options = array( + 'max-uploads' => 100, + 'key-marker' => '', + 'prefix' => '', + 'upload-id-marker' => '' + ); + try { + $listMultipartUploadInfo = $ossClient->listMultipartUploads($bucket, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": listMultipartUploads FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + printf(__FUNCTION__ . ": listMultipartUploads OK\n"); + $listUploadInfo = $listMultipartUploadInfo->getUploads(); + var_dump($listUploadInfo); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/Object.php b/source/vendor/aliyuncs/oss-sdk-php/samples/Object.php new file mode 100644 index 0000000..3bf024b --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/Object.php @@ -0,0 +1,517 @@ +putObject($bucket, "b.file", "hi, oss"); +Common::println("b.file is created"); +Common::println($result['x-oss-request-id']); +Common::println($result['etag']); +Common::println($result['content-md5']); +Common::println($result['body']); + +// 上传本地文件 +$result = $ossClient->uploadFile($bucket, "c.file", __FILE__); +Common::println("c.file is created"); +Common::println("b.file is created"); +Common::println($result['x-oss-request-id']); +Common::println($result['etag']); +Common::println($result['content-md5']); +Common::println($result['body']); + +// 下载object到本地变量 +$content = $ossClient->getObject($bucket, "b.file"); +Common::println("b.file is fetched, the content is: " . $content); + +// 给object添加symlink +$content = $ossClient->putSymlink($bucket, "test-symlink", "b.file"); +Common::println("test-symlink is created"); +Common::println($result['x-oss-request-id']); +Common::println($result['etag']); + +// 获取symlink +$content = $ossClient->getSymlink($bucket, "test-symlink"); +Common::println("test-symlink refer to : " . $content[OssClient::OSS_SYMLINK_TARGET]); + +// 下载object到本地文件 +$options = array( + OssClient::OSS_FILE_DOWNLOAD => "./c.file.localcopy", +); +$ossClient->getObject($bucket, "c.file", $options); +Common::println("b.file is fetched to the local file: c.file.localcopy"); +Common::println("b.file is created"); + +// 拷贝object +$result = $ossClient->copyObject($bucket, "c.file", $bucket, "c.file.copy"); +Common::println("lastModifiedTime: " . $result[0]); +Common::println("ETag: " . $result[1]); + +// 判断object是否存在 +$doesExist = $ossClient->doesObjectExist($bucket, "c.file.copy"); +Common::println("file c.file.copy exist? " . ($doesExist ? "yes" : "no")); + +// 删除object +$result = $ossClient->deleteObject($bucket, "c.file.copy"); +Common::println("c.file.copy is deleted"); +Common::println("b.file is created"); +Common::println($result['x-oss-request-id']); + +// 判断object是否存在 +$doesExist = $ossClient->doesObjectExist($bucket, "c.file.copy"); +Common::println("file c.file.copy exist? " . ($doesExist ? "yes" : "no")); + +// 批量删除object +$result = $ossClient->deleteObjects($bucket, array("b.file", "c.file")); +foreach($result as $object) + Common::println($object); + +sleep(2); +unlink("c.file.localcopy"); + +//******************************* 完整用法参考下面函数 **************************************************** + +listObjects($ossClient, $bucket); +listAllObjects($ossClient, $bucket); +createObjectDir($ossClient, $bucket); +putObject($ossClient, $bucket); +uploadFile($ossClient, $bucket); +getObject($ossClient, $bucket); +getObjectToLocalFile($ossClient, $bucket); +copyObject($ossClient, $bucket); +modifyMetaForObject($ossClient, $bucket); +getObjectMeta($ossClient, $bucket); +deleteObject($ossClient, $bucket); +deleteObjects($ossClient, $bucket); +doesObjectExist($ossClient, $bucket); +getSymlink($ossClient, $bucket); +putSymlink($ossClient, $bucket); +/** + * 创建虚拟目录 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function createObjectDir($ossClient, $bucket) +{ + try { + $ossClient->createObjectDir($bucket, "dir"); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 把本地变量的内容到文件 + * + * 简单上传,上传指定变量的内存值作为object的内容 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putObject($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $content = file_get_contents(__FILE__); + $options = array(); + try { + $ossClient->putObject($bucket, $object, $content, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + + +/** + * 上传指定的本地文件内容 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function uploadFile($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $filePath = __FILE__; + $options = array(); + + try { + $ossClient->uploadFile($bucket, $object, $filePath, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 列出Bucket内所有目录和文件, 注意如果符合条件的文件数目超过设置的max-keys, 用户需要使用返回的nextMarker作为入参,通过 + * 循环调用ListObjects得到所有的文件,具体操作见下面的 listAllObjects 示例 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function listObjects($ossClient, $bucket) +{ + $prefix = 'oss-php-sdk-test/'; + $delimiter = '/'; + $nextMarker = ''; + $maxkeys = 1000; + $options = array( + 'delimiter' => $delimiter, + 'prefix' => $prefix, + 'max-keys' => $maxkeys, + 'marker' => $nextMarker, + ); + try { + $listObjectInfo = $ossClient->listObjects($bucket, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + $objectList = $listObjectInfo->getObjectList(); // 文件列表 + $prefixList = $listObjectInfo->getPrefixList(); // 目录列表 + if (!empty($objectList)) { + print("objectList:\n"); + foreach ($objectList as $objectInfo) { + print($objectInfo->getKey() . "\n"); + } + } + if (!empty($prefixList)) { + print("prefixList: \n"); + foreach ($prefixList as $prefixInfo) { + print($prefixInfo->getPrefix() . "\n"); + } + } +} + +/** + * 列出Bucket内所有目录和文件, 根据返回的nextMarker循环得到所有Objects + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function listAllObjects($ossClient, $bucket) +{ + //构造dir下的文件和虚拟目录 + for ($i = 0; $i < 100; $i += 1) { + $ossClient->putObject($bucket, "dir/obj" . strval($i), "hi"); + $ossClient->createObjectDir($bucket, "dir/obj" . strval($i)); + } + + $prefix = 'dir/'; + $delimiter = '/'; + $nextMarker = ''; + $maxkeys = 30; + + while (true) { + $options = array( + 'delimiter' => $delimiter, + 'prefix' => $prefix, + 'max-keys' => $maxkeys, + 'marker' => $nextMarker, + ); + var_dump($options); + try { + $listObjectInfo = $ossClient->listObjects($bucket, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + // 得到nextMarker,从上一次listObjects读到的最后一个文件的下一个文件开始继续获取文件列表 + $nextMarker = $listObjectInfo->getNextMarker(); + $listObject = $listObjectInfo->getObjectList(); + $listPrefix = $listObjectInfo->getPrefixList(); + var_dump(count($listObject)); + var_dump(count($listPrefix)); + if ($nextMarker === '') { + break; + } + } +} + +/** + * 获取object的内容 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getObject($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $options = array(); + try { + $content = $ossClient->getObject($bucket, $object, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + if (file_get_contents(__FILE__) === $content) { + print(__FUNCTION__ . ": FileContent checked OK" . "\n"); + } else { + print(__FUNCTION__ . ": FileContent checked FAILED" . "\n"); + } +} + +/** + * put symlink + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function putSymlink($ossClient, $bucket) +{ + $symlink = "test-samples-symlink"; + $object = "test-samples-object"; + try { + $ossClient->putObject($bucket, $object, 'test-content'); + $ossClient->putSymlink($bucket, $symlink, $object); + $content = $ossClient->getObject($bucket, $symlink); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + if ($content == 'test-content') { + print(__FUNCTION__ . ": putSymlink checked OK" . "\n"); + } else { + print(__FUNCTION__ . ": putSymlink checked FAILED" . "\n"); + } +} + +/** + * 获取symlink + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getSymlink($ossClient, $bucket) +{ + $symlink = "test-samples-symlink"; + $object = "test-samples-object"; + try { + $ossClient->putObject($bucket, $object, 'test-content'); + $ossClient->putSymlink($bucket, $symlink, $object); + $content = $ossClient->getSymlink($bucket, $symlink); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + if ($content[OssClient::OSS_SYMLINK_TARGET]) { + print(__FUNCTION__ . ": getSymlink checked OK" . "\n"); + } else { + print(__FUNCTION__ . ": getSymlink checked FAILED" . "\n"); + } +} + +/** + * get_object_to_local_file + * + * 获取object + * 将object下载到指定的文件 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getObjectToLocalFile($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $localfile = "upload-test-object-name.txt"; + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $localfile, + ); + + try { + $ossClient->getObject($bucket, $object, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK, please check localfile: 'upload-test-object-name.txt'" . "\n"); + if (file_get_contents($localfile) === file_get_contents(__FILE__)) { + print(__FUNCTION__ . ": FileContent checked OK" . "\n"); + } else { + print(__FUNCTION__ . ": FileContent checked FAILED" . "\n"); + } + if (file_exists($localfile)) { + unlink($localfile); + } +} + +/** + * 拷贝object + * 当目的object和源object完全相同时,表示修改object的meta信息 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function copyObject($ossClient, $bucket) +{ + $fromBucket = $bucket; + $fromObject = "oss-php-sdk-test/upload-test-object-name.txt"; + $toBucket = $bucket; + $toObject = $fromObject . '.copy'; + $options = array(); + + try { + $ossClient->copyObject($fromBucket, $fromObject, $toBucket, $toObject, $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 修改Object Meta + * 利用copyObject接口的特性:当目的object和源object完全相同时,表示修改object的meta信息 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function modifyMetaForObject($ossClient, $bucket) +{ + $fromBucket = $bucket; + $fromObject = "oss-php-sdk-test/upload-test-object-name.txt"; + $toBucket = $bucket; + $toObject = $fromObject; + $copyOptions = array( + OssClient::OSS_HEADERS => array( + 'Cache-Control' => 'max-age=60', + 'Content-Disposition' => 'attachment; filename="xxxxxx"', + ), + ); + try { + $ossClient->copyObject($fromBucket, $fromObject, $toBucket, $toObject, $copyOptions); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 获取object meta, 也就是getObjectMeta接口 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function getObjectMeta($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $objectMeta = $ossClient->getObjectMeta($bucket, $object); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + if (isset($objectMeta[strtolower('Content-Disposition')]) && + 'attachment; filename="xxxxxx"' === $objectMeta[strtolower('Content-Disposition')] + ) { + print(__FUNCTION__ . ": ObjectMeta checked OK" . "\n"); + } else { + print(__FUNCTION__ . ": ObjectMeta checked FAILED" . "\n"); + } +} + +/** + * 删除object + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteObject($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $ossClient->deleteObject($bucket, $object); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + + +/** + * 批量删除object + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function deleteObjects($ossClient, $bucket) +{ + $objects = array(); + $objects[] = "oss-php-sdk-test/upload-test-object-name.txt"; + $objects[] = "oss-php-sdk-test/upload-test-object-name.txt.copy"; + try { + $ossClient->deleteObjects($bucket, $objects); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); +} + +/** + * 判断object是否存在 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + */ +function doesObjectExist($ossClient, $bucket) +{ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $exist = $ossClient->doesObjectExist($bucket, $object); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + var_dump($exist); +} + diff --git a/source/vendor/aliyuncs/oss-sdk-php/samples/RunAll.php b/source/vendor/aliyuncs/oss-sdk-php/samples/RunAll.php new file mode 100644 index 0000000..a4d6d9b --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/samples/RunAll.php @@ -0,0 +1,13 @@ +uploadFile($bucket, "a.file", __FILE__); + +// 生成GetObject的签名url,用户可以使用这个url直接在浏览器下载 +$signedUrl = $ossClient->signUrl($bucket, "a.file", 3600); +Common::println($signedUrl); + +// 生成用于putObject的签名URL,用户可以直接用put方法使用这个url上传文件到 "a.file" +$signedUrl = $ossClient->signUrl($bucket, "a.file", "3600", "PUT"); +Common::println($signedUrl); + +// 生成从本地文件上传PutObject的签名url, 用户可以直接使用这个url把本地文件上传到 "a.file" +$signedUrl = $ossClient->signUrl($bucket, "a.file", 3600, "PUT", array('Content-Type' => 'txt')); +Common::println($signedUrl); + +//******************************* 完整用法参考下面函数 **************************************************** + +getSignedUrlForPuttingObject($ossClient, $bucket); +getSignedUrlForPuttingObjectFromFile($ossClient, $bucket); +getSignedUrlForGettingObject($ossClient, $bucket); + +/** + * 生成GetObject的签名url,主要用于私有权限下的读访问控制 + * + * @param $ossClient OssClient OssClient实例 + * @param $bucket string 存储空间名称 + * @return null + */ +function getSignedUrlForGettingObject($ossClient, $bucket) +{ + $object = "test/test-signature-test-upload-and-download.txt"; + $timeout = 3600; + try { + $signedUrl = $ossClient->signUrl($bucket, $object, $timeout); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": signedUrl: " . $signedUrl . "\n"); + /** + * 可以类似的代码来访问签名的URL,也可以输入到浏览器中去访问 + */ + $request = new RequestCore($signedUrl); + $request->set_method('GET'); + $request->add_header('Content-Type', ''); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), $request->get_response_body(), $request->get_response_code()); + if ($res->isOK()) { + print(__FUNCTION__ . ": OK" . "\n"); + } else { + print(__FUNCTION__ . ": FAILED" . "\n"); + }; +} + +/** + * 生成PutObject的签名url,主要用于私有权限下的写访问控制 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @return null + * @throws OssException + */ +function getSignedUrlForPuttingObject($ossClient, $bucket) +{ + $object = "test/test-signature-test-upload-and-download.txt"; + $timeout = 3600; + $options = NULL; + try { + $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT"); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": signedUrl: " . $signedUrl . "\n"); + $content = file_get_contents(__FILE__); + + $request = new RequestCore($signedUrl); + $request->set_method('PUT'); + $request->add_header('Content-Type', ''); + $request->add_header('Content-Length', strlen($content)); + $request->set_body($content); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), + $request->get_response_body(), $request->get_response_code()); + if ($res->isOK()) { + print(__FUNCTION__ . ": OK" . "\n"); + } else { + print(__FUNCTION__ . ": FAILED" . "\n"); + }; +} + +/** + * 生成PutObject的签名url,主要用于私有权限下的写访问控制, 用户可以利用生成的signedUrl + * 从文件上传文件 + * + * @param OssClient $ossClient OssClient实例 + * @param string $bucket 存储空间名称 + * @throws OssException + */ +function getSignedUrlForPuttingObjectFromFile($ossClient, $bucket) +{ + $file = __FILE__; + $object = "test/test-signature-test-upload-and-download.txt"; + $timeout = 3600; + $options = array('Content-Type' => 'txt'); + try { + $signedUrl = $ossClient->signUrl($bucket, $object, $timeout, "PUT", $options); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": signedUrl: " . $signedUrl . "\n"); + + $request = new RequestCore($signedUrl); + $request->set_method('PUT'); + $request->add_header('Content-Type', 'txt'); + $request->set_read_file($file); + $request->set_read_stream_size(filesize($file)); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), + $request->get_response_body(), $request->get_response_code()); + if ($res->isOK()) { + print(__FUNCTION__ . ": OK" . "\n"); + } else { + print(__FUNCTION__ . ": FAILED" . "\n"); + }; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/MimeTypes.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/MimeTypes.php new file mode 100644 index 0000000..e9b88ff --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/MimeTypes.php @@ -0,0 +1,262 @@ + 1) { + $ext = strtolower(end($parts)); + if (isset(self::$mime_types[$ext])) { + return self::$mime_types[$ext]; + } + } + + return null; + } + + private static $mime_types = array( + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'apk' => 'application/vnd.android.package-archive', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'doc' => 'application/msword', + 'ogg' => 'audio/ogg', + 'pdf' => 'application/pdf', + 'rtf' => 'text/rtf', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'sxw' => 'application/vnd.sun.xml.writer', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sxc' => 'application/vnd.sun.xml.calc', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'sxd' => 'application/vnd.sun.xml.draw', + 'std' => 'application/vnd.sun.xml.draw.template', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxm' => 'application/vnd.sun.xml.math', + 'sis' => 'application/vnd.symbian.install', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'bcpio' => 'application/x-bcpio', + 'torrent' => 'application/x-bittorrent', + 'bz2' => 'application/x-bzip2', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'application/x-csh', + 'dvi' => 'application/x-dvi', + 'spl' => 'application/x-futuresplash', + 'gtar' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'jar' => 'application/java-archive', + 'jnlp' => 'application/x-java-jnlp-file', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'ksp' => 'application/x-kspread', + 'chrt' => 'application/x-kchart', + 'kil' => 'application/x-killustrator', + 'latex' => 'application/x-latex', + 'rpm' => 'application/x-rpm', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'zip' => 'application/zip', + 'm3u' => 'audio/x-mpegurl', + 'ra' => 'audio/x-pn-realaudio', + 'wav' => 'audio/x-wav', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'png' => 'image/png', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'css' => 'text/css', + 'rtx' => 'text/richtext', + 'tsv' => 'text/tab-separated-values', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'etx' => 'text/x-setext', + 'mxu' => 'video/vnd.mpegurl', + 'flv' => 'video/x-flv', + 'wm' => 'video/x-ms-wm', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wvx' => 'video/x-ms-wvx', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + '3gp' => 'video/3gpp', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'atom' => 'application/atom+xml', + 'au' => 'audio/basic', + 'bin' => 'application/octet-stream', + 'cdf' => 'application/x-netcdf', + 'cgm' => 'image/cgm', + 'class' => 'application/octet-stream', + 'dcr' => 'application/x-director', + 'dif' => 'video/x-dv', + 'dir' => 'application/x-director', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/octet-stream', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dtd' => 'application/xml-dtd', + 'dv' => 'video/x-dv', + 'dxr' => 'application/x-director', + 'eps' => 'application/postscript', + 'exe' => 'application/octet-stream', + 'ez' => 'application/andrew-inset', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'gz' => 'application/x-gzip', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'iges' => 'model/iges', + 'igs' => 'model/iges', + 'jp2' => 'image/jp2', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'kar' => 'audio/midi', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'm4a' => 'audio/mp4a-latm', + 'm4p' => 'audio/mp4a-latm', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'mac' => 'image/x-macpaint', + 'mathml' => 'application/mathml+xml', + 'mesh' => 'model/mesh', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mov' => 'video/quicktime', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpga' => 'audio/mpeg', + 'msh' => 'model/mesh', + 'nc' => 'application/x-netcdf', + 'oda' => 'application/oda', + 'ogv' => 'video/ogv', + 'pct' => 'image/pict', + 'pic' => 'image/pict', + 'pict' => 'image/pict', + 'pnt' => 'image/x-macpaint', + 'pntg' => 'image/x-macpaint', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'qti' => 'image/x-quicktime', + 'qtif' => 'image/x-quicktime', + 'ram' => 'audio/x-pn-realaudio', + 'rdf' => 'application/rdf+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'roff' => 'application/x-troff', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'silo' => 'model/mesh', + 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'skp' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'snd' => 'audio/basic', + 'so' => 'application/octet-stream', + 'svg' => 'image/svg+xml', + 't' => 'application/x-troff', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tr' => 'application/x-troff', + 'txt' => 'text/plain', + 'vrml' => 'model/vrml', + 'vxml' => 'application/voicexml+xml', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wrl' => 'model/vrml', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xml' => 'application/xml', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + ); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssException.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssException.php new file mode 100644 index 0000000..b0e9e8b --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssException.php @@ -0,0 +1,54 @@ +details = $details; + } else { + $message = $details; + parent::__construct($message); + } + } + + public function getHTTPStatus() + { + return isset($this->details['status']) ? $this->details['status'] : ''; + } + + public function getRequestId() + { + return isset($this->details['request-id']) ? $this->details['request-id'] : ''; + } + + public function getErrorCode() + { + return isset($this->details['code']) ? $this->details['code'] : ''; + } + + public function getErrorMessage() + { + return isset($this->details['message']) ? $this->details['message'] : ''; + } + + public function getDetails() + { + return isset($this->details['body']) ? $this->details['body'] : ''; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssUtil.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssUtil.php new file mode 100644 index 0000000..6e5d413 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Core/OssUtil.php @@ -0,0 +1,461 @@ + $value) { + if (is_string($key) && !is_array($value)) { + $temp[] = rawurlencode($key) . '=' . rawurlencode($value); + } + } + return implode('&', $temp); + } + + /** + * 转义字符替换 + * + * @param string $subject + * @return string + */ + public static function sReplace($subject) + { + $search = array('<', '>', '&', '\'', '"'); + $replace = array('<', '>', '&', ''', '"'); + return str_replace($search, $replace, $subject); + } + + /** + * 检查是否是中文编码 + * + * @param $str + * @return int + */ + public static function chkChinese($str) + { + return preg_match('/[\x80-\xff]./', $str); + } + + /** + * 检测是否GB2312编码 + * + * @param string $str + * @return boolean false UTF-8编码 TRUE GB2312编码 + */ + public static function isGb2312($str) + { + for ($i = 0; $i < strlen($str); $i++) { + $v = ord($str[$i]); + if ($v > 127) { + if (($v >= 228) && ($v <= 233)) { + if (($i + 2) >= (strlen($str) - 1)) return true; // not enough characters + $v1 = ord($str[$i + 1]); + $v2 = ord($str[$i + 2]); + if (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191)) + return false; + else + return true; + } + } + } + return false; + } + + /** + * 检测是否GBK编码 + * + * @param string $str + * @param boolean $gbk + * @return boolean + */ + public static function checkChar($str, $gbk = true) + { + for ($i = 0; $i < strlen($str); $i++) { + $v = ord($str[$i]); + if ($v > 127) { + if (($v >= 228) && ($v <= 233)) { + if (($i + 2) >= (strlen($str) - 1)) return $gbk ? true : FALSE; // not enough characters + $v1 = ord($str[$i + 1]); + $v2 = ord($str[$i + 2]); + if ($gbk) { + return (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191)) ? FALSE : TRUE;//GBK + } else { + return (($v1 >= 128) && ($v1 <= 191) && ($v2 >= 128) && ($v2 <= 191)) ? TRUE : FALSE; + } + } + } + } + return $gbk ? TRUE : FALSE; + } + + /** + * 检验bucket名称是否合法 + * bucket的命名规范: + * 1. 只能包括小写字母,数字 + * 2. 必须以小写字母或者数字开头 + * 3. 长度必须在3-63字节之间 + * + * @param string $bucket Bucket名称 + * @return boolean + */ + public static function validateBucket($bucket) + { + $pattern = '/^[a-z0-9][a-z0-9-]{2,62}$/'; + if (!preg_match($pattern, $bucket)) { + return false; + } + return true; + } + + /** + * 检验object名称是否合法 + * object命名规范: + * 1. 规则长度必须在1-1023字节之间 + * 2. 使用UTF-8编码 + * 3. 不能以 "/" "\\"开头 + * + * @param string $object Object名称 + * @return boolean + */ + public static function validateObject($object) + { + $pattern = '/^.{1,1023}$/'; + if (empty($object) || !preg_match($pattern, $object) || + self::startsWith($object, '/') || self::startsWith($object, '\\') + ) { + return false; + } + return true; + } + + + /** + * 判断字符串$str是不是以$findMe开始 + * + * @param string $str + * @param string $findMe + * @return bool + */ + public static function startsWith($str, $findMe) + { + if (strpos($str, $findMe) === 0) { + return true; + } else { + return false; + } + } + + /** + * 生成createBucketXmlBody接口的xml消息 + * + * @param string $storageClass + * @return string + */ + public static function createBucketXmlBody($storageClass) + { + $xml = new \SimpleXMLElement(''); + $xml->addChild('StorageClass', $storageClass); + return $xml->asXML(); + } + + /** + * 检验$options + * + * @param array $options + * @throws OssException + * @return boolean + */ + public static function validateOptions($options) + { + //$options + if ($options != NULL && !is_array($options)) { + throw new OssException ($options . ':' . 'option must be array'); + } + } + + /** + * 检查上传文件的内容是否合法 + * + * @param $content string + * @throws OssException + */ + public static function validateContent($content) + { + if (empty($content)) { + throw new OssException("http body content is invalid"); + } + } + + /** + * 校验BUCKET/OBJECT/OBJECT GROUP是否为空 + * + * @param string $name + * @param string $errMsg + * @throws OssException + * @return void + */ + public static function throwOssExceptionWithMessageIfEmpty($name, $errMsg) + { + if (empty($name)) { + throw new OssException($errMsg); + } + } + + /** + * 仅供测试使用的接口,请勿使用 + * + * @param $filename + * @param $size + */ + public static function generateFile($filename, $size) + { + if (file_exists($filename) && $size == filesize($filename)) { + echo $filename . " already exists, no need to create again. "; + return; + } + $part_size = 1 * 1024 * 1024; + $fp = fopen($filename, "w"); + $characters = << 0) { + if ($size < $part_size) { + $write_size = $size; + } else { + $write_size = $part_size; + } + $size -= $write_size; + $a = $characters[rand(0, $charactersLength - 1)]; + $content = str_repeat($a, $write_size); + $flag = fwrite($fp, $content); + if (!$flag) { + echo "write to " . $filename . " failed.
    "; + break; + } + } + } else { + echo "open " . $filename . " failed.
    "; + } + fclose($fp); + } + + /** + * 得到文件的md5编码 + * + * @param $filename + * @param $from_pos + * @param $to_pos + * @return string + */ + public static function getMd5SumForFile($filename, $from_pos, $to_pos) + { + $content_md5 = ""; + if (($to_pos - $from_pos) > self::OSS_MAX_PART_SIZE) { + return $content_md5; + } + $filesize = filesize($filename); + if ($from_pos >= $filesize || $to_pos >= $filesize || $from_pos < 0 || $to_pos < 0) { + return $content_md5; + } + + $total_length = $to_pos - $from_pos + 1; + $buffer = 8192; + $left_length = $total_length; + if (!file_exists($filename)) { + return $content_md5; + } + + if (false === $fh = fopen($filename, 'rb')) { + return $content_md5; + } + + fseek($fh, $from_pos); + $data = ''; + while (!feof($fh)) { + if ($left_length >= $buffer) { + $read_length = $buffer; + } else { + $read_length = $left_length; + } + if ($read_length <= 0) { + break; + } else { + $data .= fread($fh, $read_length); + $left_length = $left_length - $read_length; + } + } + fclose($fh); + $content_md5 = base64_encode(md5($data, true)); + return $content_md5; + } + + /** + * 检测是否windows系统,因为windows系统默认编码为GBK + * + * @return bool + */ + public static function isWin() + { + return strtoupper(substr(PHP_OS, 0, 3)) == "WIN"; + } + + /** + * 主要是由于windows系统编码是gbk,遇到中文时候,如果不进行转换处理会出现找不到文件的问题 + * + * @param $file_path + * @return string + */ + public static function encodePath($file_path) + { + if (self::chkChinese($file_path) && self::isWin()) { + $file_path = iconv('utf-8', 'gbk', $file_path); + } + return $file_path; + } + + /** + * 判断用户输入的endpoint是否是 xxx.xxx.xxx.xxx:port 或者 xxx.xxx.xxx.xxx的ip格式 + * + * @param string $endpoint 需要做判断的endpoint + * @return boolean + */ + public static function isIPFormat($endpoint) + { + $ip_array = explode(":", $endpoint); + $hostname = $ip_array[0]; + $ret = filter_var($hostname, FILTER_VALIDATE_IP); + if (!$ret) { + return false; + } else { + return true; + } + } + + /** + * 生成DeleteMultiObjects接口的xml消息 + * + * @param string[] $objects + * @param bool $quiet + * @return string + */ + public static function createDeleteObjectsXmlBody($objects, $quiet) + { + $xml = new \SimpleXMLElement(''); + $xml->addChild('Quiet', $quiet); + foreach ($objects as $object) { + $sub_object = $xml->addChild('Object'); + $object = OssUtil::sReplace($object); + $sub_object->addChild('Key', $object); + } + return $xml->asXML(); + } + + /** + * 生成CompleteMultipartUpload接口的xml消息 + * + * @param array[] $listParts + * @return string + */ + public static function createCompleteMultipartUploadXmlBody($listParts) + { + $xml = new \SimpleXMLElement(''); + foreach ($listParts as $node) { + $part = $xml->addChild('Part'); + $part->addChild('PartNumber', $node['PartNumber']); + $part->addChild('ETag', $node['ETag']); + } + return $xml->asXML(); + } + + /** + * 读取目录 + * + * @param string $dir + * @param string $exclude + * @param bool $recursive + * @return string[] + */ + public static function readDir($dir, $exclude = ".|..|.svn|.git", $recursive = false) + { + $file_list_array = array(); + $base_path = $dir; + $exclude_array = explode("|", $exclude); + $exclude_array = array_unique(array_merge($exclude_array, array('.', '..'))); + + if ($recursive) { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)) as $new_file) { + if ($new_file->isDir()) continue; + $object = str_replace($base_path, '', $new_file); + if (!in_array(strtolower($object), $exclude_array)) { + $object = ltrim($object, '/'); + if (is_file($new_file)) { + $key = md5($new_file . $object, false); + $file_list_array[$key] = array('path' => $new_file, 'file' => $object,); + } + } + } + } else if ($handle = opendir($dir)) { + while (false !== ($file = readdir($handle))) { + if (!in_array(strtolower($file), $exclude_array)) { + $new_file = $dir . '/' . $file; + $object = $file; + $object = ltrim($object, '/'); + if (is_file($new_file)) { + $key = md5($new_file . $object, false); + $file_list_array[$key] = array('path' => $new_file, 'file' => $object,); + } + } + } + closedir($handle); + } + return $file_list_array; + } + + /** + * Decode key based on the encoding type + * + * @param string $key + * @param string $encoding + * @return string + */ + public static function decodeKey($key, $encoding) + { + if ($encoding == "") { + return $key; + } + + if ($encoding == "url") { + return rawurldecode($key); + } else { + throw new OssException("Unrecognized encoding type: " . $encoding); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/LICENSE b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/LICENSE new file mode 100644 index 0000000..49b38bd --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2010 Ryan Parman, Foleeo Inc., and contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of Ryan Parman, Foleeo Inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS +AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore.php new file mode 100644 index 0000000..ddbda0d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore.php @@ -0,0 +1,896 @@ +). + */ + public $request_class = 'OSS\Http\RequestCore'; + + /** + * The default class to use for HTTP Responses (defaults to ). + */ + public $response_class = 'OSS\Http\ResponseCore'; + + /** + * Default useragent string to use. + */ + public $useragent = 'RequestCore/1.4.3'; + + /** + * File to read from while streaming up. + */ + public $read_file = null; + + /** + * The resource to read from while streaming up. + */ + public $read_stream = null; + + /** + * The size of the stream to read from. + */ + public $read_stream_size = null; + + /** + * The length already read from the stream. + */ + public $read_stream_read = 0; + + /** + * File to write to while streaming down. + */ + public $write_file = null; + + /** + * The resource to write to while streaming down. + */ + public $write_stream = null; + + /** + * Stores the intended starting seek position. + */ + public $seek_position = null; + + /** + * The location of the cacert.pem file to use. + */ + public $cacert_location = false; + + /** + * The state of SSL certificate verification. + */ + public $ssl_verification = false; + + /** + * The user-defined callback function to call when a stream is read from. + */ + public $registered_streaming_read_callback = null; + + /** + * The user-defined callback function to call when a stream is written to. + */ + public $registered_streaming_write_callback = null; + + /** + * 请求超时时间, 默认是5184000秒,6天 + * + * @var int + */ + public $timeout = 5184000; + + /** + * 连接超时时间,默认是10秒 + * + * @var int + */ + public $connect_timeout = 10; + + /*%******************************************************************************************%*/ + // CONSTANTS + + /** + * GET HTTP Method + */ + const HTTP_GET = 'GET'; + + /** + * POST HTTP Method + */ + const HTTP_POST = 'POST'; + + /** + * PUT HTTP Method + */ + const HTTP_PUT = 'PUT'; + + /** + * DELETE HTTP Method + */ + const HTTP_DELETE = 'DELETE'; + + /** + * HEAD HTTP Method + */ + const HTTP_HEAD = 'HEAD'; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR/DESTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $url (Optional) The URL to request or service endpoint to query. + * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` + * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. + * @return $this A reference to the current instance. + */ + public function __construct($url = null, $proxy = null, $helpers = null) + { + // Set some default values. + $this->request_url = $url; + $this->method = self::HTTP_GET; + $this->request_headers = array(); + $this->request_body = ''; + + // Set a new Request class if one was set. + if (isset($helpers['request']) && !empty($helpers['request'])) { + $this->request_class = $helpers['request']; + } + + // Set a new Request class if one was set. + if (isset($helpers['response']) && !empty($helpers['response'])) { + $this->response_class = $helpers['response']; + } + + if ($proxy) { + $this->set_proxy($proxy); + } + + return $this; + } + + /** + * Destructs the instance. Closes opened file handles. + * + * @return $this A reference to the current instance. + */ + public function __destruct() + { + if (isset($this->read_file) && isset($this->read_stream)) { + fclose($this->read_stream); + } + + if (isset($this->write_file) && isset($this->write_stream)) { + fclose($this->write_stream); + } + + return $this; + } + + + /*%******************************************************************************************%*/ + // REQUEST METHODS + + /** + * Sets the credentials to use for authentication. + * + * @param string $user (Required) The username to authenticate with. + * @param string $pass (Required) The password to authenticate with. + * @return $this A reference to the current instance. + */ + public function set_credentials($user, $pass) + { + $this->username = $user; + $this->password = $pass; + return $this; + } + + /** + * Adds a custom HTTP header to the cURL request. + * + * @param string $key (Required) The custom HTTP header to set. + * @param mixed $value (Required) The value to assign to the custom HTTP header. + * @return $this A reference to the current instance. + */ + public function add_header($key, $value) + { + $this->request_headers[$key] = $value; + return $this; + } + + /** + * Removes an HTTP header from the cURL request. + * + * @param string $key (Required) The custom HTTP header to set. + * @return $this A reference to the current instance. + */ + public function remove_header($key) + { + if (isset($this->request_headers[$key])) { + unset($this->request_headers[$key]); + } + return $this; + } + + /** + * Set the method type for the request. + * + * @param string $method (Required) One of the following constants: , , , , . + * @return $this A reference to the current instance. + */ + public function set_method($method) + { + $this->method = strtoupper($method); + return $this; + } + + /** + * Sets a custom useragent string for the class. + * + * @param string $ua (Required) The useragent string to use. + * @return $this A reference to the current instance. + */ + public function set_useragent($ua) + { + $this->useragent = $ua; + return $this; + } + + /** + * Set the body to send in the request. + * + * @param string $body (Required) The textual content to send along in the body of the request. + * @return $this A reference to the current instance. + */ + public function set_body($body) + { + $this->request_body = $body; + return $this; + } + + /** + * Set the URL to make the request to. + * + * @param string $url (Required) The URL to make the request to. + * @return $this A reference to the current instance. + */ + public function set_request_url($url) + { + $this->request_url = $url; + return $this; + } + + /** + * Set additional CURLOPT settings. These will merge with the default settings, and override if + * there is a duplicate. + * + * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. + * @return $this A reference to the current instance. + */ + public function set_curlopts($curlopts) + { + $this->curlopts = $curlopts; + return $this; + } + + /** + * Sets the length in bytes to read from the stream while streaming up. + * + * @param integer $size (Required) The length in bytes to read from the stream. + * @return $this A reference to the current instance. + */ + public function set_read_stream_size($size) + { + $this->read_stream_size = $size; + + return $this; + } + + /** + * Sets the resource to read from while streaming up. Reads the stream from its current position until + * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and + * . + * + * @param resource $resource (Required) The readable resource to read from. + * @param integer $size (Optional) The size of the stream to read. + * @return $this A reference to the current instance. + */ + public function set_read_stream($resource, $size = null) + { + if (!isset($size) || $size < 0) { + $stats = fstat($resource); + + if ($stats && $stats['size'] >= 0) { + $position = ftell($resource); + + if ($position !== false && $position >= 0) { + $size = $stats['size'] - $position; + } + } + } + + $this->read_stream = $resource; + + return $this->set_read_stream_size($size); + } + + /** + * Sets the file to read from while streaming up. + * + * @param string $location (Required) The readable location to read from. + * @return $this A reference to the current instance. + */ + public function set_read_file($location) + { + $this->read_file = $location; + $read_file_handle = fopen($location, 'r'); + + return $this->set_read_stream($read_file_handle); + } + + /** + * Sets the resource to write to while streaming down. + * + * @param resource $resource (Required) The writeable resource to write to. + * @return $this A reference to the current instance. + */ + public function set_write_stream($resource) + { + $this->write_stream = $resource; + + return $this; + } + + /** + * Sets the file to write to while streaming down. + * + * @param string $location (Required) The writeable location to write to. + * @return $this A reference to the current instance. + */ + public function set_write_file($location) + { + $this->write_file = $location; + } + + /** + * Set the proxy to use for making requests. + * + * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` + * @return $this A reference to the current instance. + */ + public function set_proxy($proxy) + { + $proxy = parse_url($proxy); + $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null; + $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null; + $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null; + $this->proxy = $proxy; + return $this; + } + + /** + * Set the intended starting seek position. + * + * @param integer $position (Required) The byte-position of the stream to begin reading from. + * @return $this A reference to the current instance. + */ + public function set_seek_position($position) + { + $this->seek_position = isset($position) ? (integer)$position : null; + + return $this; + } + + /** + * A callback function that is invoked by cURL for streaming up. + * + * @param resource $curl_handle (Required) The cURL handle for the request. + * @param resource $header_content (Required) The header callback result. + * @return headers from a stream. + */ + public function streaming_header_callback($curl_handle, $header_content) + { + $code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); + + if (isset($this->write_file) && intval($code) / 100 == 2 && !isset($this->write_file_handle)) + { + $this->write_file_handle = fopen($this->write_file, 'w'); + $this->set_write_stream($this->write_file_handle); + } + + $this->response_raw_headers .= $header_content; + return strlen($header_content); + } + + + /** + * Register a callback function to execute whenever a data stream is read from using + * . + * + * The user-defined callback function should accept three arguments: + * + *
      + *
    • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
    • + *
    • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
    • + *
    • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
    • + *
    + * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
      + *
    • The name of a global function to execute, passed as a string.
    • + *
    • A method to execute, passed as array('ClassName', 'MethodName').
    • + *
    • An anonymous function (PHP 5.3+).
    + * @return $this A reference to the current instance. + */ + public function register_streaming_read_callback($callback) + { + $this->registered_streaming_read_callback = $callback; + + return $this; + } + + /** + * Register a callback function to execute whenever a data stream is written to using + * . + * + * The user-defined callback function should accept two arguments: + * + *
      + *
    • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
    • + *
    • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
    • + *
    + * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
      + *
    • The name of a global function to execute, passed as a string.
    • + *
    • A method to execute, passed as array('ClassName', 'MethodName').
    • + *
    • An anonymous function (PHP 5.3+).
    + * @return $this A reference to the current instance. + */ + public function register_streaming_write_callback($callback) + { + $this->registered_streaming_write_callback = $callback; + + return $this; + } + + + /*%******************************************************************************************%*/ + // PREPARE, SEND, AND PROCESS REQUEST + + /** + * A callback function that is invoked by cURL for streaming up. + * + * @param resource $curl_handle (Required) The cURL handle for the request. + * @param resource $file_handle (Required) The open file handle resource. + * @param integer $length (Required) The maximum number of bytes to read. + * @return binary Binary data from a stream. + */ + public function streaming_read_callback($curl_handle, $file_handle, $length) + { + // Once we've sent as much as we're supposed to send... + if ($this->read_stream_read >= $this->read_stream_size) { + // Send EOF + return ''; + } + + // If we're at the beginning of an upload and need to seek... + if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) { + if (fseek($this->read_stream, $this->seek_position) !== 0) { + throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.'); + } + } + + $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size + $this->read_stream_read += strlen($read); + + $out = $read === false ? '' : $read; + + // Execute callback function + if ($this->registered_streaming_read_callback) { + call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out); + } + + return $out; + } + + /** + * A callback function that is invoked by cURL for streaming down. + * + * @param resource $curl_handle (Required) The cURL handle for the request. + * @param binary $data (Required) The data to write. + * @return integer The number of bytes written. + */ + public function streaming_write_callback($curl_handle, $data) + { + $code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); + + if (intval($code) / 100 != 2) + { + $this->response_error_body .= $data; + return strlen($data); + } + + $length = strlen($data); + $written_total = 0; + $written_last = 0; + + while ($written_total < $length) { + $written_last = fwrite($this->write_stream, substr($data, $written_total)); + + if ($written_last === false) { + return $written_total; + } + + $written_total += $written_last; + } + + // Execute callback function + if ($this->registered_streaming_write_callback) { + call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total); + } + + return $written_total; + } + + /** + * Prepares and adds the details of the cURL request. This can be passed along to a + * function. + * + * @return resource The handle for the cURL object. + * + */ + public function prep_request() + { + $curl_handle = curl_init(); + + // Set default options. + curl_setopt($curl_handle, CURLOPT_URL, $this->request_url); + curl_setopt($curl_handle, CURLOPT_FILETIME, true); + curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false); +// curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED); + curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5); + curl_setopt($curl_handle, CURLOPT_HEADER, true); + curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl_handle, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $this->connect_timeout); + curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true); + curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url); + curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent); + curl_setopt($curl_handle, CURLOPT_HEADERFUNCTION, array($this, 'streaming_header_callback')); + curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback')); + + // Verification of the SSL cert + if ($this->ssl_verification) { + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2); + } else { + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false); + } + + // chmod the file as 0755 + if ($this->cacert_location === true) { + curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem'); + } elseif (is_string($this->cacert_location)) { + curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location); + } + + // Debug mode + if ($this->debug_mode) { + curl_setopt($curl_handle, CURLOPT_VERBOSE, true); + } + + // Handle open_basedir & safe mode + if (!ini_get('safe_mode') && !ini_get('open_basedir')) { + curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); + } + + // Enable a proxy connection if requested. + if ($this->proxy) { + + $host = $this->proxy['host']; + $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : ''; + curl_setopt($curl_handle, CURLOPT_PROXY, $host); + + if (isset($this->proxy['user']) && isset($this->proxy['pass'])) { + curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); + } + } + + // Set credentials for HTTP Basic/Digest Authentication. + if ($this->username && $this->password) { + curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + // Handle the encoding if we can. + if (extension_loaded('zlib')) { + curl_setopt($curl_handle, CURLOPT_ENCODING, ''); + } + + // Process custom headers + if (isset($this->request_headers) && count($this->request_headers)) { + $temp_headers = array(); + + foreach ($this->request_headers as $k => $v) { + $temp_headers[] = $k . ': ' . $v; + } + + curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers); + } + + switch ($this->method) { + case self::HTTP_PUT: + //unset($this->read_stream); + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT'); + if (isset($this->read_stream)) { + if (!isset($this->read_stream_size) || $this->read_stream_size < 0) { + throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); + } + curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); + curl_setopt($curl_handle, CURLOPT_UPLOAD, true); + } else { + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + } + break; + + case self::HTTP_POST: + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'POST'); + if (isset($this->read_stream)) { + if (!isset($this->read_stream_size) || $this->read_stream_size < 0) { + throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); + } + curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); + curl_setopt($curl_handle, CURLOPT_UPLOAD, true); + } else { + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + } + break; + + case self::HTTP_HEAD: + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD); + curl_setopt($curl_handle, CURLOPT_NOBODY, 1); + break; + + default: // Assumed GET + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method); + if (isset($this->write_stream) || isset($this->write_file)) { + curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback')); + curl_setopt($curl_handle, CURLOPT_HEADER, false); + } else { + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + } + break; + } + + // Merge in the CURLOPTs + if (isset($this->curlopts) && sizeof($this->curlopts) > 0) { + foreach ($this->curlopts as $k => $v) { + curl_setopt($curl_handle, $k, $v); + } + } + + return $curl_handle; + } + + /** + * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the + * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via + * parameters. + * + * @param resource $curl_handle (Optional) The reference to the already executed cURL request. + * @param string $response (Optional) The actual response content itself that needs to be parsed. + * @return ResponseCore A object containing a parsed HTTP response. + */ + public function process_response($curl_handle = null, $response = null) + { + // Accept a custom one if it's passed. + if ($curl_handle && $response) { + $this->response = $response; + } + + // As long as this came back as a valid resource... + if (is_resource($curl_handle)) { + // Determine what's what. + $header_size = curl_getinfo($curl_handle, CURLINFO_HEADER_SIZE); + $this->response_headers = substr($this->response, 0, $header_size); + $this->response_body = substr($this->response, $header_size); + $this->response_code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); + $this->response_info = curl_getinfo($curl_handle); + + if (intval($this->response_code) / 100 != 2 && isset($this->write_file)) + { + $this->response_headers = $this->response_raw_headers; + $this->response_body = $this->response_error_body; + } + + // Parse out the headers + $this->response_headers = explode("\r\n\r\n", trim($this->response_headers)); + $this->response_headers = array_pop($this->response_headers); + $this->response_headers = explode("\r\n", $this->response_headers); + array_shift($this->response_headers); + + // Loop through and split up the headers. + $header_assoc = array(); + foreach ($this->response_headers as $header) { + $kv = explode(': ', $header); + $header_assoc[strtolower($kv[0])] = isset($kv[1]) ? $kv[1] : ''; + } + + // Reset the headers to the appropriate property. + $this->response_headers = $header_assoc; + $this->response_headers['info'] = $this->response_info; + $this->response_headers['info']['method'] = $this->method; + + if ($curl_handle && $response) { + return new ResponseCore($this->response_headers, $this->response_body, $this->response_code); + } + } + + // Return false + return false; + } + + /** + * Sends the request, calling necessary utility functions to update built-in properties. + * + * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not. + * @return string The resulting unparsed data from the request. + */ + public function send_request($parse = false) + { + set_time_limit(0); + + $curl_handle = $this->prep_request(); + $this->response = curl_exec($curl_handle); + + if ($this->response === false) { + throw new RequestCore_Exception('cURL resource: ' . (string)$curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')'); + } + + $parsed_response = $this->process_response($curl_handle, $this->response); + + curl_close($curl_handle); + + if ($parse) { + return $parsed_response; + } + + return $this->response; + } + + /*%******************************************************************************************%*/ + // RESPONSE METHODS + + /** + * Get the HTTP response headers from the request. + * + * @param string $header (Optional) A specific header value to return. Defaults to all headers. + * @return string|array All or selected header values. + */ + public function get_response_header($header = null) + { + if ($header) { + return $this->response_headers[strtolower($header)]; + } + return $this->response_headers; + } + + /** + * Get the HTTP response body from the request. + * + * @return string The response body. + */ + public function get_response_body() + { + return $this->response_body; + } + + /** + * Get the HTTP response code from the request. + * + * @return string The HTTP response code. + */ + public function get_response_code() + { + return $this->response_code; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore_Exception.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore_Exception.php new file mode 100644 index 0000000..cb4e83c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore_Exception.php @@ -0,0 +1,8 @@ +). + * @param string $body (Required) XML-formatted response from AWS. + * @param integer $status (Optional) HTTP response status code from the request. + * @return Mixed Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. + */ + public function __construct($header, $body, $status = null) + { + $this->header = $header; + $this->body = $body; + $this->status = $status; + + return $this; + } + + /** + * Did we receive the status code we expected? + * + * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. + * @return boolean Whether we received the expected status code or not. + */ + public function isOK($codes = array(200, 201, 204, 206)) + { + if (is_array($codes)) { + return in_array($this->status, $codes); + } + + return $this->status === $codes; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketInfo.php new file mode 100644 index 0000000..9b89674 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketInfo.php @@ -0,0 +1,78 @@ +location = $location; + $this->name = $name; + $this->createDate = $createDate; + } + + /** + * 得到bucket所在的region + * + * @return string + */ + public function getLocation() + { + return $this->location; + } + + /** + * 得到bucket的名称 + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * 得到bucket的创建时间 + * + * @return string + */ + public function getCreateDate() + { + return $this->createDate; + } + + /** + * bucket所在的region + * + * @var string + */ + private $location; + /** + * bucket的名称 + * + * @var string + */ + private $name; + + /** + * bucket的创建事件 + * + * @var string + */ + private $createDate; + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketListInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketListInfo.php new file mode 100644 index 0000000..910717f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/BucketListInfo.php @@ -0,0 +1,39 @@ +bucketList = $bucketList; + } + + /** + * 得到BucketInfo列表 + * + * @return BucketInfo[] + */ + public function getBucketList() + { + return $this->bucketList; + } + + /** + * BucketInfo信息列表 + * + * @var array + */ + private $bucketList = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CnameConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CnameConfig.php new file mode 100644 index 0000000..f3597d2 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CnameConfig.php @@ -0,0 +1,99 @@ +cnameList = array(); + } + + /** + * @return array + * @example + * array(2) { + * [0]=> + * array(3) { + * ["Domain"]=> + * string(11) "www.foo.com" + * ["Status"]=> + * string(7) "enabled" + * ["LastModified"]=> + * string(8) "20150101" + * } + * [1]=> + * array(3) { + * ["Domain"]=> + * string(7) "bar.com" + * ["Status"]=> + * string(8) "disabled" + * ["LastModified"]=> + * string(8) "20160101" + * } + * } + */ + public function getCnames() + { + return $this->cnameList; + } + + + public function addCname($cname) + { + if (count($this->cnameList) >= self::OSS_MAX_RULES) { + throw new OssException( + "num of cname in the config exceeds self::OSS_MAX_RULES: " . strval(self::OSS_MAX_RULES)); + } + $this->cnameList[] = array('Domain' => $cname); + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + if (!isset($xml->Cname)) return; + foreach ($xml->Cname as $entry) { + $cname = array(); + foreach ($entry as $key => $value) { + $cname[strval($key)] = strval($value); + } + $this->cnameList[] = $cname; + } + } + + public function serializeToXml() + { + $strXml = << + + +EOF; + $xml = new \SimpleXMLElement($strXml); + foreach ($this->cnameList as $cname) { + $node = $xml->addChild('Cname'); + foreach ($cname as $key => $value) { + $node->addChild($key, $value); + } + } + return $xml->asXML(); + } + + public function __toString() + { + return $this->serializeToXml(); + } + + const OSS_MAX_RULES = 10; + + private $cnameList = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsConfig.php new file mode 100644 index 0000000..c44c10a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsConfig.php @@ -0,0 +1,113 @@ +rules = array(); + } + + /** + * 得到CorsRule列表 + * + * @return CorsRule[] + */ + public function getRules() + { + return $this->rules; + } + + + /** + * 添加一条CorsRule + * + * @param CorsRule $rule + * @throws OssException + */ + public function addRule($rule) + { + if (count($this->rules) >= self::OSS_MAX_RULES) { + throw new OssException("num of rules in the config exceeds self::OSS_MAX_RULES: " . strval(self::OSS_MAX_RULES)); + } + $this->rules[] = $rule; + } + + /** + * 从xml数据中解析出CorsConfig + * + * @param string $strXml + * @throws OssException + * @return null + */ + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + if (!isset($xml->CORSRule)) return; + foreach ($xml->CORSRule as $rule) { + $corsRule = new CorsRule(); + foreach ($rule as $key => $value) { + if ($key === self::OSS_CORS_ALLOWED_HEADER) { + $corsRule->addAllowedHeader(strval($value)); + } elseif ($key === self::OSS_CORS_ALLOWED_METHOD) { + $corsRule->addAllowedMethod(strval($value)); + } elseif ($key === self::OSS_CORS_ALLOWED_ORIGIN) { + $corsRule->addAllowedOrigin(strval($value)); + } elseif ($key === self::OSS_CORS_EXPOSE_HEADER) { + $corsRule->addExposeHeader(strval($value)); + } elseif ($key === self::OSS_CORS_MAX_AGE_SECONDS) { + $corsRule->setMaxAgeSeconds(strval($value)); + } + } + $this->addRule($corsRule); + } + return; + } + + /** + * 生成xml字符串 + * + * @return string + */ + public function serializeToXml() + { + $xml = new \SimpleXMLElement(''); + foreach ($this->rules as $rule) { + $xmlRule = $xml->addChild('CORSRule'); + $rule->appendToXml($xmlRule); + } + return $xml->asXML(); + } + + public function __toString() + { + return $this->serializeToXml(); + } + + const OSS_CORS_ALLOWED_ORIGIN = 'AllowedOrigin'; + const OSS_CORS_ALLOWED_METHOD = 'AllowedMethod'; + const OSS_CORS_ALLOWED_HEADER = 'AllowedHeader'; + const OSS_CORS_EXPOSE_HEADER = 'ExposeHeader'; + const OSS_CORS_MAX_AGE_SECONDS = 'MaxAgeSeconds'; + const OSS_MAX_RULES = 10; + + /** + * orsRule列表 + * + * @var CorsRule[] + */ + private $rules = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsRule.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsRule.php new file mode 100644 index 0000000..2cbe1c1 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/CorsRule.php @@ -0,0 +1,150 @@ +allowedOrigins[] = $allowedOrigin; + } + } + + /** + * Rule中增加一条allowedMethod + * + * @param string $allowedMethod + */ + public function addAllowedMethod($allowedMethod) + { + if (!empty($allowedMethod)) { + $this->allowedMethods[] = $allowedMethod; + } + } + + /** + * Rule中增加一条allowedHeader + * + * @param string $allowedHeader + */ + public function addAllowedHeader($allowedHeader) + { + if (!empty($allowedHeader)) { + $this->allowedHeaders[] = $allowedHeader; + } + } + + /** + * Rule中增加一条exposeHeader + * + * @param string $exposeHeader + */ + public function addExposeHeader($exposeHeader) + { + if (!empty($exposeHeader)) { + $this->exposeHeaders[] = $exposeHeader; + } + } + + /** + * @return int + */ + public function getMaxAgeSeconds() + { + return $this->maxAgeSeconds; + } + + /** + * @param int $maxAgeSeconds + */ + public function setMaxAgeSeconds($maxAgeSeconds) + { + $this->maxAgeSeconds = $maxAgeSeconds; + } + + /** + * 得到AllowedHeaders列表 + * + * @return string[] + */ + public function getAllowedHeaders() + { + return $this->allowedHeaders; + } + + /** + * 得到AllowedOrigins列表 + * + * @return string[] + */ + public function getAllowedOrigins() + { + return $this->allowedOrigins; + } + + /** + * 得到AllowedMethods列表 + * + * @return string[] + */ + public function getAllowedMethods() + { + return $this->allowedMethods; + } + + /** + * 得到ExposeHeaders列表 + * + * @return string[] + */ + public function getExposeHeaders() + { + return $this->exposeHeaders; + } + + /** + * 根据提供的xmlRule, 把this按照一定的规则插入到$xmlRule中 + * + * @param \SimpleXMLElement $xmlRule + * @throws OssException + */ + public function appendToXml(&$xmlRule) + { + if (!isset($this->maxAgeSeconds)) { + throw new OssException("maxAgeSeconds is not set in the Rule"); + } + foreach ($this->allowedOrigins as $allowedOrigin) { + $xmlRule->addChild(CorsConfig::OSS_CORS_ALLOWED_ORIGIN, $allowedOrigin); + } + foreach ($this->allowedMethods as $allowedMethod) { + $xmlRule->addChild(CorsConfig::OSS_CORS_ALLOWED_METHOD, $allowedMethod); + } + foreach ($this->allowedHeaders as $allowedHeader) { + $xmlRule->addChild(CorsConfig::OSS_CORS_ALLOWED_HEADER, $allowedHeader); + } + foreach ($this->exposeHeaders as $exposeHeader) { + $xmlRule->addChild(CorsConfig::OSS_CORS_EXPOSE_HEADER, $exposeHeader); + } + $xmlRule->addChild(CorsConfig::OSS_CORS_MAX_AGE_SECONDS, strval($this->maxAgeSeconds)); + } + + private $allowedHeaders = array(); + private $allowedOrigins = array(); + private $allowedMethods = array(); + private $exposeHeaders = array(); + private $maxAgeSeconds = null; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelHistory.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelHistory.php new file mode 100644 index 0000000..6643444 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelHistory.php @@ -0,0 +1,34 @@ +liveRecordList; + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + + if (isset($xml->LiveRecord)) { + foreach ($xml->LiveRecord as $record) { + $liveRecord = new LiveChannelHistory(); + $liveRecord->parseFromXmlNode($record); + $this->liveRecordList[] = $liveRecord; + } + } + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $liveRecordList = array(); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelInfo.php new file mode 100644 index 0000000..0b5edfc --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelInfo.php @@ -0,0 +1,68 @@ +description; + } + + public function getStatus() + { + return $this->status; + } + + public function getType() + { + return $this->type; + } + + public function getFragDuration() + { + return $this->fragDuration; + } + + public function getFragCount() + { + return $this->fragCount; + } + + public function getPlayListName() + { + return $this->playlistName; + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + + $this->description = strval($xml->Description); + $this->status = strval($xml->Status); + + if (isset($xml->Target)) { + foreach ($xml->Target as $target) { + $this->type = strval($target->Type); + $this->fragDuration = strval($target->FragDuration); + $this->fragCount = strval($target->FragCount); + $this->playlistName = strval($target->PlaylistName); + } + } + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $description; + private $status; + private $type; + private $fragDuration; + private $fragCount; + private $playlistName; +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelStatus.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelStatus.php new file mode 100644 index 0000000..2ee7a68 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/GetLiveChannelStatus.php @@ -0,0 +1,107 @@ +status; + } + + public function getConnectedTime() + { + return $this->connectedTime; + } + + public function getRemoteAddr() + { + return $this->remoteAddr; + } + + public function getVideoWidth() + { + return $this->videoWidth; + } + public function getVideoHeight() + { + return $this->videoHeight; + } + public function getVideoFrameRate() + { + return $this->videoFrameRate; + } + public function getVideoBandwidth() + { + return $this->videoBandwidth; + } + public function getVideoCodec() + { + return $this->videoCodec; + } + + public function getAudioBandwidth() + { + return $this->audioBandwidth; + } + public function getAudioSampleRate() + { + return $this->audioSampleRate; + } + public function getAudioCodec() + { + return $this->audioCodec; + } + + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + $this->status = strval($xml->Status); + $this->connectedTime = strval($xml->ConnectedTime); + $this->remoteAddr = strval($xml->RemoteAddr); + + if (isset($xml->Video)) { + foreach ($xml->Video as $video) { + $this->videoWidth = intval($video->Width); + $this->videoHeight = intval($video->Height); + $this->videoFrameRate = intval($video->FrameRate); + $this->videoBandwidth = intval($video->Bandwidth); + $this->videoCodec = strval($video->Codec); + } + } + + if (isset($xml->Video)) { + foreach ($xml->Audio as $audio) { + $this->audioBandwidth = intval($audio->Bandwidth); + $this->audioSampleRate = intval($audio->SampleRate); + $this->audioCodec = strval($audio->Codec); + } + } + + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $status; + private $connectedTime; + private $remoteAddr; + + private $videoWidth; + private $videoHeight; + private $videoFrameRate; + private $videoBandwidth; + private $videoCodec; + + private $audioBandwidth; + private $audioSampleRate; + private $audioCodec; + + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleAction.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleAction.php new file mode 100644 index 0000000..5abd825 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleAction.php @@ -0,0 +1,88 @@ +action = $action; + $this->timeSpec = $timeSpec; + $this->timeValue = $timeValue; + } + + /** + * @return LifecycleAction + */ + public function getAction() + { + return $this->action; + } + + /** + * @param string $action + */ + public function setAction($action) + { + $this->action = $action; + } + + /** + * @return string + */ + public function getTimeSpec() + { + return $this->timeSpec; + } + + /** + * @param string $timeSpec + */ + public function setTimeSpec($timeSpec) + { + $this->timeSpec = $timeSpec; + } + + /** + * @return string + */ + public function getTimeValue() + { + return $this->timeValue; + } + + /** + * @param string $timeValue + */ + public function setTimeValue($timeValue) + { + $this->timeValue = $timeValue; + } + + /** + * appendToXml 把actions插入到xml中 + * + * @param \SimpleXMLElement $xmlRule + */ + public function appendToXml(&$xmlRule) + { + $xmlAction = $xmlRule->addChild($this->action); + $xmlAction->addChild($this->timeSpec, $this->timeValue); + } + + private $action; + private $timeSpec; + private $timeValue; + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleConfig.php new file mode 100644 index 0000000..fc4f575 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleConfig.php @@ -0,0 +1,107 @@ +rules = array(); + $xml = simplexml_load_string($strXml); + if (!isset($xml->Rule)) return; + $this->rules = array(); + foreach ($xml->Rule as $rule) { + $id = strval($rule->ID); + $prefix = strval($rule->Prefix); + $status = strval($rule->Status); + $actions = array(); + foreach ($rule as $key => $value) { + if ($key === 'ID' || $key === 'Prefix' || $key === 'Status') continue; + $action = $key; + $timeSpec = null; + $timeValue = null; + foreach ($value as $timeSpecKey => $timeValueValue) { + $timeSpec = $timeSpecKey; + $timeValue = strval($timeValueValue); + } + $actions[] = new LifecycleAction($action, $timeSpec, $timeValue); + } + $this->rules[] = new LifecycleRule($id, $prefix, $status, $actions); + } + return; + } + + + /** + * 生成xml字符串 + * + * @return string + */ + public function serializeToXml() + { + + $xml = new \SimpleXMLElement(''); + foreach ($this->rules as $rule) { + $xmlRule = $xml->addChild('Rule'); + $rule->appendToXml($xmlRule); + } + return $xml->asXML(); + } + + /** + * + * 添加LifecycleRule + * + * @param LifecycleRule $lifecycleRule + * @throws OssException + */ + public function addRule($lifecycleRule) + { + if (!isset($lifecycleRule)) { + throw new OssException("lifecycleRule is null"); + } + $this->rules[] = $lifecycleRule; + } + + /** + * 将配置转换成字符串,便于用户查看 + * + * @return string + */ + public function __toString() + { + return $this->serializeToXml(); + } + + /** + * 得到所有的生命周期规则 + * + * @return LifecycleRule[] + */ + public function getRules() + { + return $this->rules; + } + + /** + * @var LifecycleRule[] + */ + private $rules; +} + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleRule.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleRule.php new file mode 100644 index 0000000..ec615b9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LifecycleRule.php @@ -0,0 +1,126 @@ +id; + } + + /** + * @param string $id 规则ID + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * 得到文件前缀 + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * 设置文件前缀 + * + * @param string $prefix 文件前缀 + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Lifecycle规则的状态 + * + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * 设置Lifecycle规则状态 + * + * @param string $status + */ + public function setStatus($status) + { + $this->status = $status; + } + + /** + * + * @return LifecycleAction[] + */ + public function getActions() + { + return $this->actions; + } + + /** + * @param LifecycleAction[] $actions + */ + public function setActions($actions) + { + $this->actions = $actions; + } + + + /** + * LifecycleRule constructor. + * + * @param string $id 规则ID + * @param string $prefix 文件前缀 + * @param string $status 规则状态,可选[self::LIFECYCLE_STATUS_ENABLED, self::LIFECYCLE_STATUS_DISABLED] + * @param LifecycleAction[] $actions + */ + public function __construct($id, $prefix, $status, $actions) + { + $this->id = $id; + $this->prefix = $prefix; + $this->status = $status; + $this->actions = $actions; + } + + /** + * @param \SimpleXMLElement $xmlRule + */ + public function appendToXml(&$xmlRule) + { + $xmlRule->addChild('ID', $this->id); + $xmlRule->addChild('Prefix', $this->prefix); + $xmlRule->addChild('Status', $this->status); + foreach ($this->actions as $action) { + $action->appendToXml($xmlRule); + } + } + + private $id; + private $prefix; + private $status; + private $actions = array(); + + const LIFECYCLE_STATUS_ENABLED = 'Enabled'; + const LIFECYCLE_STATUS_DISABLED = 'Disabled'; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListMultipartUploadInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListMultipartUploadInfo.php new file mode 100644 index 0000000..105d005 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListMultipartUploadInfo.php @@ -0,0 +1,134 @@ +bucket = $bucket; + $this->keyMarker = $keyMarker; + $this->uploadIdMarker = $uploadIdMarker; + $this->nextKeyMarker = $nextKeyMarker; + $this->nextUploadIdMarker = $nextUploadIdMarker; + $this->delimiter = $delimiter; + $this->prefix = $prefix; + $this->maxUploads = $maxUploads; + $this->isTruncated = $isTruncated; + $this->uploads = $uploads; + } + + /** + * 得到bucket名称 + * + * @return string + */ + public function getBucket() + { + return $this->bucket; + } + + /** + * @return string + */ + public function getKeyMarker() + { + return $this->keyMarker; + } + + /** + * + * @return string + */ + public function getUploadIdMarker() + { + return $this->uploadIdMarker; + } + + /** + * @return string + */ + public function getNextKeyMarker() + { + return $this->nextKeyMarker; + } + + /** + * @return string + */ + public function getNextUploadIdMarker() + { + return $this->nextUploadIdMarker; + } + + /** + * @return string + */ + public function getDelimiter() + { + return $this->delimiter; + } + + /** + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * @return int + */ + public function getMaxUploads() + { + return $this->maxUploads; + } + + /** + * @return string + */ + public function getIsTruncated() + { + return $this->isTruncated; + } + + /** + * @return UploadInfo[] + */ + public function getUploads() + { + return $this->uploads; + } + + private $bucket = ""; + private $keyMarker = ""; + private $uploadIdMarker = ""; + private $nextKeyMarker = ""; + private $nextUploadIdMarker = ""; + private $delimiter = ""; + private $prefix = ""; + private $maxUploads = 0; + private $isTruncated = "false"; + private $uploads = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListPartsInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListPartsInfo.php new file mode 100644 index 0000000..f1d10ee --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ListPartsInfo.php @@ -0,0 +1,97 @@ +bucket = $bucket; + $this->key = $key; + $this->uploadId = $uploadId; + $this->nextPartNumberMarker = $nextPartNumberMarker; + $this->maxParts = $maxParts; + $this->isTruncated = $isTruncated; + $this->listPart = $listPart; + } + + /** + * @return string + */ + public function getBucket() + { + return $this->bucket; + } + + /** + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * @return string + */ + public function getUploadId() + { + return $this->uploadId; + } + + /** + * @return int + */ + public function getNextPartNumberMarker() + { + return $this->nextPartNumberMarker; + } + + /** + * @return int + */ + public function getMaxParts() + { + return $this->maxParts; + } + + /** + * @return string + */ + public function getIsTruncated() + { + return $this->isTruncated; + } + + /** + * @return array + */ + public function getListPart() + { + return $this->listPart; + } + + private $bucket = ""; + private $key = ""; + private $uploadId = ""; + private $nextPartNumberMarker = 0; + private $maxParts = 0; + private $isTruncated = ""; + private $listPart = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelConfig.php new file mode 100644 index 0000000..dadedc9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelConfig.php @@ -0,0 +1,121 @@ +description = $option['description']; + } + if (isset($option['status'])) { + $this->status = $option['status']; + } + if (isset($option['type'])) { + $this->type = $option['type']; + } + if (isset($option['fragDuration'])) { + $this->fragDuration = $option['fragDuration']; + } + if (isset($option['fragCount'])) { + $this->fragCount = $option['fragCount']; + } + if (isset($option['playListName'])) { + $this->playListName = $option['playListName']; + } + } + + public function getDescription() + { + return $this->description; + } + + public function getStatus() + { + return $this->status; + } + + public function getType() + { + return $this->type; + } + + public function getFragDuration() + { + return $this->fragDuration; + } + + public function getFragCount() + { + return $this->fragCount; + } + + public function getPlayListName() + { + return $this->playListName; + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + $this->description = strval($xml->Description); + $this->status = strval($xml->Status); + $target = $xml->Target; + $this->type = strval($target->Type); + $this->fragDuration = intval($target->FragDuration); + $this->fragCount = intval($target->FragCount); + $this->playListName = strval($target->PlayListName); + } + + public function serializeToXml() + { + $strXml = << + + +EOF; + $xml = new \SimpleXMLElement($strXml); + if (isset($this->description)) { + $xml->addChild('Description', $this->description); + } + + if (isset($this->status)) { + $xml->addChild('Status', $this->status); + } + + $node = $xml->addChild('Target'); + $node->addChild('Type', $this->type); + + if (isset($this->fragDuration)) { + $node->addChild('FragDuration', $this->fragDuration); + } + + if (isset($this->fragCount)) { + $node->addChild('FragCount', $this->fragCount); + } + + if (isset($this->playListName)) { + $node->addChild('PlayListName', $this->playListName); + } + + return $xml->asXML(); + } + + public function __toString() + { + return $this->serializeToXml(); + } + + private $description; + private $status = "enabled"; + private $type; + private $fragDuration = 5; + private $fragCount = 3; + private $playListName = "playlist.m3u8"; +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelHistory.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelHistory.php new file mode 100644 index 0000000..1c1fd4d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelHistory.php @@ -0,0 +1,59 @@ +startTime; + } + + public function getEndTime() + { + return $this->endTime; + } + + public function getRemoteAddr() + { + return $this->remoteAddr; + } + + public function parseFromXmlNode($xml) + { + if (isset($xml->StartTime)) { + $this->startTime = strval($xml->StartTime); + } + + if (isset($xml->EndTime)) { + $this->endTime = strval($xml->EndTime); + } + + if (isset($xml->RemoteAddr)) { + $this->remoteAddr = strval($xml->RemoteAddr); + } + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + $this->parseFromXmlNode($xml); + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $startTime; + private $endTime; + private $remoteAddr; +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelInfo.php new file mode 100644 index 0000000..c63ec54 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelInfo.php @@ -0,0 +1,107 @@ +name = $name; + $this->description = $description; + $this->publishUrls = array(); + $this->playUrls = array(); + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getPublishUrls() + { + return $this->publishUrls; + } + + public function getPlayUrls() + { + return $this->playUrls; + } + + public function getStatus() + { + return $this->status; + } + + public function getLastModified() + { + return $this->lastModified; + } + + public function getDescription() + { + return $this->description; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function parseFromXmlNode($xml) + { + if (isset($xml->Name)) { + $this->name = strval($xml->Name); + } + + if (isset($xml->Description)) { + $this->description = strval($xml->Description); + } + + if (isset($xml->Status)) { + $this->status = strval($xml->Status); + } + + if (isset($xml->LastModified)) { + $this->lastModified = strval($xml->LastModified); + } + + if (isset($xml->PublishUrls)) { + foreach ($xml->PublishUrls as $url) { + $this->publishUrls[] = strval($url->Url); + } + } + + if (isset($xml->PlayUrls)) { + foreach ($xml->PlayUrls as $url) { + $this->playUrls[] = strval($url->Url); + } + } + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + $this->parseFromXmlNode($xml); + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $name; + private $description; + private $publishUrls; + private $playUrls; + private $status; + private $lastModified; +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelListInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelListInfo.php new file mode 100644 index 0000000..bb5093a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LiveChannelListInfo.php @@ -0,0 +1,107 @@ +bucket; + } + + public function setBucketName($name) + { + $this->bucket = $name; + } + + /** + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * @return string + */ + public function getMarker() + { + return $this->marker; + } + + /** + * @return int + */ + public function getMaxKeys() + { + return $this->maxKeys; + } + + /** + * @return mixed + */ + public function getIsTruncated() + { + return $this->isTruncated; + } + + /** + * @return LiveChannelInfo[] + */ + public function getChannelList() + { + return $this->channelList; + } + + /** + * @return string + */ + public function getNextMarker() + { + return $this->nextMarker; + } + + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + + $this->prefix = strval($xml->Prefix); + $this->marker = strval($xml->Marker); + $this->maxKeys = intval($xml->MaxKeys); + $this->isTruncated = (strval($xml->IsTruncated) == 'true'); + $this->nextMarker = strval($xml->NextMarker); + + if (isset($xml->LiveChannel)) { + foreach ($xml->LiveChannel as $chan) { + $channel = new LiveChannelInfo(); + $channel->parseFromXmlNode($chan); + $this->channelList[] = $channel; + } + } + } + + public function serializeToXml() + { + throw new OssException("Not implemented."); + } + + private $bucket = ''; + private $prefix = ''; + private $marker = ''; + private $nextMarker = ''; + private $maxKeys = 100; + private $isTruncated = 'false'; + private $channelList = array(); +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LoggingConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LoggingConfig.php new file mode 100644 index 0000000..978421a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/LoggingConfig.php @@ -0,0 +1,86 @@ +targetBucket = $targetBucket; + $this->targetPrefix = $targetPrefix; + } + + /** + * @param $strXml + * @return null + */ + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + if (!isset($xml->LoggingEnabled)) return; + foreach ($xml->LoggingEnabled as $status) { + foreach ($status as $key => $value) { + if ($key === 'TargetBucket') { + $this->targetBucket = strval($value); + } elseif ($key === 'TargetPrefix') { + $this->targetPrefix = strval($value); + } + } + break; + } + } + + /** + * 序列化成xml字符串 + * + */ + public function serializeToXml() + { + $xml = new \SimpleXMLElement(''); + if (isset($this->targetBucket) && isset($this->targetPrefix)) { + $loggingEnabled = $xml->addChild('LoggingEnabled'); + $loggingEnabled->addChild('TargetBucket', $this->targetBucket); + $loggingEnabled->addChild('TargetPrefix', $this->targetPrefix); + } + return $xml->asXML(); + } + + /** + * @return string + */ + public function __toString() + { + return $this->serializeToXml(); + } + + /** + * @return string + */ + public function getTargetBucket() + { + return $this->targetBucket; + } + + /** + * @return string + */ + public function getTargetPrefix() + { + return $this->targetPrefix; + } + + private $targetBucket = ""; + private $targetPrefix = ""; + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectInfo.php new file mode 100644 index 0000000..2ae6c99 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectInfo.php @@ -0,0 +1,93 @@ +key = $key; + $this->lastModified = $lastModified; + $this->eTag = $eTag; + $this->type = $type; + $this->size = $size; + $this->storageClass = $storageClass; + } + + /** + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * @return string + */ + public function getLastModified() + { + return $this->lastModified; + } + + /** + * @return string + */ + public function getETag() + { + return $this->eTag; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return int + */ + public function getSize() + { + return $this->size; + } + + /** + * @return string + */ + public function getStorageClass() + { + return $this->storageClass; + } + + private $key = ""; + private $lastModified = ""; + private $eTag = ""; + private $type = ""; + private $size = 0; + private $storageClass = ""; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectListInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectListInfo.php new file mode 100644 index 0000000..dbe7c7a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/ObjectListInfo.php @@ -0,0 +1,126 @@ +bucketName = $bucketName; + $this->prefix = $prefix; + $this->marker = $marker; + $this->nextMarker = $nextMarker; + $this->maxKeys = $maxKeys; + $this->delimiter = $delimiter; + $this->isTruncated = $isTruncated; + $this->objectList = $objectList; + $this->prefixList = $prefixList; + } + + /** + * @return string + */ + public function getBucketName() + { + return $this->bucketName; + } + + /** + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * @return string + */ + public function getMarker() + { + return $this->marker; + } + + /** + * @return int + */ + public function getMaxKeys() + { + return $this->maxKeys; + } + + /** + * @return string + */ + public function getDelimiter() + { + return $this->delimiter; + } + + /** + * @return mixed + */ + public function getIsTruncated() + { + return $this->isTruncated; + } + + /** + * 返回ListObjects接口返回数据中的ObjectInfo列表 + * + * @return ObjectInfo[] + */ + public function getObjectList() + { + return $this->objectList; + } + + /** + * 返回ListObjects接口返回数据中的PrefixInfo列表 + * + * @return PrefixInfo[] + */ + public function getPrefixList() + { + return $this->prefixList; + } + + /** + * @return string + */ + public function getNextMarker() + { + return $this->nextMarker; + } + + private $bucketName = ""; + private $prefix = ""; + private $marker = ""; + private $nextMarker = ""; + private $maxKeys = 0; + private $delimiter = ""; + private $isTruncated = null; + private $objectList = array(); + private $prefixList = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PartInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PartInfo.php new file mode 100644 index 0000000..439a84d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PartInfo.php @@ -0,0 +1,63 @@ +partNumber = $partNumber; + $this->lastModified = $lastModified; + $this->eTag = $eTag; + $this->size = $size; + } + + /** + * @return int + */ + public function getPartNumber() + { + return $this->partNumber; + } + + /** + * @return string + */ + public function getLastModified() + { + return $this->lastModified; + } + + /** + * @return string + */ + public function getETag() + { + return $this->eTag; + } + + /** + * @return int + */ + public function getSize() + { + return $this->size; + } + + private $partNumber = 0; + private $lastModified = ""; + private $eTag = ""; + private $size = 0; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PrefixInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PrefixInfo.php new file mode 100644 index 0000000..e61eac4 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/PrefixInfo.php @@ -0,0 +1,36 @@ +prefix = $prefix; + } + + /** + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + private $prefix; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/RefererConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/RefererConfig.php new file mode 100644 index 0000000..1d7d975 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/RefererConfig.php @@ -0,0 +1,93 @@ +AllowEmptyReferer)) return; + if (!isset($xml->RefererList)) return; + $this->allowEmptyReferer = + (strval($xml->AllowEmptyReferer) === 'TRUE' || strval($xml->AllowEmptyReferer) === 'true') ? true : false; + + foreach ($xml->RefererList->Referer as $key => $refer) { + $this->refererList[] = strval($refer); + } + } + + + /** + * 把RefererConfig序列化成xml + * + * @return string + */ + public function serializeToXml() + { + $xml = new \SimpleXMLElement(''); + if ($this->allowEmptyReferer) { + $xml->addChild('AllowEmptyReferer', 'true'); + } else { + $xml->addChild('AllowEmptyReferer', 'false'); + } + $refererList = $xml->addChild('RefererList'); + foreach ($this->refererList as $referer) { + $refererList->addChild('Referer', $referer); + } + return $xml->asXML(); + } + + /** + * @return string + */ + function __toString() + { + return $this->serializeToXml(); + } + + /** + * @param boolean $allowEmptyReferer + */ + public function setAllowEmptyReferer($allowEmptyReferer) + { + $this->allowEmptyReferer = $allowEmptyReferer; + } + + /** + * @param string $referer + */ + public function addReferer($referer) + { + $this->refererList[] = $referer; + } + + /** + * @return boolean + */ + public function isAllowEmptyReferer() + { + return $this->allowEmptyReferer; + } + + /** + * @return array + */ + public function getRefererList() + { + return $this->refererList; + } + + private $allowEmptyReferer = true; + private $refererList = array(); +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/StorageCapacityConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/StorageCapacityConfig.php new file mode 100644 index 0000000..05e6332 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/StorageCapacityConfig.php @@ -0,0 +1,74 @@ +storageCapacity = $storageCapacity; + } + + /** + * Not implemented + */ + public function parseFromXml($strXml) + { + throw new OssException("Not implemented."); + } + + /** + * 把StorageCapacityConfig序列化成xml + * + * @return string + */ + public function serializeToXml() + { + $xml = new \SimpleXMLElement(''); + $xml->addChild('StorageCapacity', strval($this->storageCapacity)); + return $xml->asXML(); + } + + /** + * To string + * + * @return string + */ + function __toString() + { + return $this->serializeToXml(); + } + + /** + * Set storage capacity + * + * @param int $storageCapacity + */ + public function setStorageCapacity($storageCapacity) + { + $this->storageCapacity = $storageCapacity; + } + + /** + * Get storage capacity + * + * @return int + */ + public function getStorageCapacity() + { + return $this->storageCapacity; + } + + private $storageCapacity = 0; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/UploadInfo.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/UploadInfo.php new file mode 100644 index 0000000..8eaa363 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/UploadInfo.php @@ -0,0 +1,55 @@ +key = $key; + $this->uploadId = $uploadId; + $this->initiated = $initiated; + } + + /** + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * @return string + */ + public function getUploadId() + { + return $this->uploadId; + } + + /** + * @return string + */ + public function getInitiated() + { + return $this->initiated; + } + + private $key = ""; + private $uploadId = ""; + private $initiated = ""; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/WebsiteConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/WebsiteConfig.php new file mode 100644 index 0000000..8ea08a0 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/WebsiteConfig.php @@ -0,0 +1,76 @@ +indexDocument = $indexDocument; + $this->errorDocument = $errorDocument; + } + + /** + * @param string $strXml + * @return null + */ + public function parseFromXml($strXml) + { + $xml = simplexml_load_string($strXml); + if (isset($xml->IndexDocument) && isset($xml->IndexDocument->Suffix)) { + $this->indexDocument = strval($xml->IndexDocument->Suffix); + } + if (isset($xml->ErrorDocument) && isset($xml->ErrorDocument->Key)) { + $this->errorDocument = strval($xml->ErrorDocument->Key); + } + } + + /** + * 把WebsiteConfig序列化成xml + * + * @return string + * @throws OssException + */ + public function serializeToXml() + { + $xml = new \SimpleXMLElement(''); + $index_document_part = $xml->addChild('IndexDocument'); + $error_document_part = $xml->addChild('ErrorDocument'); + $index_document_part->addChild('Suffix', $this->indexDocument); + $error_document_part->addChild('Key', $this->errorDocument); + return $xml->asXML(); + } + + /** + * @return string + */ + public function getIndexDocument() + { + return $this->indexDocument; + } + + /** + * @return string + */ + public function getErrorDocument() + { + return $this->errorDocument; + } + + private $indexDocument = ""; + private $errorDocument = ""; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/XmlConfig.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/XmlConfig.php new file mode 100644 index 0000000..d353a22 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Model/XmlConfig.php @@ -0,0 +1,27 @@ +hostname = $this->checkEndpoint($endpoint, $isCName); + $this->accessKeyId = $accessKeyId; + $this->accessKeySecret = $accessKeySecret; + $this->securityToken = $securityToken; + $this->requestProxy = $requestProxy; + + self::checkEnv(); + } + + /** + * 列举用户所有的Bucket[GetService], Endpoint类型为cname不能进行此操作 + * + * @param array $options + * @throws OssException + * @return BucketListInfo + */ + public function listBuckets($options = NULL) + { + if ($this->hostType === self::OSS_HOST_TYPE_CNAME) { + throw new OssException("operation is not permitted with CName host"); + } + $this->precheckOptions($options); + $options[self::OSS_BUCKET] = ''; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $response = $this->auth($options); + $result = new ListBucketsResult($response); + return $result->getData(); + } + + /** + * 创建bucket,默认创建的bucket的ACL是OssClient::OSS_ACL_TYPE_PRIVATE + * + * @param string $bucket + * @param string $acl + * @param array $options + * @param string $storageType + * @return null + */ + public function createBucket($bucket, $acl = self::OSS_ACL_TYPE_PRIVATE, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl); + if (isset($options[self::OSS_STORAGE])) { + $this->precheckStorage($options[self::OSS_STORAGE]); + $options[self::OSS_CONTENT] = OssUtil::createBucketXmlBody($options[self::OSS_STORAGE]); + unset($options[self::OSS_STORAGE]); + } + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 删除bucket + * 如果Bucket不为空(Bucket中有Object,或者有分块上传的碎片),则Bucket无法删除, + * 必须删除Bucket中的所有Object以及碎片后,Bucket才能成功删除。 + * + * @param string $bucket + * @param array $options + * @return null + */ + public function deleteBucket($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = '/'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 判断bucket是否存在 + * + * @param string $bucket + * @return bool + * @throws OssException + */ + public function doesBucketExist($bucket) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'acl'; + $response = $this->auth($options); + $result = new ExistResult($response); + return $result->getData(); + } + + /** + * 获取bucket所属的数据中心位置信息 + * + * @param string $bucket + * @param array $options + * @throws OssException + * @return string + */ + public function getBucketLocation($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'location'; + $response = $this->auth($options); + $result = new GetLocationResult($response); + return $result->getData(); + } + + /** + * 获取Bucket的Meta信息 + * + * @param string $bucket + * @param array $options 具体参考SDK文档 + * @return array + */ + public function getBucketMeta($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_HEAD; + $options[self::OSS_OBJECT] = '/'; + $response = $this->auth($options); + $result = new HeaderResult($response); + return $result->getData(); + } + + /** + * 获取bucket的ACL配置情况 + * + * @param string $bucket + * @param array $options + * @throws OssException + * @return string + */ + public function getBucketAcl($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'acl'; + $response = $this->auth($options); + $result = new AclResult($response); + return $result->getData(); + } + + /** + * 设置bucket的ACL配置情况 + * + * @param string $bucket bucket名称 + * @param string $acl 读写权限,可选值 ['private', 'public-read', 'public-read-write'] + * @param array $options 可以为空 + * @throws OssException + * @return null + */ + public function putBucketAcl($bucket, $acl, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_HEADERS] = array(self::OSS_ACL => $acl); + $options[self::OSS_SUB_RESOURCE] = 'acl'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取object的ACL属性 + * + * @param string $bucket + * @param string $object + * @throws OssException + * @return string + */ + public function getObjectAcl($bucket, $object) + { + $options = array(); + $this->precheckCommon($bucket, $object, $options, true); + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_SUB_RESOURCE] = 'acl'; + $response = $this->auth($options); + $result = new AclResult($response); + return $result->getData(); + } + + /** + * 设置object的ACL属性 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $acl 读写权限,可选值 ['default', 'private', 'public-read', 'public-read-write'] + * @throws OssException + * @return null + */ + public function putObjectAcl($bucket, $object, $acl) + { + $this->precheckCommon($bucket, $object, $options, true); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_ACL => $acl); + $options[self::OSS_SUB_RESOURCE] = 'acl'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取Bucket的访问日志配置情况 + * + * @param string $bucket bucket名称 + * @param array $options 可以为空 + * @throws OssException + * @return LoggingConfig + */ + public function getBucketLogging($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'logging'; + $response = $this->auth($options); + $result = new GetLoggingResult($response); + return $result->getData(); + } + + /** + * 开启Bucket访问日志记录功能,只有Bucket的所有者才能更改 + * + * @param string $bucket bucket名称 + * @param string $targetBucket 日志文件存放的bucket + * @param string $targetPrefix 日志的文件前缀 + * @param array $options 可以为空 + * @throws OssException + * @return null + */ + public function putBucketLogging($bucket, $targetBucket, $targetPrefix, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $this->precheckBucket($targetBucket, 'targetbucket is not allowed empty'); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'logging'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + + $loggingConfig = new LoggingConfig($targetBucket, $targetPrefix); + $options[self::OSS_CONTENT] = $loggingConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 关闭bucket访问日志记录功能 + * + * @param string $bucket bucket名称 + * @param array $options 可以为空 + * @throws OssException + * @return null + */ + public function deleteBucketLogging($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'logging'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 将bucket设置成静态网站托管模式 + * + * @param string $bucket bucket名称 + * @param WebsiteConfig $websiteConfig + * @param array $options 可以为空 + * @throws OssException + * @return null + */ + public function putBucketWebsite($bucket, $websiteConfig, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'website'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $options[self::OSS_CONTENT] = $websiteConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取bucket的静态网站托管状态 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return WebsiteConfig + */ + public function getBucketWebsite($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'website'; + $response = $this->auth($options); + $result = new GetWebsiteResult($response); + return $result->getData(); + } + + /** + * 关闭bucket的静态网站托管模式 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return null + */ + public function deleteBucketWebsite($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'website'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 在指定的bucket上设定一个跨域资源共享(CORS)的规则,如果原规则存在则覆盖原规则 + * + * @param string $bucket bucket名称 + * @param CorsConfig $corsConfig 跨域资源共享配置,具体规则参见SDK文档 + * @param array $options array + * @throws OssException + * @return null + */ + public function putBucketCors($bucket, $corsConfig, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cors'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $options[self::OSS_CONTENT] = $corsConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取Bucket的CORS配置情况 + * + * @param string $bucket bucket名称 + * @param array $options 可以为空 + * @throws OssException + * @return CorsConfig + */ + public function getBucketCors($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cors'; + $response = $this->auth($options); + $result = new GetCorsResult($response, __FUNCTION__); + return $result->getData(); + } + + /** + * 关闭指定Bucket对应的CORS功能并清空所有规则 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return null + */ + public function deleteBucketCors($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cors'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 为指定Bucket增加CNAME绑定 + * + * @param string $bucket bucket名称 + * @param string $cname + * @param array $options + * @throws OssException + * @return null + */ + public function addBucketCname($bucket, $cname, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cname'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $cnameConfig = new CnameConfig(); + $cnameConfig->addCname($cname); + $options[self::OSS_CONTENT] = $cnameConfig->serializeToXml(); + $options[self::OSS_COMP] = 'add'; + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取指定Bucket已绑定的CNAME列表 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return CnameConfig + */ + public function getBucketCname($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cname'; + $response = $this->auth($options); + $result = new GetCnameResult($response); + return $result->getData(); + } + + /** + * 解除指定Bucket的CNAME绑定 + * + * @param string $bucket bucket名称 + * @param CnameConfig $cnameConfig + * @param array $options + * @throws OssException + * @return null + */ + public function deleteBucketCname($bucket, $cname, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'cname'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $cnameConfig = new CnameConfig(); + $cnameConfig->addCname($cname); + $options[self::OSS_CONTENT] = $cnameConfig->serializeToXml(); + $options[self::OSS_COMP] = 'delete'; + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 为指定Bucket创建LiveChannel + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param LiveChannelConfig $channelConfig + * @param array $options + * @throws OssException + * @return LiveChannelInfo + */ + public function putBucketLiveChannel($bucket, $channelName, $channelConfig, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $options[self::OSS_CONTENT] = $channelConfig->serializeToXml(); + + $response = $this->auth($options); + $result = new PutLiveChannelResult($response); + $info = $result->getData(); + $info->setName($channelName); + $info->setDescription($channelConfig->getDescription()); + + return $info; + } + + /** + * 设置LiveChannel的status + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param string channelStatus $channelStatus 为enabled或disabled + * @param array $options + * @throws OssException + * @return null + */ + public function putLiveChannelStatus($bucket, $channelName, $channelStatus, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + $options[self::OSS_LIVE_CHANNEL_STATUS] = $channelStatus; + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取LiveChannel信息 + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param array $options + * @throws OssException + * @return GetLiveChannelInfo + */ + public function getLiveChannelInfo($bucket, $channelName, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + + $response = $this->auth($options); + $result = new GetLiveChannelInfoResult($response); + return $result->getData(); + } + + /** + * 获取LiveChannel状态信息 + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param array $options + * @throws OssException + * @return GetLiveChannelStatus + */ + public function getLiveChannelStatus($bucket, $channelName, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + $options[self::OSS_COMP] = 'stat'; + + $response = $this->auth($options); + $result = new GetLiveChannelStatusResult($response); + return $result->getData(); + } + + /** + *获取LiveChannel推流记录 + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param array $options + * @throws OssException + * @return GetLiveChannelHistory + */ + public function getLiveChannelHistory($bucket, $channelName, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + $options[self::OSS_COMP] = 'history'; + + $response = $this->auth($options); + $result = new GetLiveChannelHistoryResult($response); + return $result->getData(); + } + + /** + *获取指定Bucket下的live channel列表 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return LiveChannelListInfo + */ + public function listBucketLiveChannels($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'live'; + $options[self::OSS_QUERY_STRING] = array( + 'prefix' => isset($options['prefix']) ? $options['prefix'] : '', + 'marker' => isset($options['marker']) ? $options['marker'] : '', + 'max-keys' => isset($options['max-keys']) ? $options['max-keys'] : '', + ); + $response = $this->auth($options); + $result = new ListLiveChannelResult($response); + $list = $result->getData(); + $list->setBucketName($bucket); + + return $list; + } + + /** + * 为指定LiveChannel生成播放列表 + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param string $playlistName 指定生成的点播播放列表的名称,必须以“.m3u8”结尾 + * @param array $setTime startTime和EndTime以unix时间戳格式给定,跨度不能超过一天 + * @throws OssException + * @return null + */ + public function postVodPlaylist($bucket, $channelName, $playlistName, $setTime) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_OBJECT] = $channelName . '/' . $playlistName; + $options[self::OSS_SUB_RESOURCE] = 'vod'; + $options[self::OSS_LIVE_CHANNEL_END_TIME] = $setTime['EndTime']; + $options[self::OSS_LIVE_CHANNEL_START_TIME] = $setTime['StartTime']; + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 删除指定Bucket的LiveChannel + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param array $options + * @throws OssException + * @return null + */ + public function deleteBucketLiveChannel($bucket, $channelName, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = $channelName; + $options[self::OSS_SUB_RESOURCE] = 'live'; + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 生成带签名的推流地址 + * + * @param string $bucket bucket名称 + * @param string channelName $channelName + * @param int timeout 设置超时时间,单位为秒 + * @param array $options + * @throws OssException + * @return 推流地址 + */ + public function signRtmpUrl($bucket, $channelName, $timeout = 60, $options = NULL) + { + $this->precheckCommon($bucket, $channelName, $options, false); + $expires = time() + $timeout; + $proto = 'rtmp://'; + $hostname = $this->generateHostname($bucket); + $cano_params = ''; + $query_items = array(); + $params = isset($options['params']) ? $options['params'] : array(); + uksort($params, 'strnatcasecmp'); + foreach ($params as $key => $value) { + $cano_params = $cano_params . $key . ':' . $value . "\n"; + $query_items[] = rawurlencode($key) . '=' . rawurlencode($value); + } + $resource = '/' . $bucket . '/' . $channelName; + + $string_to_sign = $expires . "\n" . $cano_params . $resource; + $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->accessKeySecret, true)); + + $query_items[] = 'OSSAccessKeyId=' . rawurlencode($this->accessKeyId); + $query_items[] = 'Expires=' . rawurlencode($expires); + $query_items[] = 'Signature=' . rawurlencode($signature); + + return $proto . $hostname . '/live/' . $channelName . '?' . implode('&', $query_items); + } + + /** + * 检验跨域资源请求, 发送跨域请求之前会发送一个preflight请求(OPTIONS)并带上特定的来源域, + * HTTP方法和header信息等给OSS以决定是否发送真正的请求。 OSS可以通过putBucketCors接口 + * 来开启Bucket的CORS支持,开启CORS功能之后,OSS在收到浏览器preflight请求时会根据设定的 + * 规则评估是否允许本次请求 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $origin 请求来源域 + * @param string $request_method 表明实际请求中会使用的HTTP方法 + * @param string $request_headers 表明实际请求中会使用的除了简单头部之外的headers + * @param array $options + * @return array + * @throws OssException + * @link http://help.aliyun.com/document_detail/oss/api-reference/cors/OptionObject.html + */ + public function optionsObject($bucket, $object, $origin, $request_method, $request_headers, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_OPTIONS; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_HEADERS] = array( + self::OSS_OPTIONS_ORIGIN => $origin, + self::OSS_OPTIONS_REQUEST_HEADERS => $request_headers, + self::OSS_OPTIONS_REQUEST_METHOD => $request_method + ); + $response = $this->auth($options); + $result = new HeaderResult($response); + return $result->getData(); + } + + /** + * 设置Bucket的Lifecycle配置 + * + * @param string $bucket bucket名称 + * @param LifecycleConfig $lifecycleConfig Lifecycle配置类 + * @param array $options + * @throws OssException + * @return null + */ + public function putBucketLifecycle($bucket, $lifecycleConfig, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'lifecycle'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $options[self::OSS_CONTENT] = $lifecycleConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取Bucket的Lifecycle配置情况 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return LifecycleConfig + */ + public function getBucketLifecycle($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'lifecycle'; + $response = $this->auth($options); + $result = new GetLifecycleResult($response); + return $result->getData(); + } + + /** + * 删除指定Bucket的生命周期配置 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return null + */ + public function deleteBucketLifecycle($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'lifecycle'; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 设置一个bucket的referer访问白名单和是否允许referer字段为空的请求访问 + * Bucket Referer防盗链具体见OSS防盗链 + * + * @param string $bucket bucket名称 + * @param RefererConfig $refererConfig + * @param array $options + * @return ResponseCore + * @throws null + */ + public function putBucketReferer($bucket, $refererConfig, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'referer'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $options[self::OSS_CONTENT] = $refererConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取Bucket的Referer配置情况 + * Bucket Referer防盗链具体见OSS防盗链 + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return RefererConfig + */ + public function getBucketReferer($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'referer'; + $response = $this->auth($options); + $result = new GetRefererResult($response); + return $result->getData(); + } + + /** + * 设置bucket的容量大小,单位GB + * 当bucket的容量大于设置的容量时,禁止继续写入 + * + * @param string $bucket bucket名称 + * @param int $storageCapacity + * @param array $options + * @return ResponseCore + * @throws null + */ + public function putBucketStorageCapacity($bucket, $storageCapacity, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'qos'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $storageCapacityConfig = new StorageCapacityConfig($storageCapacity); + $options[self::OSS_CONTENT] = $storageCapacityConfig->serializeToXml(); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取bucket的容量大小,单位GB + * + * @param string $bucket bucket名称 + * @param array $options + * @throws OssException + * @return int + */ + public function getBucketStorageCapacity($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'qos'; + $response = $this->auth($options); + $result = new GetStorageCapacityResult($response); + return $result->getData(); + } + + /** + * 获取bucket下的object列表 + * + * @param string $bucket + * @param array $options + * 其中options中的参数如下 + * $options = array( + * 'max-keys' => max-keys用于限定此次返回object的最大数,如果不设定,默认为100,max-keys取值不能大于1000。 + * 'prefix' => 限定返回的object key必须以prefix作为前缀。注意使用prefix查询时,返回的key中仍会包含prefix。 + * 'delimiter' => 是一个用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组元素 + * 'marker' => 用户设定结果从marker之后按字母排序的第一个开始返回。 + *) + * 其中 prefix,marker用来实现分页显示效果,参数的长度必须小于256字节。 + * @throws OssException + * @return ObjectListInfo + */ + public function listObjects($bucket, $options = NULL) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_HEADERS] = array( + self::OSS_DELIMITER => isset($options[self::OSS_DELIMITER]) ? $options[self::OSS_DELIMITER] : '/', + self::OSS_PREFIX => isset($options[self::OSS_PREFIX]) ? $options[self::OSS_PREFIX] : '', + self::OSS_MAX_KEYS => isset($options[self::OSS_MAX_KEYS]) ? $options[self::OSS_MAX_KEYS] : self::OSS_MAX_KEYS_VALUE, + self::OSS_MARKER => isset($options[self::OSS_MARKER]) ? $options[self::OSS_MARKER] : '', + ); + $query = isset($options[self::OSS_QUERY_STRING]) ? $options[self::OSS_QUERY_STRING] : array(); + $options[self::OSS_QUERY_STRING] = array_merge( + $query, + array(self::OSS_ENCODING_TYPE => self::OSS_ENCODING_TYPE_URL) + ); + + $response = $this->auth($options); + $result = new ListObjectsResult($response); + return $result->getData(); + } + + /** + * 创建虚拟目录 (本函数会在object名称后增加'/', 所以创建目录的object名称不需要'/'结尾,否则,目录名称会变成'//') + * + * 暂不开放此接口 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param array $options + * @return null + */ + public function createObjectDir($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $object . '/'; + $options[self::OSS_CONTENT_LENGTH] = array(self::OSS_CONTENT_LENGTH => 0); + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 上传内存中的内容 + * + * @param string $bucket bucket名称 + * @param string $object objcet名称 + * @param string $content 上传的内容 + * @param array $options + * @return null + */ + public function putObject($bucket, $object, $content, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + + $options[self::OSS_CONTENT] = $content; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $object; + + if (!isset($options[self::OSS_LENGTH])) { + $options[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]); + } else { + $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; + } + + $is_check_md5 = $this->isCheckMD5($options); + if ($is_check_md5) { + $content_md5 = base64_encode(md5($content, true)); + $options[self::OSS_CONTENT_MD5] = $content_md5; + } + + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object); + } + $response = $this->auth($options); + + if (isset($options[self::OSS_CALLBACK]) && !empty($options[self::OSS_CALLBACK])) { + $result = new CallbackResult($response); + } else { + $result = new PutSetDeleteResult($response); + } + + return $result->getData(); + } + + /** + * 创建symlink + * @param string $bucket bucket名称 + * @param string $symlink symlink名称 + * @param string $targetObject 目标object名称 + * @param array $options + * @return null + */ + public function putSymlink($bucket, $symlink ,$targetObject, $options = NULL) + { + $this->precheckCommon($bucket, $symlink, $options); + + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $symlink; + $options[self::OSS_SUB_RESOURCE] = self::OSS_SYMLINK; + $options[self::OSS_HEADERS][self::OSS_SYMLINK_TARGET] = rawurlencode($targetObject); + + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取symlink + *@param string $bucket bucket名称 + * @param string $symlink symlink名称 + * @return null + */ + public function getSymlink($bucket, $symlink) + { + $this->precheckCommon($bucket, $symlink, $options); + + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = $symlink; + $options[self::OSS_SUB_RESOURCE] = self::OSS_SYMLINK; + + $response = $this->auth($options); + $result = new SymlinkResult($response); + return $result->getData(); + } + + /** + * 上传本地文件 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $file 本地文件路径 + * @param array $options + * @return null + * @throws OssException + */ + public function uploadFile($bucket, $object, $file, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + OssUtil::throwOssExceptionWithMessageIfEmpty($file, "file path is invalid"); + $file = OssUtil::encodePath($file); + if (!file_exists($file)) { + throw new OssException($file . " file does not exist"); + } + $options[self::OSS_FILE_UPLOAD] = $file; + $file_size = filesize($options[self::OSS_FILE_UPLOAD]); + $is_check_md5 = $this->isCheckMD5($options); + if ($is_check_md5) { + $content_md5 = base64_encode(md5_file($options[self::OSS_FILE_UPLOAD], true)); + $options[self::OSS_CONTENT_MD5] = $content_md5; + } + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object, $file); + } + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_CONTENT_LENGTH] = $file_size; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 追加上传内存中的内容 + * + * @param string $bucket bucket名称 + * @param string $object objcet名称 + * @param string $content 本次追加上传的内容 + * @param array $options + * @return int next append position + * @throws OssException + */ + public function appendObject($bucket, $object, $content, $position, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + + $options[self::OSS_CONTENT] = $content; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_SUB_RESOURCE] = 'append'; + $options[self::OSS_POSITION] = strval($position); + + if (!isset($options[self::OSS_LENGTH])) { + $options[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]); + } else { + $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; + } + + $is_check_md5 = $this->isCheckMD5($options); + if ($is_check_md5) { + $content_md5 = base64_encode(md5($content, true)); + $options[self::OSS_CONTENT_MD5] = $content_md5; + } + + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object); + } + $response = $this->auth($options); + $result = new AppendResult($response); + return $result->getData(); + } + + /** + * 追加上传本地文件 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $file 追加上传的本地文件路径 + * @param array $options + * @return int next append position + * @throws OssException + */ + public function appendFile($bucket, $object, $file, $position, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + + OssUtil::throwOssExceptionWithMessageIfEmpty($file, "file path is invalid"); + $file = OssUtil::encodePath($file); + if (!file_exists($file)) { + throw new OssException($file . " file does not exist"); + } + $options[self::OSS_FILE_UPLOAD] = $file; + $file_size = filesize($options[self::OSS_FILE_UPLOAD]); + $is_check_md5 = $this->isCheckMD5($options); + if ($is_check_md5) { + $content_md5 = base64_encode(md5_file($options[self::OSS_FILE_UPLOAD], true)); + $options[self::OSS_CONTENT_MD5] = $content_md5; + } + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object, $file); + } + + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_CONTENT_LENGTH] = $file_size; + $options[self::OSS_SUB_RESOURCE] = 'append'; + $options[self::OSS_POSITION] = strval($position); + + $response = $this->auth($options); + $result = new AppendResult($response); + return $result->getData(); + } + + /** + * 拷贝一个在OSS上已经存在的object成另外一个object + * + * @param string $fromBucket 源bucket名称 + * @param string $fromObject 源object名称 + * @param string $toBucket 目标bucket名称 + * @param string $toObject 目标object名称 + * @param array $options + * @return null + * @throws OssException + */ + public function copyObject($fromBucket, $fromObject, $toBucket, $toObject, $options = NULL) + { + $this->precheckCommon($fromBucket, $fromObject, $options); + $this->precheckCommon($toBucket, $toObject, $options); + $options[self::OSS_BUCKET] = $toBucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_OBJECT] = $toObject; + if (isset($options[self::OSS_HEADERS])) { + $options[self::OSS_HEADERS][self::OSS_OBJECT_COPY_SOURCE] = '/' . $fromBucket . '/' . $fromObject; + } else { + $options[self::OSS_HEADERS] = array(self::OSS_OBJECT_COPY_SOURCE => '/' . $fromBucket . '/' . $fromObject); + } + $response = $this->auth($options); + $result = new CopyObjectResult($response); + return $result->getData(); + } + + /** + * 获取Object的Meta信息 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $options 具体参考SDK文档 + * @return array + */ + public function getObjectMeta($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_HEAD; + $options[self::OSS_OBJECT] = $object; + $response = $this->auth($options); + $result = new HeaderResult($response); + return $result->getData(); + } + + /** + * 删除某个Object + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param array $options + * @return null + */ + public function deleteObject($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_OBJECT] = $object; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 删除同一个Bucket中的多个Object + * + * @param string $bucket bucket名称 + * @param array $objects object列表 + * @param array $options + * @return ResponseCore + * @throws null + */ + public function deleteObjects($bucket, $objects, $options = null) + { + $this->precheckCommon($bucket, NULL, $options, false); + if (!is_array($objects) || !$objects) { + throw new OssException('objects must be array'); + } + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'delete'; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + $quiet = 'false'; + if (isset($options['quiet'])) { + if (is_bool($options['quiet'])) { //Boolean + $quiet = $options['quiet'] ? 'true' : 'false'; + } elseif (is_string($options['quiet'])) { // string + $quiet = ($options['quiet'] === 'true') ? 'true' : 'false'; + } + } + $xmlBody = OssUtil::createDeleteObjectsXmlBody($objects, $quiet); + $options[self::OSS_CONTENT] = $xmlBody; + $response = $this->auth($options); + $result = new DeleteObjectsResult($response); + return $result->getData(); + } + + /** + * 获得Object内容 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param array $options 该参数中必须设置ALIOSS::OSS_FILE_DOWNLOAD,ALIOSS::OSS_RANGE可选,可以根据实际情况设置;如果不设置,默认会下载全部内容 + * @return string + */ + public function getObject($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_OBJECT] = $object; + if (isset($options[self::OSS_LAST_MODIFIED])) { + $options[self::OSS_HEADERS][self::OSS_IF_MODIFIED_SINCE] = $options[self::OSS_LAST_MODIFIED]; + unset($options[self::OSS_LAST_MODIFIED]); + } + if (isset($options[self::OSS_ETAG])) { + $options[self::OSS_HEADERS][self::OSS_IF_NONE_MATCH] = $options[self::OSS_ETAG]; + unset($options[self::OSS_ETAG]); + } + if (isset($options[self::OSS_RANGE])) { + $range = $options[self::OSS_RANGE]; + $options[self::OSS_HEADERS][self::OSS_RANGE] = "bytes=$range"; + unset($options[self::OSS_RANGE]); + } + $response = $this->auth($options); + $result = new BodyResult($response); + return $result->getData(); + } + + /** + * 检测Object是否存在 + * 通过获取Object的Meta信息来判断Object是否存在, 用户需要自行解析ResponseCore判断object是否存在 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param array $options + * @return bool + */ + public function doesObjectExist($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_HEAD; + $options[self::OSS_OBJECT] = $object; + $response = $this->auth($options); + $result = new ExistResult($response); + return $result->getData(); + } + + /** + * 针对Archive类型的Object读取 + * 需要使用Restore操作让服务端执行解冻任务 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @return null + * @throws OssException + */ + public function restoreObject($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_SUB_RESOURCE] = self::OSS_RESTORE; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 获取分片大小,根据用户提供的part_size,重新计算一个更合理的partsize + * + * @param int $partSize + * @return int + */ + private function computePartSize($partSize) + { + $partSize = (integer)$partSize; + if ($partSize <= self::OSS_MIN_PART_SIZE) { + $partSize = self::OSS_MIN_PART_SIZE; + } elseif ($partSize > self::OSS_MAX_PART_SIZE) { + $partSize = self::OSS_MAX_PART_SIZE; + } + return $partSize; + } + + /** + * 计算文件可以分成多少个part,以及每个part的长度以及起始位置 + * 方法必须在 中调用 + * + * @param integer $file_size 文件大小 + * @param integer $partSize part大小,默认5M + * @return array An array 包含 key-value 键值对. Key 为 `seekTo` 和 `length`. + */ + public function generateMultiuploadParts($file_size, $partSize = 5242880) + { + $i = 0; + $size_count = $file_size; + $values = array(); + $partSize = $this->computePartSize($partSize); + while ($size_count > 0) { + $size_count -= $partSize; + $values[] = array( + self::OSS_SEEK_TO => ($partSize * $i), + self::OSS_LENGTH => (($size_count > 0) ? $partSize : ($size_count + $partSize)), + ); + $i++; + } + return $values; + } + + /** + * 初始化multi-part upload + * + * @param string $bucket Bucket名称 + * @param string $object Object名称 + * @param array $options Key-Value数组 + * @throws OssException + * @return string 返回uploadid + */ + public function initiateMultipartUpload($bucket, $object, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_SUB_RESOURCE] = 'uploads'; + $options[self::OSS_CONTENT] = ''; + + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object); + } + if (!isset($options[self::OSS_HEADERS])) { + $options[self::OSS_HEADERS] = array(); + } + $response = $this->auth($options); + $result = new InitiateMultipartUploadResult($response); + return $result->getData(); + } + + /** + * 分片上传的块上传接口 + * + * @param string $bucket Bucket名称 + * @param string $object Object名称 + * @param string $uploadId + * @param array $options Key-Value数组 + * @return string eTag + * @throws OssException + */ + public function uploadPart($bucket, $object, $uploadId, $options = null) + { + $this->precheckCommon($bucket, $object, $options); + $this->precheckParam($options, self::OSS_FILE_UPLOAD, __FUNCTION__); + $this->precheckParam($options, self::OSS_PART_NUM, __FUNCTION__); + + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_UPLOAD_ID] = $uploadId; + + if (isset($options[self::OSS_LENGTH])) { + $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; + } + $response = $this->auth($options); + $result = new UploadPartResult($response); + return $result->getData(); + } + + /** + * 获取已成功上传的part + * + * @param string $bucket Bucket名称 + * @param string $object Object名称 + * @param string $uploadId uploadId + * @param array $options Key-Value数组 + * @return ListPartsInfo + * @throws OssException + */ + public function listParts($bucket, $object, $uploadId, $options = null) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_UPLOAD_ID] = $uploadId; + $options[self::OSS_QUERY_STRING] = array(); + foreach (array('max-parts', 'part-number-marker') as $param) { + if (isset($options[$param])) { + $options[self::OSS_QUERY_STRING][$param] = $options[$param]; + unset($options[$param]); + } + } + $response = $this->auth($options); + $result = new ListPartsResult($response); + return $result->getData(); + } + + /** + * 中止进行一半的分片上传操作 + * + * @param string $bucket Bucket名称 + * @param string $object Object名称 + * @param string $uploadId uploadId + * @param array $options Key-Value数组 + * @return null + * @throws OssException + */ + public function abortMultipartUpload($bucket, $object, $uploadId, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_METHOD] = self::OSS_HTTP_DELETE; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_UPLOAD_ID] = $uploadId; + $response = $this->auth($options); + $result = new PutSetDeleteResult($response); + return $result->getData(); + } + + /** + * 在将所有数据Part都上传完成后,调用此接口完成本次分块上传 + * + * @param string $bucket Bucket名称 + * @param string $object Object名称 + * @param string $uploadId uploadId + * @param array $listParts array( array("PartNumber"=> int, "ETag"=>string)) + * @param array $options Key-Value数组 + * @throws OssException + * @return null + */ + public function completeMultipartUpload($bucket, $object, $uploadId, $listParts, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + $options[self::OSS_METHOD] = self::OSS_HTTP_POST; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_UPLOAD_ID] = $uploadId; + $options[self::OSS_CONTENT_TYPE] = 'application/xml'; + if (!is_array($listParts)) { + throw new OssException("listParts must be array type"); + } + $options[self::OSS_CONTENT] = OssUtil::createCompleteMultipartUploadXmlBody($listParts); + $response = $this->auth($options); + if (isset($options[self::OSS_CALLBACK]) && !empty($options[self::OSS_CALLBACK])) { + $result = new CallbackResult($response); + } else { + $result = new PutSetDeleteResult($response); + } + return $result->getData(); + } + + /** + * 罗列出所有执行中的Multipart Upload事件,即已经被初始化的Multipart Upload但是未被 + * Complete或者Abort的Multipart Upload事件 + * + * @param string $bucket bucket + * @param array $options 关联数组 + * @throws OssException + * @return ListMultipartUploadInfo + */ + public function listMultipartUploads($bucket, $options = null) + { + $this->precheckCommon($bucket, NULL, $options, false); + $options[self::OSS_METHOD] = self::OSS_HTTP_GET; + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = '/'; + $options[self::OSS_SUB_RESOURCE] = 'uploads'; + + foreach (array('delimiter', 'key-marker', 'max-uploads', 'prefix', 'upload-id-marker') as $param) { + if (isset($options[$param])) { + $options[self::OSS_QUERY_STRING][$param] = $options[$param]; + unset($options[$param]); + } + } + $query = isset($options[self::OSS_QUERY_STRING]) ? $options[self::OSS_QUERY_STRING] : array(); + $options[self::OSS_QUERY_STRING] = array_merge( + $query, + array(self::OSS_ENCODING_TYPE => self::OSS_ENCODING_TYPE_URL) + ); + + $response = $this->auth($options); + $result = new ListMultipartUploadResult($response); + return $result->getData(); + } + + /** + * 从一个已存在的Object中拷贝数据来上传一个Part + * + * @param string $fromBucket 源bucket名称 + * @param string $fromObject 源object名称 + * @param string $toBucket 目标bucket名称 + * @param string $toObject 目标object名称 + * @param int $partNumber 分块上传的块id + * @param string $uploadId 初始化multipart upload返回的uploadid + * @param array $options Key-Value数组 + * @return null + * @throws OssException + */ + public function uploadPartCopy($fromBucket, $fromObject, $toBucket, $toObject, $partNumber, $uploadId, $options = NULL) + { + $this->precheckCommon($fromBucket, $fromObject, $options); + $this->precheckCommon($toBucket, $toObject, $options); + + //如果没有设置$options['isFullCopy'],则需要强制判断copy的起止位置 + $start_range = "0"; + if (isset($options['start'])) { + $start_range = $options['start']; + } + $end_range = ""; + if (isset($options['end'])) { + $end_range = $options['end']; + } + $options[self::OSS_METHOD] = self::OSS_HTTP_PUT; + $options[self::OSS_BUCKET] = $toBucket; + $options[self::OSS_OBJECT] = $toObject; + $options[self::OSS_PART_NUM] = $partNumber; + $options[self::OSS_UPLOAD_ID] = $uploadId; + + if (!isset($options[self::OSS_HEADERS])) { + $options[self::OSS_HEADERS] = array(); + } + + $options[self::OSS_HEADERS][self::OSS_OBJECT_COPY_SOURCE] = '/' . $fromBucket . '/' . $fromObject; + $options[self::OSS_HEADERS][self::OSS_OBJECT_COPY_SOURCE_RANGE] = "bytes=" . $start_range . "-" . $end_range; + $response = $this->auth($options); + $result = new UploadPartResult($response); + return $result->getData(); + } + + /** + * multipart上传统一封装,从初始化到完成multipart,以及出错后中止动作 + * + * @param string $bucket bucket名称 + * @param string $object object名称 + * @param string $file 需要上传的本地文件的路径 + * @param array $options Key-Value数组 + * @return null + * @throws OssException + */ + public function multiuploadFile($bucket, $object, $file, $options = null) + { + $this->precheckCommon($bucket, $object, $options); + if (isset($options[self::OSS_LENGTH])) { + $options[self::OSS_CONTENT_LENGTH] = $options[self::OSS_LENGTH]; + unset($options[self::OSS_LENGTH]); + } + if (empty($file)) { + throw new OssException("parameter invalid, file is empty"); + } + $uploadFile = OssUtil::encodePath($file); + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = $this->getMimeType($object, $uploadFile); + } + + $upload_position = isset($options[self::OSS_SEEK_TO]) ? (integer)$options[self::OSS_SEEK_TO] : 0; + + if (isset($options[self::OSS_CONTENT_LENGTH])) { + $upload_file_size = (integer)$options[self::OSS_CONTENT_LENGTH]; + } else { + $upload_file_size = filesize($uploadFile); + if ($upload_file_size !== false) { + $upload_file_size -= $upload_position; + } + } + + if ($upload_position === false || !isset($upload_file_size) || $upload_file_size === false || $upload_file_size < 0) { + throw new OssException('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().'); + } + // 处理partSize + if (isset($options[self::OSS_PART_SIZE])) { + $options[self::OSS_PART_SIZE] = $this->computePartSize($options[self::OSS_PART_SIZE]); + } else { + $options[self::OSS_PART_SIZE] = self::OSS_MID_PART_SIZE; + } + + $is_check_md5 = $this->isCheckMD5($options); + // 如果上传的文件小于partSize,则直接使用普通方式上传 + if ($upload_file_size < $options[self::OSS_PART_SIZE] && !isset($options[self::OSS_UPLOAD_ID])) { + return $this->uploadFile($bucket, $object, $uploadFile, $options); + } + + // 初始化multipart + if (isset($options[self::OSS_UPLOAD_ID])) { + $uploadId = $options[self::OSS_UPLOAD_ID]; + } else { + // 初始化 + $uploadId = $this->initiateMultipartUpload($bucket, $object, $options); + } + + // 获取的分片 + $pieces = $this->generateMultiuploadParts($upload_file_size, (integer)$options[self::OSS_PART_SIZE]); + $response_upload_part = array(); + foreach ($pieces as $i => $piece) { + $from_pos = $upload_position + (integer)$piece[self::OSS_SEEK_TO]; + $to_pos = (integer)$piece[self::OSS_LENGTH] + $from_pos - 1; + $up_options = array( + self::OSS_FILE_UPLOAD => $uploadFile, + self::OSS_PART_NUM => ($i + 1), + self::OSS_SEEK_TO => $from_pos, + self::OSS_LENGTH => $to_pos - $from_pos + 1, + self::OSS_CHECK_MD5 => $is_check_md5, + ); + if ($is_check_md5) { + $content_md5 = OssUtil::getMd5SumForFile($uploadFile, $from_pos, $to_pos); + $up_options[self::OSS_CONTENT_MD5] = $content_md5; + } + $response_upload_part[] = $this->uploadPart($bucket, $object, $uploadId, $up_options); + } + + $uploadParts = array(); + foreach ($response_upload_part as $i => $etag) { + $uploadParts[] = array( + 'PartNumber' => ($i + 1), + 'ETag' => $etag, + ); + } + return $this->completeMultipartUpload($bucket, $object, $uploadId, $uploadParts); + } + + /** + * 上传本地目录内的文件或者目录到指定bucket的指定prefix的object中 + * + * @param string $bucket bucket名称 + * @param string $prefix 需要上传到的object的key前缀,可以理解成bucket中的子目录,结尾不能是'/',接口中会补充'/' + * @param string $localDirectory 需要上传的本地目录 + * @param string $exclude 需要排除的目录 + * @param bool $recursive 是否递归的上传localDirectory下的子目录内容 + * @param bool $checkMd5 + * @return array 返回两个列表 array("succeededList" => array("object"), "failedList" => array("object"=>"errorMessage")) + * @throws OssException + */ + public function uploadDir($bucket, $prefix, $localDirectory, $exclude = '.|..|.svn|.git', $recursive = false, $checkMd5 = true) + { + $retArray = array("succeededList" => array(), "failedList" => array()); + if (empty($bucket)) throw new OssException("parameter error, bucket is empty"); + if (!is_string($prefix)) throw new OssException("parameter error, prefix is not string"); + if (empty($localDirectory)) throw new OssException("parameter error, localDirectory is empty"); + $directory = $localDirectory; + $directory = OssUtil::encodePath($directory); + //判断是否目录 + if (!is_dir($directory)) { + throw new OssException('parameter error: ' . $directory . ' is not a directory, please check it'); + } + //read directory + $file_list_array = OssUtil::readDir($directory, $exclude, $recursive); + if (!$file_list_array) { + throw new OssException($directory . ' is empty...'); + } + foreach ($file_list_array as $k => $item) { + if (is_dir($item['path'])) { + continue; + } + $options = array( + self::OSS_PART_SIZE => self::OSS_MIN_PART_SIZE, + self::OSS_CHECK_MD5 => $checkMd5, + ); + $realObject = (!empty($prefix) ? $prefix . '/' : '') . $item['file']; + + try { + $this->multiuploadFile($bucket, $realObject, $item['path'], $options); + $retArray["succeededList"][] = $realObject; + } catch (OssException $e) { + $retArray["failedList"][$realObject] = $e->getMessage(); + } + } + return $retArray; + } + + /** + * 支持生成get和put签名, 用户可以生成一个具有一定有效期的 + * 签名过的url + * + * @param string $bucket + * @param string $object + * @param int $timeout + * @param string $method + * @param array $options Key-Value数组 + * @return string + * @throws OssException + */ + public function signUrl($bucket, $object, $timeout = 60, $method = self::OSS_HTTP_GET, $options = NULL) + { + $this->precheckCommon($bucket, $object, $options); + //method + if (self::OSS_HTTP_GET !== $method && self::OSS_HTTP_PUT !== $method) { + throw new OssException("method is invalid"); + } + $options[self::OSS_BUCKET] = $bucket; + $options[self::OSS_OBJECT] = $object; + $options[self::OSS_METHOD] = $method; + if (!isset($options[self::OSS_CONTENT_TYPE])) { + $options[self::OSS_CONTENT_TYPE] = ''; + } + $timeout = time() + $timeout; + $options[self::OSS_PREAUTH] = $timeout; + $options[self::OSS_DATE] = $timeout; + $this->setSignStsInUrl(true); + return $this->auth($options); + } + + /** + * 检测options参数 + * + * @param array $options + * @throws OssException + */ + private function precheckOptions(&$options) + { + OssUtil::validateOptions($options); + if (!$options) { + $options = array(); + } + } + + /** + * 校验bucket参数 + * + * @param string $bucket + * @param string $errMsg + * @throws OssException + */ + private function precheckBucket($bucket, $errMsg = 'bucket is not allowed empty') + { + OssUtil::throwOssExceptionWithMessageIfEmpty($bucket, $errMsg); + } + + /** + * 校验object参数 + * + * @param string $object + * @throws OssException + */ + private function precheckObject($object) + { + OssUtil::throwOssExceptionWithMessageIfEmpty($object, "object name is empty"); + } + + /** + * 校验option restore + * + * @param string $restore + * @throws OssException + */ + private function precheckStorage($storage) + { + if (is_string($storage)) { + switch ($storage) { + case self::OSS_STORAGE_ARCHIVE: + return; + case self::OSS_STORAGE_IA: + return; + case self::OSS_STORAGE_STANDARD: + return; + default: + break; + } + } + throw new OssException('storage name is invalid'); + } + + /** + * 校验bucket,options参数 + * + * @param string $bucket + * @param string $object + * @param array $options + * @param bool $isCheckObject + */ + private function precheckCommon($bucket, $object, &$options, $isCheckObject = true) + { + if ($isCheckObject) { + $this->precheckObject($object); + } + $this->precheckOptions($options); + $this->precheckBucket($bucket); + } + + /** + * 参数校验 + * + * @param array $options + * @param string $param + * @param string $funcName + * @throws OssException + */ + private function precheckParam($options, $param, $funcName) + { + if (!isset($options[$param])) { + throw new OssException('The `' . $param . '` options is required in ' . $funcName . '().'); + } + } + + /** + * 检测md5 + * + * @param array $options + * @return bool|null + */ + private function isCheckMD5($options) + { + return $this->getValue($options, self::OSS_CHECK_MD5, false, true, true); + } + + /** + * 获取value + * + * @param array $options + * @param string $key + * @param string $default + * @param bool $isCheckEmpty + * @param bool $isCheckBool + * @return bool|null + */ + private function getValue($options, $key, $default = NULL, $isCheckEmpty = false, $isCheckBool = false) + { + $value = $default; + if (isset($options[$key])) { + if ($isCheckEmpty) { + if (!empty($options[$key])) { + $value = $options[$key]; + } + } else { + $value = $options[$key]; + } + unset($options[$key]); + } + if ($isCheckBool) { + if ($value !== true && $value !== false) { + $value = false; + } + } + return $value; + } + + /** + * 获取mimetype类型 + * + * @param string $object + * @return string + */ + private function getMimeType($object, $file = null) + { + if (!is_null($file)) { + $type = MimeTypes::getMimetype($file); + if (!is_null($type)) { + return $type; + } + } + + $type = MimeTypes::getMimetype($object); + if (!is_null($type)) { + return $type; + } + + return self::DEFAULT_CONTENT_TYPE; + } + + /** + * 验证并且执行请求,按照OSS Api协议,执行操作 + * + * @param array $options + * @return ResponseCore + * @throws OssException + * @throws RequestCore_Exception + */ + private function auth($options) + { + OssUtil::validateOptions($options); + //验证bucket,list_bucket时不需要验证 + $this->authPrecheckBucket($options); + //验证object + $this->authPrecheckObject($options); + //Object名称的编码必须是utf8 + $this->authPrecheckObjectEncoding($options); + //验证ACL + $this->authPrecheckAcl($options); + // 获得当次请求使用的协议头,是https还是http + $scheme = $this->useSSL ? 'https://' : 'http://'; + // 获得当次请求使用的hostname,如果是公共域名或者专有域名,bucket拼在前面构成三级域名 + $hostname = $this->generateHostname($options[self::OSS_BUCKET]); + $string_to_sign = ''; + $headers = $this->generateHeaders($options, $hostname); + $signable_query_string_params = $this->generateSignableQueryStringParam($options); + $signable_query_string = OssUtil::toQueryString($signable_query_string_params); + $resource_uri = $this->generateResourceUri($options); + //生成请求URL + $conjunction = '?'; + $non_signable_resource = ''; + if (isset($options[self::OSS_SUB_RESOURCE])) { + $conjunction = '&'; + } + if ($signable_query_string !== '') { + $signable_query_string = $conjunction . $signable_query_string; + $conjunction = '&'; + } + $query_string = $this->generateQueryString($options); + if ($query_string !== '') { + $non_signable_resource .= $conjunction . $query_string; + $conjunction = '&'; + } + $this->requestUrl = $scheme . $hostname . $resource_uri . $signable_query_string . $non_signable_resource; + + //创建请求 + $request = new RequestCore($this->requestUrl, $this->requestProxy); + $request->set_useragent($this->generateUserAgent()); + // Streaming uploads + if (isset($options[self::OSS_FILE_UPLOAD])) { + if (is_resource($options[self::OSS_FILE_UPLOAD])) { + $length = null; + + if (isset($options[self::OSS_CONTENT_LENGTH])) { + $length = $options[self::OSS_CONTENT_LENGTH]; + } elseif (isset($options[self::OSS_SEEK_TO])) { + $stats = fstat($options[self::OSS_FILE_UPLOAD]); + if ($stats && $stats[self::OSS_SIZE] >= 0) { + $length = $stats[self::OSS_SIZE] - (integer)$options[self::OSS_SEEK_TO]; + } + } + $request->set_read_stream($options[self::OSS_FILE_UPLOAD], $length); + } else { + $request->set_read_file($options[self::OSS_FILE_UPLOAD]); + $length = $request->read_stream_size; + if (isset($options[self::OSS_CONTENT_LENGTH])) { + $length = $options[self::OSS_CONTENT_LENGTH]; + } elseif (isset($options[self::OSS_SEEK_TO]) && isset($length)) { + $length -= (integer)$options[self::OSS_SEEK_TO]; + } + $request->set_read_stream_size($length); + } + } + if (isset($options[self::OSS_SEEK_TO])) { + $request->set_seek_position((integer)$options[self::OSS_SEEK_TO]); + } + if (isset($options[self::OSS_FILE_DOWNLOAD])) { + if (is_resource($options[self::OSS_FILE_DOWNLOAD])) { + $request->set_write_stream($options[self::OSS_FILE_DOWNLOAD]); + } else { + $request->set_write_file($options[self::OSS_FILE_DOWNLOAD]); + } + } + + if (isset($options[self::OSS_METHOD])) { + $request->set_method($options[self::OSS_METHOD]); + $string_to_sign .= $options[self::OSS_METHOD] . "\n"; + } + + if (isset($options[self::OSS_CONTENT])) { + $request->set_body($options[self::OSS_CONTENT]); + if ($headers[self::OSS_CONTENT_TYPE] === 'application/x-www-form-urlencoded') { + $headers[self::OSS_CONTENT_TYPE] = 'application/octet-stream'; + } + + $headers[self::OSS_CONTENT_LENGTH] = strlen($options[self::OSS_CONTENT]); + $headers[self::OSS_CONTENT_MD5] = base64_encode(md5($options[self::OSS_CONTENT], true)); + } + + if (isset($options[self::OSS_CALLBACK])) { + $headers[self::OSS_CALLBACK] = base64_encode($options[self::OSS_CALLBACK]); + } + if (isset($options[self::OSS_CALLBACK_VAR])) { + $headers[self::OSS_CALLBACK_VAR] = base64_encode($options[self::OSS_CALLBACK_VAR]); + } + + if (!isset($headers[self::OSS_ACCEPT_ENCODING])) { + $headers[self::OSS_ACCEPT_ENCODING] = ''; + } + + uksort($headers, 'strnatcasecmp'); + + foreach ($headers as $header_key => $header_value) { + $header_value = str_replace(array("\r", "\n"), '', $header_value); + if ($header_value !== '' || $header_key === self::OSS_ACCEPT_ENCODING) { + $request->add_header($header_key, $header_value); + } + + if ( + strtolower($header_key) === 'content-md5' || + strtolower($header_key) === 'content-type' || + strtolower($header_key) === 'date' || + (isset($options['self::OSS_PREAUTH']) && (integer)$options['self::OSS_PREAUTH'] > 0) + ) { + $string_to_sign .= $header_value . "\n"; + } elseif (substr(strtolower($header_key), 0, 6) === self::OSS_DEFAULT_PREFIX) { + $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; + } + } + // 生成 signable_resource + $signable_resource = $this->generateSignableResource($options); + $string_to_sign .= rawurldecode($signable_resource) . urldecode($signable_query_string); + + //对?后面的要签名的string字母序排序 + $string_to_sign_ordered = $this->stringToSignSorted($string_to_sign); + + $signature = base64_encode(hash_hmac('sha1', $string_to_sign_ordered, $this->accessKeySecret, true)); + $request->add_header('Authorization', 'OSS ' . $this->accessKeyId . ':' . $signature); + + if (isset($options[self::OSS_PREAUTH]) && (integer)$options[self::OSS_PREAUTH] > 0) { + $signed_url = $this->requestUrl . $conjunction . self::OSS_URL_ACCESS_KEY_ID . '=' . rawurlencode($this->accessKeyId) . '&' . self::OSS_URL_EXPIRES . '=' . $options[self::OSS_PREAUTH] . '&' . self::OSS_URL_SIGNATURE . '=' . rawurlencode($signature); + return $signed_url; + } elseif (isset($options[self::OSS_PREAUTH])) { + return $this->requestUrl; + } + + if ($this->timeout !== 0) { + $request->timeout = $this->timeout; + } + if ($this->connectTimeout !== 0) { + $request->connect_timeout = $this->connectTimeout; + } + + try { + $request->send_request(); + } catch (RequestCore_Exception $e) { + throw(new OssException('RequestCoreException: ' . $e->getMessage())); + } + $response_header = $request->get_response_header(); + $response_header['oss-request-url'] = $this->requestUrl; + $response_header['oss-redirects'] = $this->redirects; + $response_header['oss-stringtosign'] = $string_to_sign; + $response_header['oss-requestheaders'] = $request->request_headers; + + $data = new ResponseCore($response_header, $request->get_response_body(), $request->get_response_code()); + //retry if OSS Internal Error + if ((integer)$request->get_response_code() === 500) { + if ($this->redirects <= $this->maxRetries) { + //设置休眠 + $delay = (integer)(pow(4, $this->redirects) * 100000); + usleep($delay); + $this->redirects++; + $data = $this->auth($options); + } + } + + $this->redirects = 0; + return $data; + } + + /** + * 设置最大尝试次数 + * + * @param int $maxRetries + * @return void + */ + public function setMaxTries($maxRetries = 3) + { + $this->maxRetries = $maxRetries; + } + + /** + * 获取最大尝试次数 + * + * @return int + */ + public function getMaxRetries() + { + return $this->maxRetries; + } + + /** + * 打开sts enable标志,使用户构造函数中传入的$sts生效 + * + * @param boolean $enable + */ + public function setSignStsInUrl($enable) + { + $this->enableStsInUrl = $enable; + } + + /** + * @return boolean + */ + public function isUseSSL() + { + return $this->useSSL; + } + + /** + * @param boolean $useSSL + */ + public function setUseSSL($useSSL) + { + $this->useSSL = $useSSL; + } + + /** + * 检查bucket名称格式是否正确,如果非法抛出异常 + * + * @param $options + * @throws OssException + */ + private function authPrecheckBucket($options) + { + if (!(('/' == $options[self::OSS_OBJECT]) && ('' == $options[self::OSS_BUCKET]) && ('GET' == $options[self::OSS_METHOD])) && !OssUtil::validateBucket($options[self::OSS_BUCKET])) { + throw new OssException('"' . $options[self::OSS_BUCKET] . '"' . 'bucket name is invalid'); + } + } + + /** + * + * 检查object名称格式是否正确,如果非法抛出异常 + * + * @param $options + * @throws OssException + */ + private function authPrecheckObject($options) + { + if (isset($options[self::OSS_OBJECT]) && $options[self::OSS_OBJECT] === '/') { + return; + } + + if (isset($options[self::OSS_OBJECT]) && !OssUtil::validateObject($options[self::OSS_OBJECT])) { + throw new OssException('"' . $options[self::OSS_OBJECT] . '"' . ' object name is invalid'); + } + } + + /** + * 检查object的编码,如果是gbk或者gb2312则尝试将其转化为utf8编码 + * + * @param mixed $options 参数 + */ + private function authPrecheckObjectEncoding(&$options) + { + $tmp_object = $options[self::OSS_OBJECT]; + try { + if (OssUtil::isGb2312($options[self::OSS_OBJECT])) { + $options[self::OSS_OBJECT] = iconv('GB2312', "UTF-8//IGNORE", $options[self::OSS_OBJECT]); + } elseif (OssUtil::checkChar($options[self::OSS_OBJECT], true)) { + $options[self::OSS_OBJECT] = iconv('GBK', "UTF-8//IGNORE", $options[self::OSS_OBJECT]); + } + } catch (\Exception $e) { + try { + $tmp_object = iconv(mb_detect_encoding($tmp_object), "UTF-8", $tmp_object); + } catch (\Exception $e) { + } + } + $options[self::OSS_OBJECT] = $tmp_object; + } + + /** + * 检查ACL是否是预定义中三种之一,如果不是抛出异常 + * + * @param $options + * @throws OssException + */ + private function authPrecheckAcl($options) + { + if (isset($options[self::OSS_HEADERS][self::OSS_ACL]) && !empty($options[self::OSS_HEADERS][self::OSS_ACL])) { + if (!in_array(strtolower($options[self::OSS_HEADERS][self::OSS_ACL]), self::$OSS_ACL_TYPES)) { + throw new OssException($options[self::OSS_HEADERS][self::OSS_ACL] . ':' . 'acl is invalid(private,public-read,public-read-write)'); + } + } + } + + /** + * 获得档次请求使用的域名 + * bucket在前的三级域名,或者二级域名,如果是cname或者ip的话,则是二级域名 + * + * @param $bucket + * @return string 剥掉协议头的域名 + */ + private function generateHostname($bucket) + { + if ($this->hostType === self::OSS_HOST_TYPE_IP) { + $hostname = $this->hostname; + } elseif ($this->hostType === self::OSS_HOST_TYPE_CNAME) { + $hostname = $this->hostname; + } else { + // 专有域或者官网endpoint + $hostname = ($bucket == '') ? $this->hostname : ($bucket . '.') . $this->hostname; + } + return $hostname; + } + + /** + * 获得当次请求的资源定位字段 + * + * @param $options + * @return string 资源定位字段 + */ + private function generateResourceUri($options) + { + $resource_uri = ""; + + // resource_uri + bucket + if (isset($options[self::OSS_BUCKET]) && '' !== $options[self::OSS_BUCKET]) { + if ($this->hostType === self::OSS_HOST_TYPE_IP) { + $resource_uri = '/' . $options[self::OSS_BUCKET]; + } + } + + // resource_uri + object + if (isset($options[self::OSS_OBJECT]) && '/' !== $options[self::OSS_OBJECT]) { + $resource_uri .= '/' . str_replace(array('%2F', '%25'), array('/', '%'), rawurlencode($options[self::OSS_OBJECT])); + } + + // resource_uri + sub_resource + $conjunction = '?'; + if (isset($options[self::OSS_SUB_RESOURCE])) { + $resource_uri .= $conjunction . $options[self::OSS_SUB_RESOURCE]; + } + return $resource_uri; + } + + /** + * 生成signalbe_query_string_param, array类型 + * + * @param array $options + * @return array + */ + private function generateSignableQueryStringParam($options) + { + $signableQueryStringParams = array(); + $signableList = array( + self::OSS_PART_NUM, + 'response-content-type', + 'response-content-language', + 'response-cache-control', + 'response-content-encoding', + 'response-expires', + 'response-content-disposition', + self::OSS_UPLOAD_ID, + self::OSS_COMP, + self::OSS_LIVE_CHANNEL_STATUS, + self::OSS_LIVE_CHANNEL_START_TIME, + self::OSS_LIVE_CHANNEL_END_TIME, + self::OSS_PROCESS, + self::OSS_POSITION, + self::OSS_SYMLINK, + self::OSS_RESTORE, + ); + + foreach ($signableList as $item) { + if (isset($options[$item])) { + $signableQueryStringParams[$item] = $options[$item]; + } + } + + if ($this->enableStsInUrl && (!is_null($this->securityToken))) { + $signableQueryStringParams["security-token"] = $this->securityToken; + } + + return $signableQueryStringParams; + } + + /** + * 生成用于签名resource段 + * + * @param mixed $options + * @return string + */ + private function generateSignableResource($options) + { + $signableResource = ""; + $signableResource .= '/'; + if (isset($options[self::OSS_BUCKET]) && '' !== $options[self::OSS_BUCKET]) { + $signableResource .= $options[self::OSS_BUCKET]; + // 如果操作没有Object操作的话,这里最后是否有斜线有个trick,ip的域名下,不需要加'/', 否则需要加'/' + if ($options[self::OSS_OBJECT] == '/') { + if ($this->hostType !== self::OSS_HOST_TYPE_IP) { + $signableResource .= "/"; + } + } + } + //signable_resource + object + if (isset($options[self::OSS_OBJECT]) && '/' !== $options[self::OSS_OBJECT]) { + $signableResource .= '/' . str_replace(array('%2F', '%25'), array('/', '%'), rawurlencode($options[self::OSS_OBJECT])); + } + if (isset($options[self::OSS_SUB_RESOURCE])) { + $signableResource .= '?' . $options[self::OSS_SUB_RESOURCE]; + } + return $signableResource; + } + + /** + * 生成query_string + * + * @param mixed $options + * @return string + */ + private function generateQueryString($options) + { + //请求参数 + $queryStringParams = array(); + if (isset($options[self::OSS_QUERY_STRING])) { + $queryStringParams = array_merge($queryStringParams, $options[self::OSS_QUERY_STRING]); + } + return OssUtil::toQueryString($queryStringParams); + } + + private function stringToSignSorted($string_to_sign) + { + $queryStringSorted = ''; + $explodeResult = explode('?', $string_to_sign); + $index = count($explodeResult); + if ($index === 1) + return $string_to_sign; + + $queryStringParams = explode('&', $explodeResult[$index - 1]); + sort($queryStringParams); + + foreach($queryStringParams as $params) + { + $queryStringSorted .= $params . '&'; + } + + $queryStringSorted = substr($queryStringSorted, 0, -1); + + return $explodeResult[0] . '?' . $queryStringSorted; + } + + /** + * 初始化headers + * + * @param mixed $options + * @param string $hostname hostname + * @return array + */ + private function generateHeaders($options, $hostname) + { + $headers = array( + self::OSS_CONTENT_MD5 => '', + self::OSS_CONTENT_TYPE => isset($options[self::OSS_CONTENT_TYPE]) ? $options[self::OSS_CONTENT_TYPE] : self::DEFAULT_CONTENT_TYPE, + self::OSS_DATE => isset($options[self::OSS_DATE]) ? $options[self::OSS_DATE] : gmdate('D, d M Y H:i:s \G\M\T'), + self::OSS_HOST => $hostname, + ); + if (isset($options[self::OSS_CONTENT_MD5])) { + $headers[self::OSS_CONTENT_MD5] = $options[self::OSS_CONTENT_MD5]; + } + + //添加stsSecurityToken + if ((!is_null($this->securityToken)) && (!$this->enableStsInUrl)) { + $headers[self::OSS_SECURITY_TOKEN] = $this->securityToken; + } + //合并HTTP headers + if (isset($options[self::OSS_HEADERS])) { + $headers = array_merge($headers, $options[self::OSS_HEADERS]); + } + return $headers; + } + + /** + * 生成请求用的UserAgent + * + * @return string + */ + private function generateUserAgent() + { + return self::OSS_NAME . "/" . self::OSS_VERSION . " (" . php_uname('s') . "/" . php_uname('r') . "/" . php_uname('m') . ";" . PHP_VERSION . ")"; + } + + /** + * 检查endpoint的种类 + * 如有有协议头,剥去协议头 + * 并且根据参数 is_cname 和endpoint本身,判定域名类型,是ip,cname,还是专有域或者官网域名 + * + * @param string $endpoint + * @param boolean $isCName + * @return string 剥掉协议头的域名 + */ + private function checkEndpoint($endpoint, $isCName) + { + $ret_endpoint = null; + if (strpos($endpoint, 'http://') === 0) { + $ret_endpoint = substr($endpoint, strlen('http://')); + } elseif (strpos($endpoint, 'https://') === 0) { + $ret_endpoint = substr($endpoint, strlen('https://')); + $this->useSSL = true; + } else { + $ret_endpoint = $endpoint; + } + + if ($isCName) { + $this->hostType = self::OSS_HOST_TYPE_CNAME; + } elseif (OssUtil::isIPFormat($ret_endpoint)) { + $this->hostType = self::OSS_HOST_TYPE_IP; + } else { + $this->hostType = self::OSS_HOST_TYPE_NORMAL; + } + return $ret_endpoint; + } + + /** + * 用来检查sdk所以来的扩展是否打开 + * + * @throws OssException + */ + public static function checkEnv() + { + if (function_exists('get_loaded_extensions')) { + //检测curl扩展 + $enabled_extension = array("curl"); + $extensions = get_loaded_extensions(); + if ($extensions) { + foreach ($enabled_extension as $item) { + if (!in_array($item, $extensions)) { + throw new OssException("Extension {" . $item . "} is not installed or not enabled, please check your php env."); + } + } + } else { + throw new OssException("function get_loaded_extensions not found."); + } + } else { + throw new OssException('Function get_loaded_extensions has been disabled, please check php config.'); + } + } + + /** + //* 设置http库的请求超时时间,单位秒 + * + * @param int $timeout + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout; + } + + /** + * 设置http库的连接超时时间,单位秒 + * + * @param int $connectTimeout + */ + public function setConnectTimeout($connectTimeout) + { + $this->connectTimeout = $connectTimeout; + } + + // 生命周期相关常量 + const OSS_LIFECYCLE_EXPIRATION = "Expiration"; + const OSS_LIFECYCLE_TIMING_DAYS = "Days"; + const OSS_LIFECYCLE_TIMING_DATE = "Date"; + //OSS 内部常量 + const OSS_BUCKET = 'bucket'; + const OSS_OBJECT = 'object'; + const OSS_HEADERS = OssUtil::OSS_HEADERS; + const OSS_METHOD = 'method'; + const OSS_QUERY = 'query'; + const OSS_BASENAME = 'basename'; + const OSS_MAX_KEYS = 'max-keys'; + const OSS_UPLOAD_ID = 'uploadId'; + const OSS_PART_NUM = 'partNumber'; + const OSS_COMP = 'comp'; + const OSS_LIVE_CHANNEL_STATUS = 'status'; + const OSS_LIVE_CHANNEL_START_TIME = 'startTime'; + const OSS_LIVE_CHANNEL_END_TIME = 'endTime'; + const OSS_POSITION = 'position'; + const OSS_MAX_KEYS_VALUE = 100; + const OSS_MAX_OBJECT_GROUP_VALUE = OssUtil::OSS_MAX_OBJECT_GROUP_VALUE; + const OSS_MAX_PART_SIZE = OssUtil::OSS_MAX_PART_SIZE; + const OSS_MID_PART_SIZE = OssUtil::OSS_MID_PART_SIZE; + const OSS_MIN_PART_SIZE = OssUtil::OSS_MIN_PART_SIZE; + const OSS_FILE_SLICE_SIZE = 8192; + const OSS_PREFIX = 'prefix'; + const OSS_DELIMITER = 'delimiter'; + const OSS_MARKER = 'marker'; + const OSS_ACCEPT_ENCODING = 'Accept-Encoding'; + const OSS_CONTENT_MD5 = 'Content-Md5'; + const OSS_SELF_CONTENT_MD5 = 'x-oss-meta-md5'; + const OSS_CONTENT_TYPE = 'Content-Type'; + const OSS_CONTENT_LENGTH = 'Content-Length'; + const OSS_IF_MODIFIED_SINCE = 'If-Modified-Since'; + const OSS_IF_UNMODIFIED_SINCE = 'If-Unmodified-Since'; + const OSS_IF_MATCH = 'If-Match'; + const OSS_IF_NONE_MATCH = 'If-None-Match'; + const OSS_CACHE_CONTROL = 'Cache-Control'; + const OSS_EXPIRES = 'Expires'; + const OSS_PREAUTH = 'preauth'; + const OSS_CONTENT_COING = 'Content-Coding'; + const OSS_CONTENT_DISPOSTION = 'Content-Disposition'; + const OSS_RANGE = 'range'; + const OSS_ETAG = 'etag'; + const OSS_LAST_MODIFIED = 'lastmodified'; + const OS_CONTENT_RANGE = 'Content-Range'; + const OSS_CONTENT = OssUtil::OSS_CONTENT; + const OSS_BODY = 'body'; + const OSS_LENGTH = OssUtil::OSS_LENGTH; + const OSS_HOST = 'Host'; + const OSS_DATE = 'Date'; + const OSS_AUTHORIZATION = 'Authorization'; + const OSS_FILE_DOWNLOAD = 'fileDownload'; + const OSS_FILE_UPLOAD = 'fileUpload'; + const OSS_PART_SIZE = 'partSize'; + const OSS_SEEK_TO = 'seekTo'; + const OSS_SIZE = 'size'; + const OSS_QUERY_STRING = 'query_string'; + const OSS_SUB_RESOURCE = 'sub_resource'; + const OSS_DEFAULT_PREFIX = 'x-oss-'; + const OSS_CHECK_MD5 = 'checkmd5'; + const DEFAULT_CONTENT_TYPE = 'application/octet-stream'; + const OSS_SYMLINK_TARGET = 'x-oss-symlink-target'; + const OSS_SYMLINK = 'symlink'; + const OSS_HTTP_CODE = 'http_code'; + const OSS_REQUEST_ID = 'x-oss-request-id'; + const OSS_INFO = 'info'; + const OSS_STORAGE = 'storage'; + const OSS_RESTORE = 'restore'; + const OSS_STORAGE_STANDARD = 'Standard'; + const OSS_STORAGE_IA = 'IA'; + const OSS_STORAGE_ARCHIVE = 'Archive'; + + //私有URL变量 + const OSS_URL_ACCESS_KEY_ID = 'OSSAccessKeyId'; + const OSS_URL_EXPIRES = 'Expires'; + const OSS_URL_SIGNATURE = 'Signature'; + //HTTP方法 + const OSS_HTTP_GET = 'GET'; + const OSS_HTTP_PUT = 'PUT'; + const OSS_HTTP_HEAD = 'HEAD'; + const OSS_HTTP_POST = 'POST'; + const OSS_HTTP_DELETE = 'DELETE'; + const OSS_HTTP_OPTIONS = 'OPTIONS'; + //其他常量 + const OSS_ACL = 'x-oss-acl'; + const OSS_OBJECT_ACL = 'x-oss-object-acl'; + const OSS_OBJECT_GROUP = 'x-oss-file-group'; + const OSS_MULTI_PART = 'uploads'; + const OSS_MULTI_DELETE = 'delete'; + const OSS_OBJECT_COPY_SOURCE = 'x-oss-copy-source'; + const OSS_OBJECT_COPY_SOURCE_RANGE = "x-oss-copy-source-range"; + const OSS_PROCESS = "x-oss-process"; + const OSS_CALLBACK = "x-oss-callback"; + const OSS_CALLBACK_VAR = "x-oss-callback-var"; + //支持STS SecurityToken + const OSS_SECURITY_TOKEN = "x-oss-security-token"; + const OSS_ACL_TYPE_PRIVATE = 'private'; + const OSS_ACL_TYPE_PUBLIC_READ = 'public-read'; + const OSS_ACL_TYPE_PUBLIC_READ_WRITE = 'public-read-write'; + const OSS_ENCODING_TYPE = "encoding-type"; + const OSS_ENCODING_TYPE_URL = "url"; + + // 域名类型 + const OSS_HOST_TYPE_NORMAL = "normal";//http://bucket.oss-cn-hangzhou.aliyuncs.com/object + const OSS_HOST_TYPE_IP = "ip"; //http://1.1.1.1/bucket/object + const OSS_HOST_TYPE_SPECIAL = 'special'; //http://bucket.guizhou.gov/object + const OSS_HOST_TYPE_CNAME = "cname"; //http://mydomain.com/object + //OSS ACL数组 + static $OSS_ACL_TYPES = array( + self::OSS_ACL_TYPE_PRIVATE, + self::OSS_ACL_TYPE_PUBLIC_READ, + self::OSS_ACL_TYPE_PUBLIC_READ_WRITE + ); + // OssClient版本信息 + const OSS_NAME = "aliyun-sdk-php"; + const OSS_VERSION = "2.3.0"; + const OSS_BUILD = "20180105"; + const OSS_AUTHOR = ""; + const OSS_OPTIONS_ORIGIN = 'Origin'; + const OSS_OPTIONS_REQUEST_METHOD = 'Access-Control-Request-Method'; + const OSS_OPTIONS_REQUEST_HEADERS = 'Access-Control-Request-Headers'; + + //是否使用ssl + private $useSSL = false; + private $maxRetries = 3; + private $redirects = 0; + + // 用户提供的域名类型,有四种 OSS_HOST_TYPE_NORMAL, OSS_HOST_TYPE_IP, OSS_HOST_TYPE_SPECIAL, OSS_HOST_TYPE_CNAME + private $hostType = self::OSS_HOST_TYPE_NORMAL; + private $requestUrl; + private $accessKeyId; + private $accessKeySecret; + private $hostname; + private $securityToken; + private $requestProxy = null; + private $enableStsInUrl = false; + private $timeout = 0; + private $connectTimeout = 0; +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AclResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AclResult.php new file mode 100644 index 0000000..6da0860 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AclResult.php @@ -0,0 +1,32 @@ +rawResponse->body; + if (empty($content)) { + throw new OssException("body is null"); + } + $xml = simplexml_load_string($content); + if (isset($xml->AccessControlList->Grant)) { + return strval($xml->AccessControlList->Grant); + } else { + throw new OssException("xml format exception"); + } + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AppendResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AppendResult.php new file mode 100644 index 0000000..433c03e --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/AppendResult.php @@ -0,0 +1,27 @@ +rawResponse->header; + if (isset($header["x-oss-next-append-position"])) { + return intval($header["x-oss-next-append-position"]); + } + throw new OssException("cannot get next-append-position"); + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/BodyResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/BodyResult.php new file mode 100644 index 0000000..44ba15e --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/BodyResult.php @@ -0,0 +1,19 @@ +rawResponse->body) ? "" : $this->rawResponse->body; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CallbackResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CallbackResult.php new file mode 100644 index 0000000..514e985 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CallbackResult.php @@ -0,0 +1,21 @@ +rawResponse->status; + if ((int)(intval($status) / 100) == 2 && (int)(intval($status)) !== 203) { + return true; + } + return false; + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CopyObjectResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CopyObjectResult.php new file mode 100644 index 0000000..498723e --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/CopyObjectResult.php @@ -0,0 +1,30 @@ +rawResponse->body; + $xml = simplexml_load_string($body); + $result = array(); + + if (isset($xml->LastModified)) { + $result[] = $xml->LastModified; + } + if (isset($xml->ETag)) { + $result[] = $xml->ETag; + } + + return $result; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/DeleteObjectsResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/DeleteObjectsResult.php new file mode 100644 index 0000000..dc373b8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/DeleteObjectsResult.php @@ -0,0 +1,27 @@ +rawResponse->body; + $xml = simplexml_load_string($body); + $objects = array(); + + if (isset($xml->Deleted)) { + foreach($xml->Deleted as $deleteKey) + $objects[] = $deleteKey->Key; + } + return $objects; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ExistResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ExistResult.php new file mode 100644 index 0000000..f7aa287 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ExistResult.php @@ -0,0 +1,35 @@ +rawResponse->status) === 200 ? true : false; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 判断是否存在的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCnameResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCnameResult.php new file mode 100644 index 0000000..eed01f9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCnameResult.php @@ -0,0 +1,19 @@ +rawResponse->body; + $config = new CnameConfig(); + $config->parseFromXml($content); + return $config; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCorsResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCorsResult.php new file mode 100644 index 0000000..a51afe2 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetCorsResult.php @@ -0,0 +1,35 @@ +rawResponse->body; + $config = new CorsConfig(); + $config->parseFromXml($content); + return $config; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 获取bucket相关配置的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLifecycleResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLifecycleResult.php new file mode 100644 index 0000000..6b440c3 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLifecycleResult.php @@ -0,0 +1,41 @@ +rawResponse->body; + $config = new LifecycleConfig(); + $config->parseFromXml($content); + return $config; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 获取bucket相关配置的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelHistoryResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelHistoryResult.php new file mode 100644 index 0000000..202a668 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelHistoryResult.php @@ -0,0 +1,19 @@ +rawResponse->body; + $channelList = new GetLiveChannelHistory(); + $channelList->parseFromXml($content); + return $channelList; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelInfoResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelInfoResult.php new file mode 100644 index 0000000..d5a9005 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelInfoResult.php @@ -0,0 +1,19 @@ +rawResponse->body; + $channelList = new GetLiveChannelInfo(); + $channelList->parseFromXml($content); + return $channelList; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelStatusResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelStatusResult.php new file mode 100644 index 0000000..6b8a60f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLiveChannelStatusResult.php @@ -0,0 +1,19 @@ +rawResponse->body; + $channelList = new GetLiveChannelStatus(); + $channelList->parseFromXml($content); + return $channelList; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLocationResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLocationResult.php new file mode 100644 index 0000000..71c4c96 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLocationResult.php @@ -0,0 +1,30 @@ +rawResponse->body; + if (empty($content)) { + throw new OssException("body is null"); + } + $xml = simplexml_load_string($content); + return $xml; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLoggingResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLoggingResult.php new file mode 100644 index 0000000..72fc3ae --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetLoggingResult.php @@ -0,0 +1,41 @@ +rawResponse->body; + $config = new LoggingConfig(); + $config->parseFromXml($content); + return $config; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 获取bucket相关配置的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetRefererResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetRefererResult.php new file mode 100644 index 0000000..aee50d3 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetRefererResult.php @@ -0,0 +1,41 @@ +rawResponse->body; + $config = new RefererConfig(); + $config->parseFromXml($content); + return $config; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 获取bucket相关配置的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetStorageCapacityResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetStorageCapacityResult.php new file mode 100644 index 0000000..84e4916 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetStorageCapacityResult.php @@ -0,0 +1,34 @@ +rawResponse->body; + if (empty($content)) { + throw new OssException("body is null"); + } + $xml = simplexml_load_string($content); + if (isset($xml->StorageCapacity)) { + return intval($xml->StorageCapacity); + } else { + throw new OssException("xml format exception"); + } + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetWebsiteResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetWebsiteResult.php new file mode 100644 index 0000000..3099172 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/GetWebsiteResult.php @@ -0,0 +1,40 @@ +rawResponse->body; + $config = new WebsiteConfig(); + $config->parseFromXml($content); + return $config; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK, 获取bucket相关配置的接口,404也认为是一种 + * 有效响应 + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2 || (int)(intval($status)) === 404) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/HeaderResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/HeaderResult.php new file mode 100644 index 0000000..c9aae56 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/HeaderResult.php @@ -0,0 +1,23 @@ +rawResponse->header) ? array() : $this->rawResponse->header; + } + +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/InitiateMultipartUploadResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/InitiateMultipartUploadResult.php new file mode 100644 index 0000000..af985f2 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/InitiateMultipartUploadResult.php @@ -0,0 +1,29 @@ +rawResponse->body; + $xml = simplexml_load_string($content); + if (isset($xml->UploadId)) { + return strval($xml->UploadId); + } + throw new OssException("cannot get UploadId"); + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListBucketsResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListBucketsResult.php new file mode 100644 index 0000000..a58fb2d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListBucketsResult.php @@ -0,0 +1,33 @@ +rawResponse->body; + $xml = new \SimpleXMLElement($content); + if (isset($xml->Buckets) && isset($xml->Buckets->Bucket)) { + foreach ($xml->Buckets->Bucket as $bucket) { + $bucketInfo = new BucketInfo(strval($bucket->Location), + strval($bucket->Name), + strval($bucket->CreationDate)); + $bucketList[] = $bucketInfo; + } + } + return new BucketListInfo($bucketList); + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListLiveChannelResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListLiveChannelResult.php new file mode 100644 index 0000000..1a6e2a4 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListLiveChannelResult.php @@ -0,0 +1,16 @@ +rawResponse->body; + $channelList = new LiveChannelListInfo(); + $channelList->parseFromXml($content); + return $channelList; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListMultipartUploadResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListMultipartUploadResult.php new file mode 100644 index 0000000..bcb20bf --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListMultipartUploadResult.php @@ -0,0 +1,55 @@ +rawResponse->body; + $xml = simplexml_load_string($content); + + $encodingType = isset($xml->EncodingType) ? strval($xml->EncodingType) : ""; + $bucket = isset($xml->Bucket) ? strval($xml->Bucket) : ""; + $keyMarker = isset($xml->KeyMarker) ? strval($xml->KeyMarker) : ""; + $keyMarker = OssUtil::decodeKey($keyMarker, $encodingType); + $uploadIdMarker = isset($xml->UploadIdMarker) ? strval($xml->UploadIdMarker) : ""; + $nextKeyMarker = isset($xml->NextKeyMarker) ? strval($xml->NextKeyMarker) : ""; + $nextKeyMarker = OssUtil::decodeKey($nextKeyMarker, $encodingType); + $nextUploadIdMarker = isset($xml->NextUploadIdMarker) ? strval($xml->NextUploadIdMarker) : ""; + $delimiter = isset($xml->Delimiter) ? strval($xml->Delimiter) : ""; + $delimiter = OssUtil::decodeKey($delimiter, $encodingType); + $prefix = isset($xml->Prefix) ? strval($xml->Prefix) : ""; + $prefix = OssUtil::decodeKey($prefix, $encodingType); + $maxUploads = isset($xml->MaxUploads) ? intval($xml->MaxUploads) : 0; + $isTruncated = isset($xml->IsTruncated) ? strval($xml->IsTruncated) : ""; + $listUpload = array(); + + if (isset($xml->Upload)) { + foreach ($xml->Upload as $upload) { + $key = isset($upload->Key) ? strval($upload->Key) : ""; + $key = OssUtil::decodeKey($key, $encodingType); + $uploadId = isset($upload->UploadId) ? strval($upload->UploadId) : ""; + $initiated = isset($upload->Initiated) ? strval($upload->Initiated) : ""; + $listUpload[] = new UploadInfo($key, $uploadId, $initiated); + } + } + return new ListMultipartUploadInfo($bucket, $keyMarker, $uploadIdMarker, + $nextKeyMarker, $nextUploadIdMarker, + $delimiter, $prefix, $maxUploads, $isTruncated, $listUpload); + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListObjectsResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListObjectsResult.php new file mode 100644 index 0000000..fcf493d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListObjectsResult.php @@ -0,0 +1,71 @@ +rawResponse->body); + $encodingType = isset($xml->EncodingType) ? strval($xml->EncodingType) : ""; + $objectList = $this->parseObjectList($xml, $encodingType); + $prefixList = $this->parsePrefixList($xml, $encodingType); + $bucketName = isset($xml->Name) ? strval($xml->Name) : ""; + $prefix = isset($xml->Prefix) ? strval($xml->Prefix) : ""; + $prefix = OssUtil::decodeKey($prefix, $encodingType); + $marker = isset($xml->Marker) ? strval($xml->Marker) : ""; + $marker = OssUtil::decodeKey($marker, $encodingType); + $maxKeys = isset($xml->MaxKeys) ? intval($xml->MaxKeys) : 0; + $delimiter = isset($xml->Delimiter) ? strval($xml->Delimiter) : ""; + $delimiter = OssUtil::decodeKey($delimiter, $encodingType); + $isTruncated = isset($xml->IsTruncated) ? strval($xml->IsTruncated) : ""; + $nextMarker = isset($xml->NextMarker) ? strval($xml->NextMarker) : ""; + $nextMarker = OssUtil::decodeKey($nextMarker, $encodingType); + return new ObjectListInfo($bucketName, $prefix, $marker, $nextMarker, $maxKeys, $delimiter, $isTruncated, $objectList, $prefixList); + } + + private function parseObjectList($xml, $encodingType) + { + $retList = array(); + if (isset($xml->Contents)) { + foreach ($xml->Contents as $content) { + $key = isset($content->Key) ? strval($content->Key) : ""; + $key = OssUtil::decodeKey($key, $encodingType); + $lastModified = isset($content->LastModified) ? strval($content->LastModified) : ""; + $eTag = isset($content->ETag) ? strval($content->ETag) : ""; + $type = isset($content->Type) ? strval($content->Type) : ""; + $size = isset($content->Size) ? intval($content->Size) : 0; + $storageClass = isset($content->StorageClass) ? strval($content->StorageClass) : ""; + $retList[] = new ObjectInfo($key, $lastModified, $eTag, $type, $size, $storageClass); + } + } + return $retList; + } + + private function parsePrefixList($xml, $encodingType) + { + $retList = array(); + if (isset($xml->CommonPrefixes)) { + foreach ($xml->CommonPrefixes as $commonPrefix) { + $prefix = isset($commonPrefix->Prefix) ? strval($commonPrefix->Prefix) : ""; + $prefix = OssUtil::decodeKey($prefix, $encodingType); + $retList[] = new PrefixInfo($prefix); + } + } + return $retList; + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListPartsResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListPartsResult.php new file mode 100644 index 0000000..fd8a1b8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/ListPartsResult.php @@ -0,0 +1,42 @@ +rawResponse->body; + $xml = simplexml_load_string($content); + $bucket = isset($xml->Bucket) ? strval($xml->Bucket) : ""; + $key = isset($xml->Key) ? strval($xml->Key) : ""; + $uploadId = isset($xml->UploadId) ? strval($xml->UploadId) : ""; + $nextPartNumberMarker = isset($xml->NextPartNumberMarker) ? intval($xml->NextPartNumberMarker) : ""; + $maxParts = isset($xml->MaxParts) ? intval($xml->MaxParts) : ""; + $isTruncated = isset($xml->IsTruncated) ? strval($xml->IsTruncated) : ""; + $partList = array(); + if (isset($xml->Part)) { + foreach ($xml->Part as $part) { + $partNumber = isset($part->PartNumber) ? intval($part->PartNumber) : ""; + $lastModified = isset($part->LastModified) ? strval($part->LastModified) : ""; + $eTag = isset($part->ETag) ? strval($part->ETag) : ""; + $size = isset($part->Size) ? intval($part->Size) : ""; + $partList[] = new PartInfo($partNumber, $lastModified, $eTag, $size); + } + } + return new ListPartsInfo($bucket, $key, $uploadId, $nextPartNumberMarker, $maxParts, $isTruncated, $partList); + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutLiveChannelResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutLiveChannelResult.php new file mode 100644 index 0000000..dcac86b --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutLiveChannelResult.php @@ -0,0 +1,16 @@ +rawResponse->body; + $channel = new LiveChannelInfo(); + $channel->parseFromXml($content); + return $channel; + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutSetDeleteResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutSetDeleteResult.php new file mode 100644 index 0000000..97af003 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/PutSetDeleteResult.php @@ -0,0 +1,20 @@ + $this->rawResponse->body); + return array_merge($this->rawResponse->header, $body); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/Result.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/Result.php new file mode 100644 index 0000000..491256f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/Result.php @@ -0,0 +1,175 @@ +rawResponse = $response; + $this->parseResponse(); + } + + /** + * 获取requestId + * + * @return string + */ + public function getRequestId() + { + if (isset($this->rawResponse) && + isset($this->rawResponse->header) && + isset($this->rawResponse->header['x-oss-request-id']) + ) { + return $this->rawResponse->header['x-oss-request-id']; + } else { + return ''; + } + } + + /** + * 得到返回数据,不同的请求返回数据格式不同 + * + * $return mixed + */ + public function getData() + { + return $this->parsedData; + } + + /** + * 由子类实现,不同的请求返回数据有不同的解析逻辑,由子类实现 + * + * @return mixed + */ + abstract protected function parseDataFromResponse(); + + /** + * 操作是否成功 + * + * @return mixed + */ + public function isOK() + { + return $this->isOk; + } + + /** + * @throws OssException + */ + public function parseResponse() + { + $this->isOk = $this->isResponseOk(); + if ($this->isOk) { + $this->parsedData = $this->parseDataFromResponse(); + } else { + $httpStatus = strval($this->rawResponse->status); + $requestId = strval($this->getRequestId()); + $code = $this->retrieveErrorCode($this->rawResponse->body); + $message = $this->retrieveErrorMessage($this->rawResponse->body); + $body = $this->rawResponse->body; + + $details = array( + 'status' => $httpStatus, + 'request-id' => $requestId, + 'code' => $code, + 'message' => $message, + 'body' => $body + ); + throw new OssException($details); + } + } + + /** + * 尝试从body中获取错误Message + * + * @param $body + * @return string + */ + private function retrieveErrorMessage($body) + { + if (empty($body) || false === strpos($body, 'Message)) { + return strval($xml->Message); + } + return ''; + } + + /** + * 尝试从body中获取错误Code + * + * @param $body + * @return string + */ + private function retrieveErrorCode($body) + { + if (empty($body) || false === strpos($body, 'Code)) { + return strval($xml->Code); + } + return ''; + } + + /** + * 根据返回http状态码判断,[200-299]即认为是OK + * + * @return bool + */ + protected function isResponseOk() + { + $status = $this->rawResponse->status; + if ((int)(intval($status) / 100) == 2) { + return true; + } + return false; + } + + /** + * 返回原始的返回数据 + * + * @return ResponseCore + */ + public function getRawResponse() + { + return $this->rawResponse; + } + + /** + * 标示请求是否成功 + */ + protected $isOk = false; + /** + * 由子类解析过的数据 + */ + protected $parsedData = null; + /** + * 存放auth函数返回的原始Response + * + * @var ResponseCore + */ + protected $rawResponse; +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/SymlinkResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/SymlinkResult.php new file mode 100644 index 0000000..9c6d861 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/SymlinkResult.php @@ -0,0 +1,24 @@ +rawResponse->header[OssClient::OSS_SYMLINK_TARGET] = rawurldecode($this->rawResponse->header[OssClient::OSS_SYMLINK_TARGET]); + return $this->rawResponse->header; + } +} + diff --git a/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/UploadPartResult.php b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/UploadPartResult.php new file mode 100644 index 0000000..c6b66d4 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/src/OSS/Result/UploadPartResult.php @@ -0,0 +1,28 @@ +rawResponse->header; + if (isset($header["etag"])) { + return $header["etag"]; + } + throw new OssException("cannot get ETag"); + + } +} \ No newline at end of file diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/AclResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/AclResultTest.php new file mode 100644 index 0000000..12f4b1a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/AclResultTest.php @@ -0,0 +1,59 @@ + + + + 00220120222 + user_example + + + public-read + + +BBBB; + + private $invalidXml = << + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new AclResult($response); + $this->assertEquals("public-read", $result->getData()); + } + + public function testParseNullXml() + { + $response = new ResponseCore(array(), "", 200); + try { + new AclResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('body is null', $e->getMessage()); + } + } + + public function testParseInvalidXml() + { + $response = new ResponseCore(array(), $this->invalidXml, 200); + try { + new AclResult($response); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals("xml format exception", $e->getMessage()); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BodyResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BodyResultTest.php new file mode 100644 index 0000000..af13d4d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BodyResultTest.php @@ -0,0 +1,26 @@ +assertTrue($result->isOK()); + $this->assertEquals($result->getData(), "hi"); + } + + public function testParseInvalid404() + { + $response = new ResponseCore(array(), null, 200); + $result = new BodyResult($response); + $this->assertTrue($result->isOK()); + $this->assertEquals($result->getData(), ""); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketCnameTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketCnameTest.php new file mode 100644 index 0000000..87c9e54 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketCnameTest.php @@ -0,0 +1,77 @@ +client = Common::getOssClient(); + $this->bucketName = 'php-sdk-test-bucket-' . strval(rand(0, 10000)); + $this->client->createBucket($this->bucketName); + } + + public function tearDown() + { + $this->client->deleteBucket($this->bucketName); + } + + public function testBucketWithoutCname() + { + $cnameConfig = $this->client->getBucketCname($this->bucketName); + $this->assertEquals(0, count($cnameConfig->getCnames())); + } + + public function testAddCname() + { + $this->client->addBucketCname($this->bucketName, 'www.baidu.com'); + $this->client->addBucketCname($this->bucketName, 'www.qq.com'); + + $ret = $this->client->getBucketCname($this->bucketName); + $this->assertEquals(2, count($ret->getCnames())); + + // add another 2 cnames + $this->client->addBucketCname($this->bucketName, 'www.sina.com.cn'); + $this->client->addBucketCname($this->bucketName, 'www.iqiyi.com'); + + $ret = $this->client->getBucketCname($this->bucketName); + $cnames = $ret->getCnames(); + $cnameList = array(); + + foreach ($cnames as $c) { + $cnameList[] = $c['Domain']; + } + $should = array( + 'www.baidu.com', + 'www.qq.com', + 'www.sina.com.cn', + 'www.iqiyi.com' + ); + $this->assertEquals(4, count($cnames)); + $this->assertEquals(sort($should), sort($cnameList)); + } + + public function testDeleteCname() + { + $this->client->addBucketCname($this->bucketName, 'www.baidu.com'); + $this->client->addBucketCname($this->bucketName, 'www.qq.com'); + + $ret = $this->client->getBucketCname($this->bucketName); + $this->assertEquals(2, count($ret->getCnames())); + + // delete one cname + $this->client->deleteBucketCname($this->bucketName, 'www.baidu.com'); + + $ret = $this->client->getBucketCname($this->bucketName); + $this->assertEquals(1, count($ret->getCnames())); + $cnames = $ret->getCnames(); + $this->assertEquals('www.qq.com', $cnames[0]['Domain']); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketInfoTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketInfoTest.php new file mode 100644 index 0000000..80fa25c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketInfoTest.php @@ -0,0 +1,21 @@ +assertNotNull($bucketInfo); + $this->assertEquals('cn-beijing', $bucketInfo->getLocation()); + $this->assertEquals('name', $bucketInfo->getName()); + $this->assertEquals('today', $bucketInfo->getCreateDate()); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketLiveChannelTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketLiveChannelTest.php new file mode 100644 index 0000000..bed68b0 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/BucketLiveChannelTest.php @@ -0,0 +1,283 @@ +client = Common::getOssClient(); + $this->bucketName = 'php-sdk-test-rtmp-bucket-name-' . strval(rand(0, 10000)); + $this->client->createBucket($this->bucketName); + Common::waitMetaSync(); + } + + public function tearDown() + { + ////to delete created bucket + //1. delele live channel + $list = $this->client->listBucketLiveChannels($this->bucketName); + if (count($list->getChannelList()) != 0) + { + foreach($list->getChannelList() as $list) + { + $this->client->deleteBucketLiveChannel($this->bucketName, $list->getName()); + } + } + //2. delete exsited object + $prefix = 'live-test/'; + $delimiter = '/'; + $nextMarker = ''; + $maxkeys = 1000; + $options = array( + 'delimiter' => $delimiter, + 'prefix' => $prefix, + 'max-keys' => $maxkeys, + 'marker' => $nextMarker, + ); + + try { + $listObjectInfo = $this->client->listObjects($this->bucketName, $options); + } catch (OssException $e) { + printf($e->getMessage() . "\n"); + return; + } + + $objectList = $listObjectInfo->getObjectList(); // 文件列表 + if (!empty($objectList)) + { + foreach($objectList as $objectInfo) + $this->client->deleteObject($this->bucketName, $objectInfo->getKey()); + } + //3. delete the bucket + $this->client->deleteBucket($this->bucketName); + } + + public function testPutLiveChannel() + { + $config = new LiveChannelConfig(array( + 'description' => 'live channel 1', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $info = $this->client->putBucketLiveChannel($this->bucketName, 'live-1', $config); + $this->client->deleteBucketLiveChannel($this->bucketName, 'live-1'); + + $this->assertEquals('live-1', $info->getName()); + $this->assertEquals('live channel 1', $info->getDescription()); + $this->assertEquals(1, count($info->getPublishUrls())); + $this->assertEquals(1, count($info->getPlayUrls())); + } + + public function testPutLiveChannelWithDefaultParams() + { + $config = new LiveChannelConfig(array( + 'description' => 'live channel 1', + 'type' => 'HLS', + )); + $info = $this->client->putBucketLiveChannel($this->bucketName, 'live-1', $config); + $this->client->deleteBucketLiveChannel($this->bucketName, 'live-1'); + + $this->assertEquals('live-1', $info->getName()); + $this->assertEquals('live channel 1', $info->getDescription()); + $this->assertEquals(1, count($info->getPublishUrls())); + $this->assertEquals(1, count($info->getPlayUrls())); + } + + public function testListLiveChannels() + { + $config = new LiveChannelConfig(array( + 'description' => 'live channel 1', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, 'live-1', $config); + + $config = new LiveChannelConfig(array( + 'description' => 'live channel 2', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, 'live-2', $config); + + $list = $this->client->listBucketLiveChannels($this->bucketName); + + $this->assertEquals($this->bucketName, $list->getBucketName()); + $this->assertEquals(false, $list->getIsTruncated()); + $channels = $list->getChannelList(); + $this->assertEquals(2, count($channels)); + + $chan1 = $channels[0]; + $this->assertEquals('live-1', $chan1->getName()); + $this->assertEquals('live channel 1', $chan1->getDescription()); + $this->assertEquals(1, count($chan1->getPublishUrls())); + $this->assertEquals(1, count($chan1->getPlayUrls())); + + $chan2 = $channels[1]; + $this->assertEquals('live-2', $chan2->getName()); + $this->assertEquals('live channel 2', $chan2->getDescription()); + $this->assertEquals(1, count($chan2->getPublishUrls())); + $this->assertEquals(1, count($chan2->getPlayUrls())); + + $list = $this->client->listBucketLiveChannels($this->bucketName, array( + 'prefix' => 'live-', + 'marker' => 'live-1', + 'max-keys' => 10 + )); + $channels = $list->getChannelList(); + $this->assertEquals(1, count($channels)); + $chan2 = $channels[0]; + $this->assertEquals('live-2', $chan2->getName()); + $this->assertEquals('live channel 2', $chan2->getDescription()); + $this->assertEquals(1, count($chan2->getPublishUrls())); + $this->assertEquals(1, count($chan2->getPlayUrls())); + + $this->client->deleteBucketLiveChannel($this->bucketName, 'live-1'); + $this->client->deleteBucketLiveChannel($this->bucketName, 'live-2'); + $list = $this->client->listBucketLiveChannels($this->bucketName, array( + 'prefix' => 'live-' + )); + $this->assertEquals(0, count($list->getChannelList())); + } + + public function testDeleteLiveChannel() + { + $channelName = 'live-to-delete'; + $config = new LiveChannelConfig(array( + 'description' => 'live channel to delete', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, $channelName, $config); + + $this->client->deleteBucketLiveChannel($this->bucketName, $channelName); + $list = $this->client->listBucketLiveChannels($this->bucketName, array( + 'prefix' => $channelName + )); + + $this->assertEquals(0, count($list->getChannelList())); + } + + public function testSignRtmpUrl() + { + $channelName = '90475'; + $bucket = 'douyu'; + $now = time(); + $url = $this->client->signRtmpUrl($bucket, $channelName, 900, array( + 'params' => array( + 'playlistName' => 'playlist.m3u8' + ) + )); + + $ret = parse_url($url); + $this->assertEquals('rtmp', $ret['scheme']); + parse_str($ret['query'], $query); + + $this->assertTrue(isset($query['OSSAccessKeyId'])); + $this->assertTrue(isset($query['Signature'])); + $this->assertTrue(intval($query['Expires']) - ($now + 900) < 3); + $this->assertEquals('playlist.m3u8', $query['playlistName']); + } + + public function testLiveChannelInfo() + { + $channelName = 'live-to-put-status'; + $config = new LiveChannelConfig(array( + 'description' => 'test live channel info', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, $channelName, $config); + + $info = $this->client->getLiveChannelInfo($this->bucketName, $channelName); + $this->assertEquals('test live channel info', $info->getDescription()); + $this->assertEquals('enabled', $info->getStatus()); + $this->assertEquals('HLS', $info->getType()); + $this->assertEquals(10, $info->getFragDuration()); + $this->assertEquals(5, $info->getFragCount()); + $this->assertEquals('playlist.m3u8', $info->getPlayListName()); + + $this->client->deleteBucketLiveChannel($this->bucketName, $channelName); + $list = $this->client->listBucketLiveChannels($this->bucketName, array( + 'prefix' => $channelName + )); + $this->assertEquals(0, count($list->getChannelList())); + } + + public function testPutLiveChannelStatus() + { + $channelName = 'live-to-put-status'; + $config = new LiveChannelConfig(array( + 'description' => 'test live channel info', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, $channelName, $config); + + $info = $this->client->getLiveChannelInfo($this->bucketName, $channelName); + $this->assertEquals('test live channel info', $info->getDescription()); + $this->assertEquals('enabled', $info->getStatus()); + $this->assertEquals('HLS', $info->getType()); + $this->assertEquals(10, $info->getFragDuration()); + $this->assertEquals(5, $info->getFragCount()); + $this->assertEquals('playlist.m3u8', $info->getPlayListName()); + $status = $this->client->getLiveChannelStatus($this->bucketName, $channelName); + $this->assertEquals('Idle', $status->getStatus()); + + + $resp = $this->client->putLiveChannelStatus($this->bucketName, $channelName, "disabled"); + $info = $this->client->getLiveChannelInfo($this->bucketName, $channelName); + $this->assertEquals('test live channel info', $info->getDescription()); + $this->assertEquals('disabled', $info->getStatus()); + $this->assertEquals('HLS', $info->getType()); + $this->assertEquals(10, $info->getFragDuration()); + $this->assertEquals(5, $info->getFragCount()); + $this->assertEquals('playlist.m3u8', $info->getPlayListName()); + + $status = $this->client->getLiveChannelStatus($this->bucketName, $channelName); + //getLiveChannelInfo + $this->assertEquals('Disabled', $status->getStatus()); + + $this->client->deleteBucketLiveChannel($this->bucketName, $channelName); + $list = $this->client->listBucketLiveChannels($this->bucketName, array( + 'prefix' => $channelName + )); + $this->assertEquals(0, count($list->getChannelList())); + + } + public function testLiveChannelHistory() + { + $channelName = 'live-test-history'; + $config = new LiveChannelConfig(array( + 'description' => 'test live channel info', + 'type' => 'HLS', + 'fragDuration' => 10, + 'fragCount' => 5, + 'playListName' => 'hello.m3u8' + )); + $this->client->putBucketLiveChannel($this->bucketName, $channelName, $config); + + $history = $this->client->getLiveChannelHistory($this->bucketName, $channelName); + $this->assertEquals(0, count($history->getLiveRecordList())); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CallbackTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CallbackTest.php new file mode 100644 index 0000000..a0db003 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CallbackTest.php @@ -0,0 +1,297 @@ +ossClient->putObject($this->bucket, $copiedObject, file_get_contents(__FILE__)); + + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $upload_id = $this->ossClient->initiateMultipartUpload($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + /* + * step 2. uploadPartCopy + */ + $copyId = 1; + $eTag = $this->ossClient->uploadPartCopy($this->bucket, $copiedObject, $this->bucket, $object, $copyId, $upload_id); + $upload_parts[] = array( + 'PartNumber' => $copyId, + 'ETag' => $eTag, + ); + + try { + $listPartsInfo = $this->ossClient->listParts($this->bucket, $object, $upload_id); + $this->assertNotNull($listPartsInfo); + } catch (OssException $e) { + $this->assertTrue(false); + } + + /** + * step 3. + */ + + $json = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size},\"x:var1\":${x:var1},\"x:var2\":${x:var2}}", + "callbackBodyType":"application/json" + }'; + + $var = + '{ + "x:var1":"value1", + "x:var2":"值2" + }'; + $options = array(OssClient::OSS_CALLBACK => $json, + OssClient::OSS_CALLBACK_VAR => $var + ); + + try { + $result = $this->ossClient->completeMultipartUpload($this->bucket, $object, $upload_id, $upload_parts, $options); + $this->assertEquals("200", $result['info']['http_code']); + $this->assertEquals("{\"Status\":\"OK\"}", $result['body']); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testMultipartUploadCallbackFailed() + { + $object = "multipart-callback-test.txt"; + $copiedObject = "multipart-callback-test.txt.copied"; + $this->ossClient->putObject($this->bucket, $copiedObject, file_get_contents(__FILE__)); + + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $upload_id = $this->ossClient->initiateMultipartUpload($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + /* + * step 2. uploadPartCopy + */ + $copyId = 1; + $eTag = $this->ossClient->uploadPartCopy($this->bucket, $copiedObject, $this->bucket, $object, $copyId, $upload_id); + $upload_parts[] = array( + 'PartNumber' => $copyId, + 'ETag' => $eTag, + ); + + try { + $listPartsInfo = $this->ossClient->listParts($this->bucket, $object, $upload_id); + $this->assertNotNull($listPartsInfo); + } catch (OssException $e) { + $this->assertTrue(false); + } + + /** + * step 3. + */ + + $json = + '{ + "callbackUrl":"www.baidu.com", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size},\"x:var1\":${x:var1},\"x:var2\":${x:var2}}", + "callbackBodyType":"application/json" + }'; + + $var = + '{ + "x:var1":"value1", + "x:var2":"值2" + }'; + $options = array(OssClient::OSS_CALLBACK => $json, + OssClient::OSS_CALLBACK_VAR => $var + ); + + try { + $result = $this->ossClient->completeMultipartUpload($this->bucket, $object, $upload_id, $upload_parts, $options); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertTrue(true); + $this->assertEquals("203", $e->getHTTPStatus()); + } + + } + + public function testPutObjectCallbackNormal() + { + //json + { + $json = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", + "callbackBodyType":"application/json" + }'; + $options = array(OssClient::OSS_CALLBACK => $json); + $this->putObjectCallbackOk($options, "200"); + } + //url + { + $url = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}", + "callbackBodyType":"application/x-www-form-urlencoded" + }'; + $options = array(OssClient::OSS_CALLBACK => $url); + $this->putObjectCallbackOk($options, "200"); + } + // Unspecified typre + { + $url = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}" + }'; + $options = array(OssClient::OSS_CALLBACK => $url); + $this->putObjectCallbackOk($options, "200"); + } + //json and body is chinese + { + $json = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\" 春水碧于天,画船听雨眠。\":\"垆边人似月,皓腕凝霜雪。\"}", + "callbackBodyType":"application/json" + }'; + $options = array(OssClient::OSS_CALLBACK => $json); + $this->putObjectCallbackOk($options, "200"); + } + //url and body is chinese + { + $url = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"春水碧于天,画船听雨眠。垆边人似月,皓腕凝霜雪", + "callbackBodyType":"application/x-www-form-urlencoded" + }'; + $options = array(OssClient::OSS_CALLBACK => $url); + $this->putObjectCallbackOk($options, "200"); + } + //json and add callback_var + { + $json = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size},\"x:var1\":${x:var1},\"x:var2\":${x:var2}}", + "callbackBodyType":"application/json" + }'; + + $var = + '{ + "x:var1":"value1", + "x:var2":"aliyun.com" + }'; + $options = array(OssClient::OSS_CALLBACK => $json, + OssClient::OSS_CALLBACK_VAR => $var + ); + $this->putObjectCallbackOk($options, "200"); + } + //url and add callback_var + { + $url = + '{ + "callbackUrl":"oss-demo.aliyuncs.com:23450", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}&my_var1=${x:var1}&my_var2=${x:var2}", + "callbackBodyType":"application/x-www-form-urlencoded" + }'; + $var = + '{ + "x:var1":"value1凌波不过横塘路,但目送,芳", + "x:var2":"值2" + }'; + $options = array(OssClient::OSS_CALLBACK => $url, + OssClient::OSS_CALLBACK_VAR => $var + ); + $this->putObjectCallbackOk($options, "200"); + } + + } + + public function testPutCallbackWithCallbackFailed() + { + { + $json = + '{ + "callbackUrl":"http://www.baidu.com", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", + "callbackBodyType":"application/json" + }'; + $options = array(OssClient::OSS_CALLBACK => $json); + $this->putObjectCallbackFailed($options, "203"); + } + + { + $url = + '{ + "callbackUrl":"http://www.baidu.com", + "callbackHost":"oss-cn-hangzhou.aliyuncs.com", + "callbackBody":"bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&imageInfo.height=${imageInfo.height}&imageInfo.width=${imageInfo.width}&imageInfo.format=${imageInfo.format}&my_var1=${x:var1}&my_var2=${x:var2}", + "callbackBodyType":"application/x-www-form-urlencoded" + }'; + $options = array(OssClient::OSS_CALLBACK => $url); + $this->putObjectCallbackFailed($options, "203"); + } + + } + + private function putObjectCallbackOk($options, $status) + { + $object = "oss-php-sdk-callback-test.txt"; + $content = file_get_contents(__FILE__); + try { + $result = $this->ossClient->putObject($this->bucket, $object, $content, $options); + $this->assertEquals($status, $result['info']['http_code']); + $this->assertEquals("{\"Status\":\"OK\"}", $result['body']); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + private function putObjectCallbackFailed($options, $status) + { + $object = "oss-php-sdk-callback-test.txt"; + $content = file_get_contents(__FILE__); + try { + $result = $this->ossClient->putObject($this->bucket, $object, $content, $options); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals($status, $e->getHTTPStatus()); + $this->assertTrue(true); + } + } + + public function setUp() + { + parent::setUp(); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CnameConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CnameConfigTest.php new file mode 100644 index 0000000..e3c1ce9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CnameConfigTest.php @@ -0,0 +1,77 @@ + + + + www.foo.com + enabled + 20150101 + + + bar.com + disabled + 20160101 + + +BBBB; + + public function testFromXml() + { + $cnameConfig = new CnameConfig(); + $cnameConfig->parseFromXml($this->xml1); + + $cnames = $cnameConfig->getCnames(); + $this->assertEquals(2, count($cnames)); + $this->assertEquals('www.foo.com', $cnames[0]['Domain']); + $this->assertEquals('enabled', $cnames[0]['Status']); + $this->assertEquals('20150101', $cnames[0]['LastModified']); + + $this->assertEquals('bar.com', $cnames[1]['Domain']); + $this->assertEquals('disabled', $cnames[1]['Status']); + $this->assertEquals('20160101', $cnames[1]['LastModified']); + } + + public function testToXml() + { + $cnameConfig = new CnameConfig(); + $cnameConfig->addCname('www.foo.com'); + $cnameConfig->addCname('bar.com'); + + $xml = $cnameConfig->serializeToXml(); + $comp = new CnameConfig(); + $comp->parseFromXml($xml); + + $cnames1 = $cnameConfig->getCnames(); + $cnames2 = $comp->getCnames(); + + $this->assertEquals(count($cnames1), count($cnames2)); + $this->assertEquals(count($cnames1[0]), count($cnames2[0])); + $this->assertEquals(1, count($cnames1[0])); + $this->assertEquals($cnames1[0]['Domain'], $cnames2[0]['Domain']); + } + + public function testCnameNumberLimit() + { + $cnameConfig = new CnameConfig(); + for ($i = 0; $i < CnameConfig::OSS_MAX_RULES; $i += 1) { + $cnameConfig->addCname(strval($i) . '.foo.com'); + } + try { + $cnameConfig->addCname('www.foo.com'); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals( + $e->getMessage(), + "num of cname in the config exceeds self::OSS_MAX_RULES: " . strval(CnameConfig::OSS_MAX_RULES)); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/Common.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/Common.php new file mode 100644 index 0000000..9d7190c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/Common.php @@ -0,0 +1,70 @@ +getMessage() . "\n"); + return null; + } + return $ossClient; + } + + public static function getBucketName() + { + return getenv('OSS_BUCKET'); + } + + /** + * 工具方法,创建一个bucket + */ + public static function createBucket() + { + $ossClient = self::getOssClient(); + if (is_null($ossClient)) exit(1); + $bucket = self::getBucketName(); + $acl = OssClient::OSS_ACL_TYPE_PUBLIC_READ; + try { + $ossClient->createBucket($bucket, $acl); + } catch (OssException $e) { + printf(__FUNCTION__ . ": FAILED\n"); + printf($e->getMessage() . "\n"); + return; + } + print(__FUNCTION__ . ": OK" . "\n"); + } + + /** + * Wait for bucket meta sync + */ + public static function waitMetaSync() + { + if (getenv('TRAVIS')) { + sleep(10); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ContentTypeTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ContentTypeTest.php new file mode 100644 index 0000000..606c810 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ContentTypeTest.php @@ -0,0 +1,133 @@ +/dev/null', $output, $status); + + $this->assertEquals(0, $status); + } + + private function getContentType($bucket, $object) + { + $client = Common::getOssClient(); + $headers = $client->getObjectMeta($bucket, $object); + return $headers['content-type']; + } + + public function testByFileName() + { + $client = Common::getOssClient(); + $bucket = Common::getBucketName(); + + $file = '/tmp/x.html'; + $object = 'test/x'; + $this->runCmd('touch ' . $file); + + $client->uploadFile($bucket, $object, $file); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('text/html', $type); + + $file = '/tmp/x.json'; + $object = 'test/y'; + $this->runCmd('dd if=/dev/urandom of=' . $file . ' bs=1024 count=100'); + + $client->multiuploadFile($bucket, $object, $file, array('partSize' => 100)); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('application/json', $type); + } + + public function testByObjectKey() + { + $client = Common::getOssClient(); + $bucket = Common::getBucketName(); + + $object = "test/x.txt"; + $client->putObject($bucket, $object, "hello world"); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('text/plain', $type); + + $file = '/tmp/x.html'; + $object = 'test/x.txt'; + $this->runCmd('touch ' . $file); + + $client->uploadFile($bucket, $object, $file); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('text/html', $type); + + $file = '/tmp/x.none'; + $object = 'test/x.txt'; + $this->runCmd('touch ' . $file); + + $client->uploadFile($bucket, $object, $file); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('text/plain', $type); + + $file = '/tmp/x.mp3'; + $object = 'test/y.json'; + $this->runCmd('dd if=/dev/urandom of=' . $file . ' bs=1024 count=100'); + + $client->multiuploadFile($bucket, $object, $file, array('partSize' => 100)); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('audio/mpeg', $type); + + $file = '/tmp/x.none'; + $object = 'test/y.json'; + $this->runCmd('dd if=/dev/urandom of=' . $file . ' bs=1024 count=100'); + + $client->multiuploadFile($bucket, $object, $file, array('partSize' => 100)); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('application/json', $type); + } + + public function testByUser() + { + $client = Common::getOssClient(); + $bucket = Common::getBucketName(); + + $object = "test/x.txt"; + $client->putObject($bucket, $object, "hello world", array( + 'Content-Type' => 'text/html' + )); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('text/html', $type); + + $file = '/tmp/x.html'; + $object = 'test/x'; + $this->runCmd('touch ' . $file); + + $client->uploadFile($bucket, $object, $file, array( + 'Content-Type' => 'application/json' + )); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('application/json', $type); + + $file = '/tmp/x.json'; + $object = 'test/y'; + $this->runCmd('dd if=/dev/urandom of=' . $file . ' bs=1024 count=100'); + + $client->multiuploadFile($bucket, $object, $file, array( + 'partSize' => 100, + 'Content-Type' => 'audio/mpeg' + )); + $type = $this->getContentType($bucket, $object); + + $this->assertEquals('audio/mpeg', $type); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CopyObjectResult.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CopyObjectResult.php new file mode 100644 index 0000000..171d4c8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CopyObjectResult.php @@ -0,0 +1,52 @@ + + + Fri, 24 Feb 2012 07:18:48 GMT + "5B3C1A2E053D763E1B002CC607C5A0FE" + +BBBB; + + public function testNullResponse() + { + $response = null; + try { + new CopyObjectResult($response); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('raw response is null', $e->getMessage()); + } + } + + public function testOkResponse() + { + $header= array(); + $response = new ResponseCore($header, $this->body, 200); + $result = new CopyObjectResult($response); + $data = $result->getData(); + $this->assertTrue($result->isOK()); + $this->assertEquals("Fri, 24 Feb 2012 07:18:48 GMT", $data[0]); + $this->assertEquals("\"5B3C1A2E053D763E1B002CC607C5A0FE\"", $data[1]); + } + + public function testFailResponse() + { + $response = new ResponseCore(array(), "", 404); + try { + new CopyObjectResult($response); + $this->assertFalse(true); + } catch (OssException $e) { + + } + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CorsConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CorsConfigTest.php new file mode 100644 index 0000000..ddc4d3a --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/CorsConfigTest.php @@ -0,0 +1,140 @@ + + + +http://www.b.com +http://www.a.com +http://www.a.com +GET +PUT +POST +x-oss-test +x-oss-test2 +x-oss-test2 +x-oss-test3 +x-oss-test1 +x-oss-test1 +x-oss-test2 +10 + + +http://www.b.com +GET +x-oss-test +x-oss-test1 +110 + + +BBBB; + + private $validXml2 = << + + +http://www.b.com +http://www.a.com +http://www.a.com +GET +PUT +POST +x-oss-test +x-oss-test2 +x-oss-test2 +x-oss-test3 +x-oss-test1 +x-oss-test1 +x-oss-test2 +10 + + +BBBB; + + public function testParseValidXml() + { + $corsConfig = new CorsConfig(); + $corsConfig->parseFromXml($this->validXml); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($corsConfig->serializeToXml())); + $this->assertNotNull($corsConfig->getRules()); + $rules = $corsConfig->getRules(); + $this->assertNotNull($rules[0]->getAllowedHeaders()); + $this->assertNotNull($rules[0]->getAllowedMethods()); + $this->assertNotNull($rules[0]->getAllowedOrigins()); + $this->assertNotNull($rules[0]->getExposeHeaders()); + $this->assertNotNull($rules[0]->getMaxAgeSeconds()); + } + + public function testParseValidXml2() + { + $corsConfig = new CorsConfig(); + $corsConfig->parseFromXml($this->validXml2); + $this->assertEquals($this->cleanXml($this->validXml2), $this->cleanXml($corsConfig->serializeToXml())); + } + + public function testCreateCorsConfigFromMoreThan10Rules() + { + $corsConfig = new CorsConfig(); + $rule = new CorsRule(); + for ($i = 0; $i < CorsConfig::OSS_MAX_RULES; $i += 1) { + $corsConfig->addRule($rule); + } + try { + $corsConfig->addRule($rule); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals($e->getMessage(), "num of rules in the config exceeds self::OSS_MAX_RULES: " . strval(CorsConfig::OSS_MAX_RULES)); + } + } + + public function testCreateCorsConfigParamAbsent() + { + $corsConfig = new CorsConfig(); + $rule = new CorsRule(); + $corsConfig->addRule($rule); + + try { + $xml = $corsConfig->serializeToXml(); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals($e->getMessage(), "maxAgeSeconds is not set in the Rule"); + } + } + + public function testCreateCorsConfigFromScratch() + { + $corsConfig = new CorsConfig(); + $rule = new CorsRule(); + $rule->addAllowedHeader("x-oss-test"); + $rule->addAllowedHeader("x-oss-test2"); + $rule->addAllowedHeader("x-oss-test2"); + $rule->addAllowedHeader("x-oss-test3"); + $rule->addAllowedOrigin("http://www.b.com"); + $rule->addAllowedOrigin("http://www.a.com"); + $rule->addAllowedOrigin("http://www.a.com"); + $rule->addAllowedMethod("GET"); + $rule->addAllowedMethod("PUT"); + $rule->addAllowedMethod("POST"); + $rule->addExposeHeader("x-oss-test1"); + $rule->addExposeHeader("x-oss-test1"); + $rule->addExposeHeader("x-oss-test2"); + $rule->setMaxAgeSeconds(10); + $corsConfig->addRule($rule); + $this->assertEquals($this->cleanXml($this->validXml2), $this->cleanXml($corsConfig->serializeToXml())); + $this->assertEquals($this->cleanXml($this->validXml2), $this->cleanXml(strval($corsConfig))); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ExistResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ExistResultTest.php new file mode 100644 index 0000000..e1b4e81 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ExistResultTest.php @@ -0,0 +1,38 @@ +assertTrue($result->isOK()); + $this->assertEquals($result->getData(), true); + } + + public function testParseInvalid404() + { + $response = new ResponseCore(array(), "", 404); + $result = new ExistResult($response); + $this->assertTrue($result->isOK()); + $this->assertEquals($result->getData(), false); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), "", 300); + try { + new ExistResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetCorsResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetCorsResultTest.php new file mode 100644 index 0000000..a3281c8 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetCorsResultTest.php @@ -0,0 +1,67 @@ + + + +http://www.b.com +http://www.a.com +http://www.a.com +GET +PUT +POST +x-oss-test +x-oss-test2 +x-oss-test2 +x-oss-test3 +x-oss-test1 +x-oss-test1 +x-oss-test2 +10 + + +http://www.b.com +GET +x-oss-test +x-oss-test1 +110 + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetCorsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $corsConfig = $result->getData(); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($corsConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), $this->validXml, 300); + try { + new GetCorsResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLifecycleResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLifecycleResultTest.php new file mode 100644 index 0000000..92ae208 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLifecycleResultTest.php @@ -0,0 +1,59 @@ + + + +delete obsoleted files +obsoleted/ +Enabled +3 + + +delete temporary files +temporary/ +Enabled +2022-10-12T00:00:00.000Z +2022-10-12T00:00:00.000Z + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetLifecycleResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $lifecycleConfig = $result->getData(); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($lifecycleConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), $this->validXml, 300); + try { + new GetLifecycleResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLoggingResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLoggingResultTest.php new file mode 100644 index 0000000..6195014 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetLoggingResultTest.php @@ -0,0 +1,51 @@ + + + +TargetBucket +TargetPrefix + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetLoggingResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $loggingConfig = $result->getData(); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($loggingConfig->serializeToXml())); + $this->assertEquals("TargetBucket", $loggingConfig->getTargetBucket()); + $this->assertEquals("TargetPrefix", $loggingConfig->getTargetPrefix()); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), $this->validXml, 300); + try { + new GetLoggingResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetRefererResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetRefererResultTest.php new file mode 100644 index 0000000..072aa43 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetRefererResultTest.php @@ -0,0 +1,51 @@ + + +true + +http://www.aliyun.com +https://www.aliyun.com +http://www.*.com +https://www.?.aliyuncs.com + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetRefererResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $refererConfig = $result->getData(); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($refererConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), $this->validXml, 300); + try { + new GetRefererResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetWebsiteResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetWebsiteResultTest.php new file mode 100644 index 0000000..70e1559 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/GetWebsiteResultTest.php @@ -0,0 +1,50 @@ + + + +index.html + + +errorDocument.html + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetWebsiteResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $websiteConfig = $result->getData(); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($websiteConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + + public function testInvalidResponse() + { + $response = new ResponseCore(array(), $this->validXml, 300); + try { + new GetWebsiteResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HeaderResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HeaderResultTest.php new file mode 100644 index 0000000..dae4975 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HeaderResultTest.php @@ -0,0 +1,23 @@ + 'value'), "", 200); + $result = new HeaderResult($response); + $this->assertTrue($result->isOK()); + $this->assertTrue(is_array($result->getData())); + $data = $result->getData(); + $this->assertEquals($data['key'], 'value'); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HttpTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HttpTest.php new file mode 100644 index 0000000..a59dfcd --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/HttpTest.php @@ -0,0 +1,77 @@ +assertFalse($res->isOK()); + $this->assertTrue($res->isOK(500)); + } + + public function testGet() + { + $httpCore = new RequestCore("http://www.baidu.com"); + $httpResponse = $httpCore->send_request(); + $this->assertNotNull($httpResponse); + } + + public function testSetProxyAndTimeout() + { + $httpCore = new RequestCore("http://www.baidu.com"); + $httpCore->set_proxy("1.0.2.1:8888"); + $httpCore->connect_timeout = 1; + try { + $httpResponse = $httpCore->send_request(); + $this->assertTrue(false); + } catch (RequestCore_Exception $e) { + + } + } + + public function testGetParseTrue() + { + $httpCore = new RequestCore("http://www.baidu.com"); + $httpCore->curlopts = array(CURLOPT_HEADER => true); + $url = $httpCore->send_request(true); + foreach ($httpCore->get_response_header() as $key => $value) { + $this->assertEquals($httpCore->get_response_header($key), $value); + } + $this->assertNotNull($url); + } + + public function testParseResponse() + { + $httpCore = new RequestCore("http://www.baidu.com"); + $response = $httpCore->send_request(); + $parsed = $httpCore->process_response(null, $response); + $this->assertNotNull($parsed); + } + + public function testExceptionGet() + { + $httpCore = null; + $exception = false; + try { + $httpCore = new RequestCore("http://www.notexistsitexx.com"); + $httpCore->set_body(""); + $httpCore->set_method("GET"); + $httpCore->connect_timeout = 10; + $httpCore->timeout = 10; + $res = $httpCore->send_request(); + } catch (RequestCore_Exception $e) { + $exception = true; + } + $this->assertTrue($exception); + } +} + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/InitiateMultipartUploadResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/InitiateMultipartUploadResultTest.php new file mode 100644 index 0000000..9f6c7a5 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/InitiateMultipartUploadResultTest.php @@ -0,0 +1,47 @@ + + + multipart_upload + multipart.data + 0004B9894A22E5B1888A1E29F8236E2D + +BBBB; + + private $invalidXml = << + + multipart_upload + multipart.data + +BBBB; + + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new InitiateMultipartUploadResult($response); + $this->assertEquals("0004B9894A22E5B1888A1E29F8236E2D", $result->getData()); + } + + public function testParseInvalidXml() + { + $response = new ResponseCore(array(), $this->invalidXml, 200); + try { + $result = new InitiateMultipartUploadResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LifecycleConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LifecycleConfigTest.php new file mode 100644 index 0000000..7bd0331 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LifecycleConfigTest.php @@ -0,0 +1,130 @@ + + + +delete obsoleted files +obsoleted/ +Enabled +3 + + +delete temporary files +temporary/ +Enabled +2022-10-12T00:00:00.000Z +2022-10-12T00:00:00.000Z + + +BBBB; + + private $validLifecycle2 = << + +delete temporary files +temporary/ +Enabled +2022-10-12T00:00:00.000Z +2022-10-12T00:00:00.000Z + + +BBBB; + + private $nullLifecycle = << + +BBBB; + + public function testConstructValidConfig() + { + $lifecycleConfig = new LifecycleConfig(); + $actions = array(); + $actions[] = new LifecycleAction("Expiration", "Days", 3); + $lifecycleRule = new LifecycleRule("delete obsoleted files", "obsoleted/", "Enabled", $actions); + $lifecycleConfig->addRule($lifecycleRule); + $actions = array(); + $actions[] = new LifecycleAction("Expiration", "Date", '2022-10-12T00:00:00.000Z'); + $actions[] = new LifecycleAction("Expiration2", "Date", '2022-10-12T00:00:00.000Z'); + $lifecycleRule = new LifecycleRule("delete temporary files", "temporary/", "Enabled", $actions); + $lifecycleConfig->addRule($lifecycleRule); + try { + $lifecycleConfig->addRule(null); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('lifecycleRule is null', $e->getMessage()); + } + $this->assertEquals($this->cleanXml(strval($lifecycleConfig)), $this->cleanXml($this->validLifecycle)); + } + + public function testParseValidXml() + { + $lifecycleConfig = new LifecycleConfig(); + $lifecycleConfig->parseFromXml($this->validLifecycle); + $this->assertEquals($this->cleanXml($lifecycleConfig->serializeToXml()), $this->cleanXml($this->validLifecycle)); + $this->assertEquals(2, count($lifecycleConfig->getRules())); + $rules = $lifecycleConfig->getRules(); + $this->assertEquals('delete temporary files', $rules[1]->getId()); + } + + public function testParseValidXml2() + { + $lifecycleConfig = new LifecycleConfig(); + $lifecycleConfig->parseFromXml($this->validLifecycle2); + $this->assertEquals($this->cleanXml($lifecycleConfig->serializeToXml()), $this->cleanXml($this->validLifecycle2)); + $this->assertEquals(1, count($lifecycleConfig->getRules())); + $rules = $lifecycleConfig->getRules(); + $this->assertEquals('delete temporary files', $rules[0]->getId()); + } + + public function testParseNullXml() + { + $lifecycleConfig = new LifecycleConfig(); + $lifecycleConfig->parseFromXml($this->nullLifecycle); + $this->assertEquals($this->cleanXml($lifecycleConfig->serializeToXml()), $this->cleanXml($this->nullLifecycle)); + $this->assertEquals(0, count($lifecycleConfig->getRules())); + } + + public function testLifecycleRule() + { + $lifecycleRule = new LifecycleRule("x", "x", "x", array('x')); + $lifecycleRule->setId("id"); + $lifecycleRule->setPrefix("prefix"); + $lifecycleRule->setStatus("Enabled"); + $lifecycleRule->setActions(array()); + + $this->assertEquals('id', $lifecycleRule->getId()); + $this->assertEquals('prefix', $lifecycleRule->getPrefix()); + $this->assertEquals('Enabled', $lifecycleRule->getStatus()); + $this->assertEmpty($lifecycleRule->getActions()); + } + + public function testLifecycleAction() + { + $action = new LifecycleAction('x', 'x', 'x'); + $this->assertEquals($action->getAction(), 'x'); + $this->assertEquals($action->getTimeSpec(), 'x'); + $this->assertEquals($action->getTimeValue(), 'x'); + $action->setAction('y'); + $action->setTimeSpec('y'); + $action->setTimeValue('y'); + $this->assertEquals($action->getAction(), 'y'); + $this->assertEquals($action->getTimeSpec(), 'y'); + $this->assertEquals($action->getTimeValue(), 'y'); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListBucketsResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListBucketsResultTest.php new file mode 100644 index 0000000..1abe1f5 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListBucketsResultTest.php @@ -0,0 +1,97 @@ + + + + ut_test_put_bucket + ut_test_put_bucket + + + + oss-cn-hangzhou-a + xz02tphky6fjfiuc0 + 2014-05-15T11:18:32.000Z + + + oss-cn-hangzhou-a + xz02tphky6fjfiuc1 + 2014-05-15T11:18:32.000Z + + + +BBBB; + + private $nullXml = << + + + ut_test_put_bucket + ut_test_put_bucket + + + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new ListBucketsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $bucketListInfo = $result->getData(); + $this->assertEquals(2, count($bucketListInfo->getBucketList())); + } + + public function testParseNullXml() + { + $response = new ResponseCore(array(), $this->nullXml, 200); + $result = new ListBucketsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $bucketListInfo = $result->getData(); + $this->assertEquals(0, count($bucketListInfo->getBucketList())); + } + + public function test403() + { + $errorHeader = array( + 'x-oss-request-id' => '1a2b-3c4d' + ); + + $errorBody = <<< BBBB + + + NoSuchBucket + The specified bucket does not exist. + 566B870D207FB3044302EB0A + hello.oss-test.aliyun-inc.com + hello + +BBBB; + $response = new ResponseCore($errorHeader, $errorBody, 403); + try { + new ListBucketsResult($response); + } catch (OssException $e) { + $this->assertEquals( + $e->getMessage(), + 'NoSuchBucket: The specified bucket does not exist. RequestId: 1a2b-3c4d'); + $this->assertEquals($e->getHTTPStatus(), '403'); + $this->assertEquals($e->getRequestId(), '1a2b-3c4d'); + $this->assertEquals($e->getErrorCode(), 'NoSuchBucket'); + $this->assertEquals($e->getErrorMessage(), 'The specified bucket does not exist.'); + $this->assertEquals($e->getDetails(), $errorBody); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListMultipartUploadResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListMultipartUploadResultTest.php new file mode 100644 index 0000000..5c757d3 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListMultipartUploadResultTest.php @@ -0,0 +1,114 @@ + + + oss-example + xx + 3 + oss.avi + 0004B99B8E707874FC2D692FA5D77D3F + x + xx + 1000 + false + + multipart.data + 0004B999EF518A1FE585B0C9360DC4C8 + 2012-02-23T04:18:23.000Z + + + multipart.data + 0004B999EF5A239BB9138C6227D69F95 + 2012-02-23T04:18:23.000Z + + + oss.avi + 0004B99B8E707874FC2D692FA5D77D3F + 2012-02-23T06:14:27.000Z + + +BBBB; + + private $validXmlWithEncodedKey = << + + oss-example + url + php%2Bkey-marker + 3 + php%2Bnext-key-marker + 0004B99B8E707874FC2D692FA5D77D3F + %2F + php%2Bprefix + 1000 + true + + php%2Bkey-1 + 0004B999EF518A1FE585B0C9360DC4C8 + 2012-02-23T04:18:23.000Z + + + php%2Bkey-2 + 0004B999EF5A239BB9138C6227D69F95 + 2012-02-23T04:18:23.000Z + + + php%2Bkey-3 + 0004B99B8E707874FC2D692FA5D77D3F + 2012-02-23T06:14:27.000Z + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new ListMultipartUploadResult($response); + $listMultipartUploadInfo = $result->getData(); + $this->assertEquals("oss-example", $listMultipartUploadInfo->getBucket()); + $this->assertEquals("xx", $listMultipartUploadInfo->getKeyMarker()); + $this->assertEquals(3, $listMultipartUploadInfo->getUploadIdMarker()); + $this->assertEquals("oss.avi", $listMultipartUploadInfo->getNextKeyMarker()); + $this->assertEquals("0004B99B8E707874FC2D692FA5D77D3F", $listMultipartUploadInfo->getNextUploadIdMarker()); + $this->assertEquals("x", $listMultipartUploadInfo->getDelimiter()); + $this->assertEquals("xx", $listMultipartUploadInfo->getPrefix()); + $this->assertEquals(1000, $listMultipartUploadInfo->getMaxUploads()); + $this->assertEquals("false", $listMultipartUploadInfo->getIsTruncated()); + $uploads = $listMultipartUploadInfo->getUploads(); + $this->assertEquals("multipart.data", $uploads[0]->getKey()); + $this->assertEquals("0004B999EF518A1FE585B0C9360DC4C8", $uploads[0]->getUploadId()); + $this->assertEquals("2012-02-23T04:18:23.000Z", $uploads[0]->getInitiated()); + } + + public function testParseValidXmlWithEncodedKey() + { + $response = new ResponseCore(array(), $this->validXmlWithEncodedKey, 200); + $result = new ListMultipartUploadResult($response); + $listMultipartUploadInfo = $result->getData(); + $this->assertEquals("oss-example", $listMultipartUploadInfo->getBucket()); + $this->assertEquals("php+key-marker", $listMultipartUploadInfo->getKeyMarker()); + $this->assertEquals("php+next-key-marker", $listMultipartUploadInfo->getNextKeyMarker()); + $this->assertEquals(3, $listMultipartUploadInfo->getUploadIdMarker()); + $this->assertEquals("0004B99B8E707874FC2D692FA5D77D3F", $listMultipartUploadInfo->getNextUploadIdMarker()); + $this->assertEquals("/", $listMultipartUploadInfo->getDelimiter()); + $this->assertEquals("php+prefix", $listMultipartUploadInfo->getPrefix()); + $this->assertEquals(1000, $listMultipartUploadInfo->getMaxUploads()); + $this->assertEquals("true", $listMultipartUploadInfo->getIsTruncated()); + $uploads = $listMultipartUploadInfo->getUploads(); + $this->assertEquals("php+key-1", $uploads[0]->getKey()); + $this->assertEquals("0004B999EF518A1FE585B0C9360DC4C8", $uploads[0]->getUploadId()); + $this->assertEquals("2012-02-23T04:18:23.000Z", $uploads[0]->getInitiated()); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListObjectsResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListObjectsResultTest.php new file mode 100644 index 0000000..85f262c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListObjectsResultTest.php @@ -0,0 +1,151 @@ + + + testbucket-hf + + + 1000 + / + false + + oss-php-sdk-test/ + + + test/ + + +BBBB; + + private $validXml2 = << + + testbucket-hf + oss-php-sdk-test/ + xx + 1000 + / + false + + oss-php-sdk-test/upload-test-object-name.txt + 2015-11-18T03:36:00.000Z + "89B9E567E7EB8815F2F7D41851F9A2CD" + Normal + 13115 + Standard + + cname_user + cname_user + + + +BBBB; + + private $validXmlWithEncodedKey = << + + testbucket-hf + url + php%2Fprefix + php%2Fmarker + php%2Fnext-marker + 1000 + %2F + true + + php/a%2Bb + 2015-11-18T03:36:00.000Z + "89B9E567E7EB8815F2F7D41851F9A2CD" + Normal + 13115 + Standard + + cname_user + cname_user + + + +BBBB; + + public function testParseValidXml1() + { + $response = new ResponseCore(array(), $this->validXml1, 200); + $result = new ListObjectsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $objectListInfo = $result->getData(); + $this->assertEquals(2, count($objectListInfo->getPrefixList())); + $this->assertEquals(0, count($objectListInfo->getObjectList())); + $this->assertEquals('testbucket-hf', $objectListInfo->getBucketName()); + $this->assertEquals('', $objectListInfo->getPrefix()); + $this->assertEquals('', $objectListInfo->getMarker()); + $this->assertEquals(1000, $objectListInfo->getMaxKeys()); + $this->assertEquals('/', $objectListInfo->getDelimiter()); + $this->assertEquals('false', $objectListInfo->getIsTruncated()); + $prefixes = $objectListInfo->getPrefixList(); + $this->assertEquals('oss-php-sdk-test/', $prefixes[0]->getPrefix()); + $this->assertEquals('test/', $prefixes[1]->getPrefix()); + } + + public function testParseValidXml2() + { + $response = new ResponseCore(array(), $this->validXml2, 200); + $result = new ListObjectsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $objectListInfo = $result->getData(); + $this->assertEquals(0, count($objectListInfo->getPrefixList())); + $this->assertEquals(1, count($objectListInfo->getObjectList())); + $this->assertEquals('testbucket-hf', $objectListInfo->getBucketName()); + $this->assertEquals('oss-php-sdk-test/', $objectListInfo->getPrefix()); + $this->assertEquals('xx', $objectListInfo->getMarker()); + $this->assertEquals(1000, $objectListInfo->getMaxKeys()); + $this->assertEquals('/', $objectListInfo->getDelimiter()); + $this->assertEquals('false', $objectListInfo->getIsTruncated()); + $objects = $objectListInfo->getObjectList(); + $this->assertEquals('oss-php-sdk-test/upload-test-object-name.txt', $objects[0]->getKey()); + $this->assertEquals('2015-11-18T03:36:00.000Z', $objects[0]->getLastModified()); + $this->assertEquals('"89B9E567E7EB8815F2F7D41851F9A2CD"', $objects[0]->getETag()); + $this->assertEquals('Normal', $objects[0]->getType()); + $this->assertEquals(13115, $objects[0]->getSize()); + $this->assertEquals('Standard', $objects[0]->getStorageClass()); + } + + public function testParseValidXmlWithEncodedKey() + { + $response = new ResponseCore(array(), $this->validXmlWithEncodedKey, 200); + $result = new ListObjectsResult($response); + $this->assertTrue($result->isOK()); + $this->assertNotNull($result->getData()); + $this->assertNotNull($result->getRawResponse()); + $objectListInfo = $result->getData(); + $this->assertEquals(0, count($objectListInfo->getPrefixList())); + $this->assertEquals(1, count($objectListInfo->getObjectList())); + $this->assertEquals('testbucket-hf', $objectListInfo->getBucketName()); + $this->assertEquals('php/prefix', $objectListInfo->getPrefix()); + $this->assertEquals('php/marker', $objectListInfo->getMarker()); + $this->assertEquals('php/next-marker', $objectListInfo->getNextMarker()); + $this->assertEquals(1000, $objectListInfo->getMaxKeys()); + $this->assertEquals('/', $objectListInfo->getDelimiter()); + $this->assertEquals('true', $objectListInfo->getIsTruncated()); + $objects = $objectListInfo->getObjectList(); + $this->assertEquals('php/a+b', $objects[0]->getKey()); + $this->assertEquals('2015-11-18T03:36:00.000Z', $objects[0]->getLastModified()); + $this->assertEquals('"89B9E567E7EB8815F2F7D41851F9A2CD"', $objects[0]->getETag()); + $this->assertEquals('Normal', $objects[0]->getType()); + $this->assertEquals(13115, $objects[0]->getSize()); + $this->assertEquals('Standard', $objects[0]->getStorageClass()); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListPartsResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListPartsResultTest.php new file mode 100644 index 0000000..c446714 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ListPartsResultTest.php @@ -0,0 +1,62 @@ + + + multipart_upload + multipart.data + 0004B999EF5A239BB9138C6227D69F95 + 5 + 1000 + false + + 1 + 2012-02-23T07:01:34.000Z + "3349DC700140D7F86A078484278075A9" + 6291456 + + + 2 + 2012-02-23T07:01:12.000Z + "3349DC700140D7F86A078484278075A9" + 6291456 + + + 5 + 2012-02-23T07:02:03.000Z + "7265F4D211B56873A381D321F586E4A9" + 1024 + + +BBBB; + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new ListPartsResult($response); + $listPartsInfo = $result->getData(); + $this->assertEquals("multipart_upload", $listPartsInfo->getBucket()); + $this->assertEquals("multipart.data", $listPartsInfo->getKey()); + $this->assertEquals("0004B999EF5A239BB9138C6227D69F95", $listPartsInfo->getUploadId()); + $this->assertEquals(5, $listPartsInfo->getNextPartNumberMarker()); + $this->assertEquals(1000, $listPartsInfo->getMaxParts()); + $this->assertEquals("false", $listPartsInfo->getIsTruncated()); + $this->assertEquals(3, count($listPartsInfo->getListPart())); + $parts = $listPartsInfo->getListPart(); + $this->assertEquals(1, $parts[0]->getPartNumber()); + $this->assertEquals('2012-02-23T07:01:34.000Z', $parts[0]->getLastModified()); + $this->assertEquals('"3349DC700140D7F86A078484278075A9"', $parts[0]->getETag()); + $this->assertEquals(6291456, $parts[0]->getSize()); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LiveChannelXmlTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LiveChannelXmlTest.php new file mode 100644 index 0000000..cc3e219 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LiveChannelXmlTest.php @@ -0,0 +1,249 @@ + + + xxx + enabled + + hls + 1000 + 5 + hello.m3u8 + + +BBBB; + + private $info = << + + live-1 + xxx + + rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/213443245345 + + + http://bucket.oss-cn-hangzhou.aliyuncs.com/213443245345/播放列表.m3u8 + + enabled + 2015-11-24T14:25:31.000Z + +BBBB; + + private $list = << + +xxx + yyy + 100 + false + 121312132 + + 12123214323431 + xxx + + rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/1 + + + http://bucket.oss-cn-hangzhou.aliyuncs.com/1/播放列表.m3u8 + + enabled + 2015-11-24T14:25:31.000Z + + + 432423432423 + yyy + + rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/2 + + + http://bucket.oss-cn-hangzhou.aliyuncs.com/2/播放列表.m3u8 + + enabled + 2016-11-24T14:25:31.000Z + + +BBBB; + + private $status = << + + Live + 2016-10-20T14:25:31.000Z + 10.1.2.4:47745 + + + +BBBB; + + private $history = << + + + 2013-11-24T14:25:31.000Z + 2013-11-24T15:25:31.000Z + 10.101.194.148:56861 + + + 2014-11-24T14:25:31.000Z + 2014-11-24T15:25:31.000Z + 10.101.194.148:56862 + + + 2015-11-24T14:25:31.000Z + 2015-11-24T15:25:31.000Z + 10.101.194.148:56863 + + +BBBB; + + public function testLiveChannelStatus() + { + $stat = new GetLiveChannelStatus(); + $stat->parseFromXml($this->status); + + $this->assertEquals('Live', $stat->getStatus()); + $this->assertEquals('2016-10-20T14:25:31.000Z', $stat->getConnectedTime()); + $this->assertEquals('10.1.2.4:47745', $stat->getRemoteAddr()); + + $this->assertEquals(1280, $stat->getVideoWidth()); + $this->assertEquals(536, $stat->getVideoHeight()); + $this->assertEquals(24, $stat->getVideoFrameRate()); + $this->assertEquals(72513, $stat->getVideoBandwidth()); + $this->assertEquals('H264', $stat->getVideoCodec()); + $this->assertEquals(6519, $stat->getAudioBandwidth()); + $this->assertEquals(44100, $stat->getAudioSampleRate()); + $this->assertEquals('AAC', $stat->getAudioCodec()); + + } + + public function testLiveChannelHistory() + { + $history = new GetLiveChannelHistory(); + $history->parseFromXml($this->history); + + $recordList = $history->getLiveRecordList(); + $this->assertEquals(3, count($recordList)); + + $list0 = $recordList[0]; + $this->assertEquals('2013-11-24T14:25:31.000Z', $list0->getStartTime()); + $this->assertEquals('2013-11-24T15:25:31.000Z', $list0->getEndTime()); + $this->assertEquals('10.101.194.148:56861', $list0->getRemoteAddr()); + + $list1 = $recordList[1]; + $this->assertEquals('2014-11-24T14:25:31.000Z', $list1->getStartTime()); + $this->assertEquals('2014-11-24T15:25:31.000Z', $list1->getEndTime()); + $this->assertEquals('10.101.194.148:56862', $list1->getRemoteAddr()); + + $list2 = $recordList[2]; + $this->assertEquals('2015-11-24T14:25:31.000Z', $list2->getStartTime()); + $this->assertEquals('2015-11-24T15:25:31.000Z', $list2->getEndTime()); + $this->assertEquals('10.101.194.148:56863', $list2->getRemoteAddr()); + + } + + public function testLiveChannelConfig() + { + $config = new LiveChannelConfig(array('name' => 'live-1')); + $config->parseFromXml($this->config); + + $this->assertEquals('xxx', $config->getDescription()); + $this->assertEquals('enabled', $config->getStatus()); + $this->assertEquals('hls', $config->getType()); + $this->assertEquals(1000, $config->getFragDuration()); + $this->assertEquals(5, $config->getFragCount()); + $this->assertEquals('hello.m3u8', $config->getPlayListName()); + + $xml = $config->serializeToXml(); + $config2 = new LiveChannelConfig(array('name' => 'live-2')); + $config2->parseFromXml($xml); + $this->assertEquals('xxx', $config2->getDescription()); + $this->assertEquals('enabled', $config2->getStatus()); + $this->assertEquals('hls', $config2->getType()); + $this->assertEquals(1000, $config2->getFragDuration()); + $this->assertEquals(5, $config2->getFragCount()); + $this->assertEquals('hello.m3u8', $config2->getPlayListName()); + } + + public function testLiveChannelInfo() + { + $info = new LiveChannelInfo(array('name' => 'live-1')); + $info->parseFromXml($this->info); + + $this->assertEquals('live-1', $info->getName()); + $this->assertEquals('xxx', $info->getDescription()); + $this->assertEquals('enabled', $info->getStatus()); + $this->assertEquals('2015-11-24T14:25:31.000Z', $info->getLastModified()); + $pubs = $info->getPublishUrls(); + $this->assertEquals(1, count($pubs)); + $this->assertEquals('rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/213443245345', $pubs[0]); + + $plays = $info->getPlayUrls(); + $this->assertEquals(1, count($plays)); + $this->assertEquals('http://bucket.oss-cn-hangzhou.aliyuncs.com/213443245345/播放列表.m3u8', $plays[0]); + } + + public function testLiveChannelList() + { + $list = new LiveChannelListInfo(); + $list->parseFromXml($this->list); + + $this->assertEquals('xxx', $list->getPrefix()); + $this->assertEquals('yyy', $list->getMarker()); + $this->assertEquals(100, $list->getMaxKeys()); + $this->assertEquals(false, $list->getIsTruncated()); + $this->assertEquals('121312132', $list->getNextMarker()); + + $channels = $list->getChannelList(); + $this->assertEquals(2, count($channels)); + + $chan1 = $channels[0]; + $this->assertEquals('12123214323431', $chan1->getName()); + $this->assertEquals('xxx', $chan1->getDescription()); + $this->assertEquals('enabled', $chan1->getStatus()); + $this->assertEquals('2015-11-24T14:25:31.000Z', $chan1->getLastModified()); + $pubs = $chan1->getPublishUrls(); + $this->assertEquals(1, count($pubs)); + $this->assertEquals('rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/1', $pubs[0]); + + $plays = $chan1->getPlayUrls(); + $this->assertEquals(1, count($plays)); + $this->assertEquals('http://bucket.oss-cn-hangzhou.aliyuncs.com/1/播放列表.m3u8', $plays[0]); + + $chan2 = $channels[1]; + $this->assertEquals('432423432423', $chan2->getName()); + $this->assertEquals('yyy', $chan2->getDescription()); + $this->assertEquals('enabled', $chan2->getStatus()); + $this->assertEquals('2016-11-24T14:25:31.000Z', $chan2->getLastModified()); + $pubs = $chan2->getPublishUrls(); + $this->assertEquals(1, count($pubs)); + $this->assertEquals('rtmp://bucket.oss-cn-hangzhou.aliyuncs.com/live/2', $pubs[0]); + + $plays = $chan2->getPlayUrls(); + $this->assertEquals(1, count($plays)); + $this->assertEquals('http://bucket.oss-cn-hangzhou.aliyuncs.com/2/播放列表.m3u8', $plays[0]); + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LoggingConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LoggingConfigTest.php new file mode 100644 index 0000000..01496bb --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/LoggingConfigTest.php @@ -0,0 +1,47 @@ + + + +TargetBucket +TargetPrefix + + +BBBB; + + private $nullXml = << + +BBBB; + + public function testParseValidXml() + { + $loggingConfig = new LoggingConfig(); + $loggingConfig->parseFromXml($this->validXml); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml(strval($loggingConfig))); + } + + public function testConstruct() + { + $loggingConfig = new LoggingConfig('TargetBucket', 'TargetPrefix'); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($loggingConfig->serializeToXml())); + } + + public function testFailedConstruct() + { + $loggingConfig = new LoggingConfig('TargetBucket', null); + $this->assertEquals($this->cleanXml($this->nullXml), $this->cleanXml($loggingConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/MimeTypesTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/MimeTypesTest.php new file mode 100644 index 0000000..0697409 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/MimeTypesTest.php @@ -0,0 +1,13 @@ +assertEquals('application/xml', MimeTypes::getMimetype('file.xml')); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ObjectAclTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ObjectAclTest.php new file mode 100644 index 0000000..d397288 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/ObjectAclTest.php @@ -0,0 +1,28 @@ +deleteObject($bucket, $object); + $client->putObject($bucket, $object, "hello world"); + + $acl = $client->getObjectAcl($bucket, $object); + $this->assertEquals('default', $acl); + + $client->putObjectAcl($bucket, $object, 'public-read'); + $acl = $client->getObjectAcl($bucket, $object); + $this->assertEquals('public-read', $acl); + + $content = $client->getObject($bucket, $object); + $this->assertEquals('hello world', $content); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketCorsTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketCorsTest.php new file mode 100644 index 0000000..a32154b --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketCorsTest.php @@ -0,0 +1,84 @@ +addAllowedHeader("x-oss-test"); + $rule->addAllowedHeader("x-oss-test2"); + $rule->addAllowedHeader("x-oss-test2"); + $rule->addAllowedHeader("x-oss-test3"); + $rule->addAllowedOrigin("http://www.b.com"); + $rule->addAllowedOrigin("http://www.a.com"); + $rule->addAllowedOrigin("http://www.a.com"); + $rule->addAllowedMethod("GET"); + $rule->addAllowedMethod("PUT"); + $rule->addAllowedMethod("POST"); + $rule->addExposeHeader("x-oss-test1"); + $rule->addExposeHeader("x-oss-test1"); + $rule->addExposeHeader("x-oss-test2"); + $rule->setMaxAgeSeconds(10); + $corsConfig->addRule($rule); + $rule = new CorsRule(); + $rule->addAllowedHeader("x-oss-test"); + $rule->addAllowedMethod("GET"); + $rule->addAllowedOrigin("http://www.b.com"); + $rule->addExposeHeader("x-oss-test1"); + $rule->setMaxAgeSeconds(110); + $corsConfig->addRule($rule); + + try { + $this->ossClient->putBucketCors($this->bucket, $corsConfig); + } catch (OssException $e) { + $this->assertFalse(True); + } + + try { + Common::waitMetaSync(); + $object = "cors/test.txt"; + $this->ossClient->putObject($this->bucket, $object, file_get_contents(__FILE__)); + $headers = $this->ossClient->optionsObject($this->bucket, $object, "http://www.a.com", "GET", "", null); + $this->assertNotEmpty($headers); + } catch (OssException $e) { + var_dump($e->getMessage()); + } + + try { + Common::waitMetaSync(); + $corsConfig2 = $this->ossClient->getBucketCors($this->bucket); + $this->assertNotNull($corsConfig2); + $this->assertEquals($corsConfig->serializeToXml(), $corsConfig2->serializeToXml()); + } catch (OssException $e) { + $this->assertFalse(True); + } + + try { + Common::waitMetaSync(); + $this->ossClient->deleteBucketCors($this->bucket); + } catch (OssException $e) { + $this->assertFalse(True); + } + + try { + Common::waitMetaSync(); + $corsConfig3 = $this->ossClient->getBucketCors($this->bucket); + $this->assertNotNull($corsConfig3); + $this->assertNotEquals($corsConfig->serializeToXml(), $corsConfig3->serializeToXml()); + } catch (OssException $e) { + $this->assertFalse(True); + } + + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLifecycleTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLifecycleTest.php new file mode 100644 index 0000000..46da1f0 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLifecycleTest.php @@ -0,0 +1,57 @@ +addRule($lifecycleRule); + $actions = array(); + $actions[] = new LifecycleAction("Expiration", "Date", '2022-10-12T00:00:00.000Z'); + $lifecycleRule = new LifecycleRule("delete temporary files", "temporary/", "Enabled", $actions); + $lifecycleConfig->addRule($lifecycleRule); + + try { + $this->ossClient->putBucketLifecycle($this->bucket, $lifecycleConfig); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + Common::waitMetaSync(); + $lifecycleConfig2 = $this->ossClient->getBucketLifecycle($this->bucket); + $this->assertEquals($lifecycleConfig->serializeToXml(), $lifecycleConfig2->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + Common::waitMetaSync(); + $this->ossClient->deleteBucketLifecycle($this->bucket); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + Common::waitMetaSync(); + $lifecycleConfig3 = $this->ossClient->getBucketLifecycle($this->bucket); + $this->assertNotEquals($lifecycleConfig->serializeToXml(), $lifecycleConfig3->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLoggingTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLoggingTest.php new file mode 100644 index 0000000..16a10eb --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketLoggingTest.php @@ -0,0 +1,43 @@ +bucket, 'prefix'); + try { + $this->ossClient->putBucketLogging($this->bucket, $this->bucket, 'prefix'); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $loggingConfig2 = $this->ossClient->getBucketLogging($this->bucket); + $this->assertEquals($loggingConfig->serializeToXml(), $loggingConfig2->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $this->ossClient->deleteBucketLogging($this->bucket); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $loggingConfig3 = $this->ossClient->getBucketLogging($this->bucket); + $this->assertNotEquals($loggingConfig->serializeToXml(), $loggingConfig3->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketRefererTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketRefererTest.php new file mode 100644 index 0000000..ba7d14f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketRefererTest.php @@ -0,0 +1,48 @@ +addReferer('http://www.aliyun.com'); + + try { + $this->ossClient->putBucketReferer($this->bucket, $refererConfig); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $refererConfig2 = $this->ossClient->getBucketReferer($this->bucket); + $this->assertEquals($refererConfig->serializeToXml(), $refererConfig2->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $nullRefererConfig = new RefererConfig(); + $nullRefererConfig->setAllowEmptyReferer(false); + $this->ossClient->putBucketReferer($this->bucket, $nullRefererConfig); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $refererConfig3 = $this->ossClient->getBucketLogging($this->bucket); + $this->assertNotEquals($refererConfig->serializeToXml(), $refererConfig3->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketStorageCapacityTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketStorageCapacityTest.php new file mode 100644 index 0000000..87548f9 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketStorageCapacityTest.php @@ -0,0 +1,56 @@ +ossClient->getBucketStorageCapacity($this->bucket); + $this->assertEquals($storageCapacity, -1); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + $this->ossClient->putBucketStorageCapacity($this->bucket, 1000); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + Common::waitMetaSync(); + $storageCapacity = $this->ossClient->getBucketStorageCapacity($this->bucket); + $this->assertEquals($storageCapacity, 1000); + } catch (OssException $e) { + $this->assertTrue(false); + } + + try { + $this->ossClient->putBucketStorageCapacity($this->bucket, 0); + + Common::waitMetaSync(); + + $storageCapacity = $this->ossClient->getBucketStorageCapacity($this->bucket); + $this->assertEquals($storageCapacity, 0); + + $this->ossClient->putObject($this->bucket, 'test-storage-capacity','test-content'); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('Bucket storage exceed max storage capacity.',$e->getErrorMessage()); + } + + try { + $this->ossClient->putBucketStorageCapacity($this->bucket, -2); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals(400, $e->getHTTPStatus()); + $this->assertEquals('InvalidArgument', $e->getErrorCode()); + } + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketTest.php new file mode 100644 index 0000000..f207ca1 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketTest.php @@ -0,0 +1,113 @@ +ossClient->createBucket("s"); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('"s"bucket name is invalid', $e->getMessage()); + } + } + + public function testBucketWithInvalidACL() + { + try { + $this->ossClient->createBucket($this->bucket, "invalid"); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('invalid:acl is invalid(private,public-read,public-read-write)', $e->getMessage()); + } + } + + public function testBucket() + { + $this->ossClient->createBucket($this->bucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); + + $bucketListInfo = $this->ossClient->listBuckets(); + $this->assertNotNull($bucketListInfo); + + $bucketList = $bucketListInfo->getBucketList(); + $this->assertTrue(is_array($bucketList)); + $this->assertGreaterThan(0, count($bucketList)); + + $this->ossClient->putBucketAcl($this->bucket, OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); + Common::waitMetaSync(); + $this->assertEquals($this->ossClient->getBucketAcl($this->bucket), OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE); + + $this->assertTrue($this->ossClient->doesBucketExist($this->bucket)); + $this->assertFalse($this->ossClient->doesBucketExist($this->bucket . '-notexist')); + + $this->assertEquals($this->ossClient->getBucketLocation($this->bucket), 'oss-us-west-1'); + + $res = $this->ossClient->getBucketMeta($this->bucket); + $this->assertEquals('200', $res['info']['http_code']); + $this->assertEquals('oss-us-west-1', $res['x-oss-bucket-region']); + } + + public function testCreateBucketWithStorageType() + { + $object = 'storage-object'; + + $this->ossClient->putObject($this->archiveBucket, $object,'testcontent'); + try { + $this->ossClient->getObject($this->archiveBucket, $object); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('403', $e->getHTTPStatus()); + $this->assertEquals('InvalidObjectState', $e->getErrorCode()); + } + + $this->ossClient->putObject($this->iaBucket, $object,'testcontent'); + $result = $this->ossClient->getObject($this->iaBucket, $object); + $this->assertEquals($result, 'testcontent'); + + $this->ossClient->putObject($this->bucket, $object,'testcontent'); + $result = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($result, 'testcontent'); + } + + public function setUp() + { + parent::setUp(); + + $this->iaBucket = 'ia-' . $this->bucket; + $this->archiveBucket = 'archive-' . $this->bucket; + $options = array( + OssClient::OSS_STORAGE => OssClient::OSS_STORAGE_IA + ); + + $this->ossClient->createBucket($this->iaBucket, OssClient::OSS_ACL_TYPE_PRIVATE, $options); + + $options = array( + OssClient::OSS_STORAGE => OssClient::OSS_STORAGE_ARCHIVE + ); + + $this->ossClient->createBucket($this->archiveBucket, OssClient::OSS_ACL_TYPE_PRIVATE, $options); + } + + public function tearDown() + { + parent::tearDown(); + + $object = 'storage-object'; + + $this->ossClient->deleteObject($this->iaBucket, $object); + $this->ossClient->deleteObject($this->archiveBucket, $object); + $this->ossClient->deleteBucket($this->iaBucket); + $this->ossClient->deleteBucket($this->archiveBucket); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketWebsiteTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketWebsiteTest.php new file mode 100644 index 0000000..dfa9cc1 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientBucketWebsiteTest.php @@ -0,0 +1,46 @@ +ossClient->putBucketWebsite($this->bucket, $websiteConfig); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertTrue(false); + } + + try { + Common::waitMetaSync(); + $websiteConfig2 = $this->ossClient->getBucketWebsite($this->bucket); + $this->assertEquals($websiteConfig->serializeToXml(), $websiteConfig2->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $this->ossClient->deleteBucketWebsite($this->bucket); + } catch (OssException $e) { + $this->assertTrue(false); + } + try { + Common::waitMetaSync(); + $websiteConfig3 = $this->ossClient->getBucketLogging($this->bucket); + $this->assertNotEquals($websiteConfig->serializeToXml(), $websiteConfig3->serializeToXml()); + } catch (OssException $e) { + $this->assertTrue(false); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientImageTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientImageTest.php new file mode 100644 index 0000000..df8bd6c --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientImageTest.php @@ -0,0 +1,100 @@ +client = Common::getOssClient(); + $this->bucketName = 'php-sdk-test-bucket-image-' . strval(rand(0, 10000)); + $this->client->createBucket($this->bucketName); + Common::waitMetaSync(); + $this->local_file = "example.jpg"; + $this->object = "oss-example.jpg"; + $this->download_file = "image.jpg"; + + $this->client->uploadFile($this->bucketName, $this->object, $this->local_file); + } + + public function tearDown() + { + $this->client->deleteObject($this->bucketName, $this->object); + $this->client->deleteBucket($this->bucketName); + } + + public function testImageResize() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/resize,m_fixed,h_100,w_100", ); + $this->check($options, 100, 100, 3267, 'jpg'); + } + + public function testImageCrop() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/crop,w_100,h_100,x_100,y_100,r_1", ); + $this->check($options, 100, 100, 1969, 'jpg'); + } + + public function testImageRotate() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/rotate,90", ); + $this->check($options, 267, 400, 20998, 'jpg'); + } + + public function testImageSharpen() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/sharpen,100", ); + $this->check($options, 400, 267, 23015, 'jpg'); + } + + public function testImageWatermark() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/watermark,text_SGVsbG8g5Zu-54mH5pyN5YqhIQ", ); + $this->check($options, 400, 267, 26369, 'jpg'); + } + + public function testImageFormat() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/format,png", ); + $this->check($options, 400, 267, 160733, 'png'); + } + + public function testImageTofile() + { + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $this->download_file, + OssClient::OSS_PROCESS => "image/resize,m_fixed,w_100,h_100", ); + $this->check($options, 100, 100, 3267, 'jpg'); + } + + private function check($options, $width, $height, $size, $type) + { + $this->client->getObject($this->bucketName, $this->object, $options); + $array = getimagesize($this->download_file); + $this->assertEquals($width, $array[0]); + $this->assertEquals($height, $array[1]); + $this->assertEquals($type === 'jpg' ? 2 : 3, $array[2]);//2 <=> jpg + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientMultipartUploadTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientMultipartUploadTest.php new file mode 100644 index 0000000..a95f412 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientMultipartUploadTest.php @@ -0,0 +1,313 @@ +ossClient->uploadDir($this->bucket, "", "abc/ds/s/s/notexitst"); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals("parameter error: abc/ds/s/s/notexitst is not a directory, please check it", $e->getMessage()); + } + + } + + public function testMultipartUploadBigFile() + { + $bigFileName = __DIR__ . DIRECTORY_SEPARATOR . "/bigfile.tmp"; + $localFilename = __DIR__ . DIRECTORY_SEPARATOR . "/localfile.tmp"; + OssUtil::generateFile($bigFileName, 6 * 1024 * 1024); + $object = 'mpu/multipart-bigfile-test.tmp'; + try { + $this->ossClient->multiuploadFile($this->bucket, $object, $bigFileName, array(OssClient::OSS_PART_SIZE => 1)); + $options = array(OssClient::OSS_FILE_DOWNLOAD => $localFilename); + $this->ossClient->getObject($this->bucket, $object, $options); + $this->assertEquals(md5_file($bigFileName), md5_file($localFilename)); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertFalse(true); + } + unlink($bigFileName); + unlink($localFilename); + } + + public function testMultipartUploadBigFileWithMD5Check() + { + $bigFileName = __DIR__ . DIRECTORY_SEPARATOR . "/bigfile.tmp"; + $localFilename = __DIR__ . DIRECTORY_SEPARATOR . "/localfile.tmp"; + OssUtil::generateFile($bigFileName, 6 * 1024 * 1024); + $object = 'mpu/multipart-bigfile-test.tmp'; + $options = array( + OssClient::OSS_CHECK_MD5 => true, + OssClient::OSS_PART_SIZE => 1, + ); + try { + $this->ossClient->multiuploadFile($this->bucket, $object, $bigFileName, $options); + $options = array(OssClient::OSS_FILE_DOWNLOAD => $localFilename); + $this->ossClient->getObject($this->bucket, $object, $options); + $this->assertEquals(md5_file($bigFileName), md5_file($localFilename)); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertFalse(true); + } + unlink($bigFileName); + unlink($localFilename); + } + + public function testCopyPart() + { + $object = "mpu/multipart-test.txt"; + $copiedObject = "mpu/multipart-test.txt.copied"; + $this->ossClient->putObject($this->bucket, $copiedObject, file_get_contents(__FILE__)); + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $upload_id = $this->ossClient->initiateMultipartUpload($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + /* + * step 2. uploadPartCopy + */ + $copyId = 1; + $eTag = $this->ossClient->uploadPartCopy($this->bucket, $copiedObject, $this->bucket, $object, $copyId, $upload_id); + $upload_parts[] = array( + 'PartNumber' => $copyId, + 'ETag' => $eTag, + ); + + try { + $listPartsInfo = $this->ossClient->listParts($this->bucket, $object, $upload_id); + $this->assertNotNull($listPartsInfo); + } catch (OssException $e) { + $this->assertTrue(false); + } + + /** + * step 3. + */ + try { + $this->ossClient->completeMultipartUpload($this->bucket, $object, $upload_id, $upload_parts); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertTrue(false); + } + + $this->assertEquals($this->ossClient->getObject($this->bucket, $object), file_get_contents(__FILE__)); + $this->assertEquals($this->ossClient->getObject($this->bucket, $copiedObject), file_get_contents(__FILE__)); + } + + public function testAbortMultipartUpload() + { + $object = "mpu/multipart-test.txt"; + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $upload_id = $this->ossClient->initiateMultipartUpload($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + /* + * step 2. 上传分片 + */ + $part_size = 10 * 1024 * 1024; + $upload_file = __FILE__; + $upload_filesize = filesize($upload_file); + $pieces = $this->ossClient->generateMultiuploadParts($upload_filesize, $part_size); + $response_upload_part = array(); + $upload_position = 0; + $is_check_md5 = true; + foreach ($pieces as $i => $piece) { + $from_pos = $upload_position + (integer)$piece[OssClient::OSS_SEEK_TO]; + $to_pos = (integer)$piece[OssClient::OSS_LENGTH] + $from_pos - 1; + $up_options = array( + OssClient::OSS_FILE_UPLOAD => $upload_file, + OssClient::OSS_PART_NUM => ($i + 1), + OssClient::OSS_SEEK_TO => $from_pos, + OssClient::OSS_LENGTH => $to_pos - $from_pos + 1, + OssClient::OSS_CHECK_MD5 => $is_check_md5, + ); + if ($is_check_md5) { + $content_md5 = OssUtil::getMd5SumForFile($upload_file, $from_pos, $to_pos); + $up_options[OssClient::OSS_CONTENT_MD5] = $content_md5; + } + //2. 将每一分片上传到OSS + try { + $response_upload_part[] = $this->ossClient->uploadPart($this->bucket, $object, $upload_id, $up_options); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + $upload_parts = array(); + foreach ($response_upload_part as $i => $eTag) { + $upload_parts[] = array( + 'PartNumber' => ($i + 1), + 'ETag' => $eTag, + ); + } + + try { + $listPartsInfo = $this->ossClient->listParts($this->bucket, $object, $upload_id); + $this->assertNotNull($listPartsInfo); + } catch (OssException $e) { + $this->assertTrue(false); + } + $this->assertEquals(1, count($listPartsInfo->getListPart())); + + $numOfMultipartUpload1 = 0; + $options = null; + try { + $listMultipartUploadInfo = $listMultipartUploadInfo = $this->ossClient->listMultipartUploads($this->bucket, $options); + $this->assertNotNull($listMultipartUploadInfo); + $numOfMultipartUpload1 = count($listMultipartUploadInfo->getUploads()); + } catch (OssException $e) { + $this->assertFalse(true); + } + + try { + $this->ossClient->abortMultipartUpload($this->bucket, $object, $upload_id); + } catch (OssException $e) { + $this->assertTrue(false); + } + + $numOfMultipartUpload2 = 0; + try { + $listMultipartUploadInfo = $listMultipartUploadInfo = $this->ossClient->listMultipartUploads($this->bucket, $options); + $this->assertNotNull($listMultipartUploadInfo); + $numOfMultipartUpload2 = count($listMultipartUploadInfo->getUploads()); + } catch (OssException $e) { + $this->assertFalse(true); + } + $this->assertEquals($numOfMultipartUpload1 - 1, $numOfMultipartUpload2); + } + + public function testPutObjectByRawApis() + { + $object = "mpu/multipart-test.txt"; + /** + * step 1. 初始化一个分块上传事件, 也就是初始化上传Multipart, 获取upload id + */ + try { + $upload_id = $this->ossClient->initiateMultipartUpload($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + /* + * step 2. 上传分片 + */ + $part_size = 10 * 1024 * 1024; + $upload_file = __FILE__; + $upload_filesize = filesize($upload_file); + $pieces = $this->ossClient->generateMultiuploadParts($upload_filesize, $part_size); + $response_upload_part = array(); + $upload_position = 0; + $is_check_md5 = true; + foreach ($pieces as $i => $piece) { + $from_pos = $upload_position + (integer)$piece[OssClient::OSS_SEEK_TO]; + $to_pos = (integer)$piece[OssClient::OSS_LENGTH] + $from_pos - 1; + $up_options = array( + OssClient::OSS_FILE_UPLOAD => $upload_file, + OssClient::OSS_PART_NUM => ($i + 1), + OssClient::OSS_SEEK_TO => $from_pos, + OssClient::OSS_LENGTH => $to_pos - $from_pos + 1, + OssClient::OSS_CHECK_MD5 => $is_check_md5, + ); + if ($is_check_md5) { + $content_md5 = OssUtil::getMd5SumForFile($upload_file, $from_pos, $to_pos); + $up_options[OssClient::OSS_CONTENT_MD5] = $content_md5; + } + //2. 将每一分片上传到OSS + try { + $response_upload_part[] = $this->ossClient->uploadPart($this->bucket, $object, $upload_id, $up_options); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + $upload_parts = array(); + foreach ($response_upload_part as $i => $eTag) { + $upload_parts[] = array( + 'PartNumber' => ($i + 1), + 'ETag' => $eTag, + ); + } + + try { + $listPartsInfo = $this->ossClient->listParts($this->bucket, $object, $upload_id); + $this->assertNotNull($listPartsInfo); + } catch (OssException $e) { + $this->assertTrue(false); + } + + /** + * step 3. + */ + try { + $this->ossClient->completeMultipartUpload($this->bucket, $object, $upload_id, $upload_parts); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + function testPutObjectsByDir() + { + $localDirectory = dirname(__FILE__); + $prefix = "samples/codes"; + try { + $this->ossClient->uploadDir($this->bucket, $prefix, $localDirectory); + } catch (OssException $e) { + var_dump($e->getMessage()); + $this->assertFalse(true); + + } + $this->assertTrue($this->ossClient->doesObjectExist($this->bucket, 'samples/codes/' . "OssClientMultipartUploadTest.php")); + } + + public function testPutObjectByMultipartUpload() + { + $object = "mpu/multipart-test.txt"; + $file = __FILE__; + $options = array(); + + try { + $this->ossClient->multiuploadFile($this->bucket, $object, $file, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testPutObjectByMultipartUploadWithMD5Check() + { + $object = "mpu/multipart-test.txt"; + $file = __FILE__; + $options = array(OssClient::OSS_CHECK_MD5 => true); + + try { + $this->ossClient->multiuploadFile($this->bucket, $object, $file, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testListMultipartUploads() + { + $options = null; + try { + $listMultipartUploadInfo = $this->ossClient->listMultipartUploads($this->bucket, $options); + $this->assertNotNull($listMultipartUploadInfo); + } catch (OssException $e) { + $this->assertFalse(true); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientObjectTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientObjectTest.php new file mode 100644 index 0000000..34e3ded --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientObjectTest.php @@ -0,0 +1,588 @@ +ossClient->getObjectMeta($this->bucket, $object); + $this->assertEquals('200', $res['info']['http_code']); + $this->assertEquals('text/plain', $res['content-type']); + $this->assertEquals('Accept-Encoding', $res['vary']); + $this->assertTrue(isset($res['content-length'])); + $this->assertFalse(isset($res['content-encoding'])); + } catch (OssException $e) { + $this->assertTrue(false); + } + + $options = array(OssClient::OSS_HEADERS => array(OssClient::OSS_ACCEPT_ENCODING => 'deflate, gzip')); + + try { + $res = $this->ossClient->getObjectMeta($this->bucket, $object, $options); + $this->assertEquals('200', $res['info']['http_code']); + $this->assertEquals('text/plain', $res['content-type']); + $this->assertEquals('Accept-Encoding', $res['vary']); + $this->assertFalse(isset($res['content-length'])); + $this->assertEquals('gzip', $res['content-encoding']); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testGetObjectWithAcceptEncoding() + { + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $options = array(OssClient::OSS_HEADERS => array(OssClient::OSS_ACCEPT_ENCODING => 'deflate, gzip')); + + try { + $res = $this->ossClient->getObject($this->bucket, $object, $options); + $this->assertEquals(file_get_contents(__FILE__), $res); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testGetObjectWithHeader() + { + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $res = $this->ossClient->getObject($this->bucket, $object, array(OssClient::OSS_LAST_MODIFIED => "xx")); + $this->assertEquals(file_get_contents(__FILE__), $res); + } catch (OssException $e) { + $this->assertEquals('"/ilegal.txt" object name is invalid', $e->getMessage()); + } + } + + public function testGetObjectWithIleggalEtag() + { + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $res = $this->ossClient->getObject($this->bucket, $object, array(OssClient::OSS_ETAG => "xx")); + $this->assertEquals(file_get_contents(__FILE__), $res); + } catch (OssException $e) { + $this->assertEquals('"/ilegal.txt" object name is invalid', $e->getMessage()); + } + } + + public function testObject() + { + /** + * 上传本地变量到bucket + */ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $content = file_get_contents(__FILE__); + $options = array( + OssClient::OSS_LENGTH => strlen($content), + OssClient::OSS_HEADERS => array( + 'Expires' => 'Fri, 28 Feb 2020 05:38:42 GMT', + 'Cache-Control' => 'no-cache', + 'Content-Disposition' => 'attachment;filename=oss_download.log', + 'Content-Encoding' => 'utf-8', + 'Content-Language' => 'zh-CN', + 'x-oss-server-side-encryption' => 'AES256', + 'x-oss-meta-self-define-title' => 'user define meta info', + ), + ); + + try { + $this->ossClient->putObject($this->bucket, $object, $content, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + + try { + $this->ossClient->putObject($this->bucket, $object, $content, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + + try { + $result = $this->ossClient->deleteObjects($this->bucket, "stringtype", $options); + $this->assertEquals('stringtype', $result[0]); + } catch (OssException $e) { + $this->assertEquals('objects must be array', $e->getMessage()); + } + + try { + $result = $this->ossClient->deleteObjects($this->bucket, "stringtype", $options); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('objects must be array', $e->getMessage()); + } + + try { + $this->ossClient->uploadFile($this->bucket, $object, "notexist.txt", $options); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('notexist.txt file does not exist', $e->getMessage()); + } + + /** + * getObject到本地变量,检查是否match + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * getObject的前五个字节 + */ + try { + $options = array(OssClient::OSS_RANGE => '0-4'); + $content = $this->ossClient->getObject($this->bucket, $object, $options); + $this->assertEquals($content, 'assertFalse(true); + } + + + /** + * 上传本地文件到object + */ + try { + $this->ossClient->uploadFile($this->bucket, $object, __FILE__); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 下载文件到本地变量,检查是否match + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 下载文件到本地文件 + */ + $localfile = "upload-test-object-name.txt"; + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $localfile, + ); + + try { + $this->ossClient->getObject($this->bucket, $object, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + $this->assertTrue(file_get_contents($localfile) === file_get_contents(__FILE__)); + if (file_exists($localfile)) { + unlink($localfile); + } + + /** + * 下载文件到本地文件 no such key + */ + $localfile = "upload-test-object-name-no-such-key.txt"; + $options = array( + OssClient::OSS_FILE_DOWNLOAD => $localfile, + ); + + try { + $this->ossClient->getObject($this->bucket, $object . "no-such-key", $options); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertTrue(true); + $this->assertFalse(file_exists($localfile)); + if (strpos($e, "The specified key does not exist") == false) + { + $this->assertTrue(true); + } + } + + /** + * 下载文件到内容 no such key + */ + try { + $result = $this->ossClient->getObject($this->bucket, $object . "no-such-key"); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertTrue(true); + if (strpos($e, "The specified key does not exist") == false) + { + $this->assertTrue(true); + } + } + + /** + * 复制object + */ + $to_bucket = $this->bucket; + $to_object = $object . '.copy'; + $options = array(); + try { + $result = $this->ossClient->copyObject($this->bucket, $object, $to_bucket, $to_object, $options); + $this->assertFalse(empty($result)); + $this->assertEquals(strlen("2016-11-21T03:46:58.000Z"), strlen($result[0])); + $this->assertEquals(strlen("\"5B3C1A2E053D763E1B002CC607C5A0FE\""), strlen($result[1])); + } catch (OssException $e) { + $this->assertFalse(true); + var_dump($e->getMessage()); + + } + + /** + * 检查复制的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $to_object); + $this->assertEquals($content, file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 列出bucket内的文件列表 + */ + $prefix = ''; + $delimiter = '/'; + $next_marker = ''; + $maxkeys = 1000; + $options = array( + 'delimiter' => $delimiter, + 'prefix' => $prefix, + 'max-keys' => $maxkeys, + 'marker' => $next_marker, + ); + + try { + $listObjectInfo = $this->ossClient->listObjects($this->bucket, $options); + $objectList = $listObjectInfo->getObjectList(); + $prefixList = $listObjectInfo->getPrefixList(); + $this->assertNotNull($objectList); + $this->assertNotNull($prefixList); + $this->assertTrue(is_array($objectList)); + $this->assertTrue(is_array($prefixList)); + + } catch (OssException $e) { + $this->assertTrue(false); + } + + /** + * 设置文件的meta信息 + */ + $from_bucket = $this->bucket; + $from_object = "oss-php-sdk-test/upload-test-object-name.txt"; + $to_bucket = $from_bucket; + $to_object = $from_object; + $copy_options = array( + OssClient::OSS_HEADERS => array( + 'Expires' => '2012-10-01 08:00:00', + 'Content-Disposition' => 'attachment; filename="xxxxxx"', + ), + ); + try { + $this->ossClient->copyObject($from_bucket, $from_object, $to_bucket, $to_object, $copy_options); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 获取文件的meta信息 + */ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + try { + $objectMeta = $this->ossClient->getObjectMeta($this->bucket, $object); + $this->assertEquals('attachment; filename="xxxxxx"', $objectMeta[strtolower('Content-Disposition')]); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除单个文件 + */ + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + + try { + $this->assertTrue($this->ossClient->doesObjectExist($this->bucket, $object)); + $this->ossClient->deleteObject($this->bucket, $object); + $this->assertFalse($this->ossClient->doesObjectExist($this->bucket, $object)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除多个个文件 + */ + $object1 = "oss-php-sdk-test/upload-test-object-name.txt"; + $object2 = "oss-php-sdk-test/upload-test-object-name.txt.copy"; + $list = array($object1, $object2); + try { + $this->assertTrue($this->ossClient->doesObjectExist($this->bucket, $object2)); + + $result = $this->ossClient->deleteObjects($this->bucket, $list); + $this->assertEquals($list[1], $result[0]); + $this->assertEquals($list[0], $result[1]); + + $result = $this->ossClient->deleteObjects($this->bucket, $list, array('quiet' => 'true')); + $this->assertEquals(array(), $result); + $this->assertFalse($this->ossClient->doesObjectExist($this->bucket, $object2)); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testAppendObject() + { + $object = "oss-php-sdk-test/append-test-object-name.txt"; + $content_array = array('Hello OSS', 'Hi OSS', 'OSS OK'); + + /** + * 追加上传字符串 + */ + try { + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[0], 0); + $this->assertEquals($position, strlen($content_array[0])); + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[1], $position); + $this->assertEquals($position, strlen($content_array[0]) + strlen($content_array[1])); + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[2], $position); + $this->assertEquals($position, strlen($content_array[0]) + strlen($content_array[1]) + strlen($content_array[1])); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查内容的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, implode($content_array)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 追加上传本地文件 + */ + try { + $position = $this->ossClient->appendFile($this->bucket, $object, __FILE__, 0); + $this->assertEquals($position, filesize(__FILE__)); + $position = $this->ossClient->appendFile($this->bucket, $object, __FILE__, $position); + $this->assertEquals($position, filesize(__FILE__) * 2); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查复制的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__) . file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + + + $options = array( + OssClient::OSS_HEADERS => array( + 'Expires' => '2012-10-01 08:00:00', + 'Content-Disposition' => 'attachment; filename="xxxxxx"', + ), + ); + + /** + * 带option的追加上传 + */ + try { + $position = $this->ossClient->appendObject($this->bucket, $object, "Hello OSS, ", 0, $options); + $position = $this->ossClient->appendObject($this->bucket, $object, "Hi OSS.", $position); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 获取文件的meta信息 + */ + try { + $objectMeta = $this->ossClient->getObjectMeta($this->bucket, $object); + $this->assertEquals('attachment; filename="xxxxxx"', $objectMeta[strtolower('Content-Disposition')]); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testPutIllelObject() + { + $object = "/ilegal.txt"; + try { + $this->ossClient->putObject($this->bucket, $object, "hi", null); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('"/ilegal.txt" object name is invalid', $e->getMessage()); + } + } + + public function testCheckMD5() + { + $object = "oss-php-sdk-test/upload-test-object-name.txt"; + $content = file_get_contents(__FILE__); + $options = array(OssClient::OSS_CHECK_MD5 => true); + + /** + * 上传数据开启MD5 + */ + try { + $this->ossClient->putObject($this->bucket, $object, $content, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查复制的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 上传文件开启MD5 + */ + try { + $this->ossClient->uploadFile($this->bucket, $object, __FILE__, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查复制的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + + $object = "oss-php-sdk-test/append-test-object-name.txt"; + $content_array = array('Hello OSS', 'Hi OSS', 'OSS OK'); + $options = array(OssClient::OSS_CHECK_MD5 => true); + + /** + * 追加上传字符串 + */ + try { + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[0], 0, $options); + $this->assertEquals($position, strlen($content_array[0])); + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[1], $position, $options); + $this->assertEquals($position, strlen($content_array[0]) + strlen($content_array[1])); + $position = $this->ossClient->appendObject($this->bucket, $object, $content_array[2], $position, $options); + $this->assertEquals($position, strlen($content_array[0]) + strlen($content_array[1]) + strlen($content_array[1])); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查内容的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, implode($content_array)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 追加上传本地文件 + */ + try { + $position = $this->ossClient->appendFile($this->bucket, $object, __FILE__, 0, $options); + $this->assertEquals($position, filesize(__FILE__)); + $position = $this->ossClient->appendFile($this->bucket, $object, __FILE__, $position, $options); + $this->assertEquals($position, filesize(__FILE__) * 2); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 检查复制的是否相同 + */ + try { + $content = $this->ossClient->getObject($this->bucket, $object); + $this->assertEquals($content, file_get_contents(__FILE__) . file_get_contents(__FILE__)); + } catch (OssException $e) { + $this->assertFalse(true); + } + + /** + * 删除测试object + */ + try { + $this->ossClient->deleteObject($this->bucket, $object); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function setUp() + { + parent::setUp(); + $this->ossClient->putObject($this->bucket, 'oss-php-sdk-test/upload-test-object-name.txt', file_get_contents(__FILE__)); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientRestoreObjectTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientRestoreObjectTest.php new file mode 100644 index 0000000..cc1412f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientRestoreObjectTest.php @@ -0,0 +1,96 @@ +ossClient->putObject($this->iaBucket, $object,'testcontent'); + try{ + $this->ossClient->restoreObject($this->iaBucket, $object); + $this->assertTrue(false); + }catch (OssException $e){ + $this->assertEquals('400', $e->getHTTPStatus()); + $this->assertEquals('OperationNotSupported', $e->getErrorCode()); + } + } + + public function testNullObjectRestoreObject() + { + $object = 'null-object'; + + try{ + $this->ossClient->restoreObject($this->bucket, $object); + $this->assertTrue(false); + }catch (OssException $e){ + $this->assertEquals('404', $e->getHTTPStatus()); + } + } + + public function testArchiveRestoreObject() + { + $object = 'storage-object'; + + $this->ossClient->putObject($this->archiveBucket, $object,'testcontent'); + try{ + $this->ossClient->getObject($this->archiveBucket, $object); + $this->assertTrue(false); + }catch (OssException $e){ + $this->assertEquals('403', $e->getHTTPStatus()); + $this->assertEquals('InvalidObjectState', $e->getErrorCode()); + } + $result = $this->ossClient->restoreObject($this->archiveBucket, $object); + common::waitMetaSync(); + $this->assertEquals('202', $result['info']['http_code']); + + try{ + $this->ossClient->restoreObject($this->archiveBucket, $object); + }catch(OssException $e){ + $this->assertEquals('409', $e->getHTTPStatus()); + $this->assertEquals('RestoreAlreadyInProgress', $e->getErrorCode()); + } + } + + public function setUp() + { + parent::setUp(); + + $this->iaBucket = 'ia-' . $this->bucket; + $this->archiveBucket = 'archive-' . $this->bucket; + $options = array( + OssClient::OSS_STORAGE => OssClient::OSS_STORAGE_IA + ); + + $this->ossClient->createBucket($this->iaBucket, OssClient::OSS_ACL_TYPE_PRIVATE, $options); + + $options = array( + OssClient::OSS_STORAGE => OssClient::OSS_STORAGE_ARCHIVE + ); + + $this->ossClient->createBucket($this->archiveBucket, OssClient::OSS_ACL_TYPE_PRIVATE, $options); + } + + public function tearDown() + { + parent::tearDown(); + + $object = 'storage-object'; + + $this->ossClient->deleteObject($this->iaBucket, $object); + $this->ossClient->deleteObject($this->archiveBucket, $object); + $this->ossClient->deleteBucket($this->iaBucket); + $this->ossClient->deleteBucket($this->archiveBucket); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientSignatureTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientSignatureTest.php new file mode 100644 index 0000000..109121d --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientSignatureTest.php @@ -0,0 +1,111 @@ +ossClient->putObject($this->bucket, $object, file_get_contents(__FILE__)); + $timeout = 3600; + try { + $signedUrl = $this->ossClient->signUrl($this->bucket, $object, $timeout); + } catch (OssException $e) { + $this->assertFalse(true); + } + + $request = new RequestCore($signedUrl); + $request->set_method('GET'); + $request->add_header('Content-Type', ''); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), $request->get_response_body(), $request->get_response_code()); + $this->assertEquals(file_get_contents(__FILE__), $res->body); + } + + public function testGetSignedUrlForPuttingObject() + { + $object = "a.file"; + $timeout = 3600; + try { + $signedUrl = $this->ossClient->signUrl($this->bucket, $object, $timeout, "PUT"); + $content = file_get_contents(__FILE__); + $request = new RequestCore($signedUrl); + $request->set_method('PUT'); + $request->add_header('Content-Type', ''); + $request->add_header('Content-Length', strlen($content)); + $request->set_body($content); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), + $request->get_response_body(), $request->get_response_code()); + $this->assertTrue($res->isOK()); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testGetSignedUrlForPuttingObjectFromFile() + { + $file = __FILE__; + $object = "a.file"; + $timeout = 3600; + $options = array('Content-Type' => 'txt'); + try { + $signedUrl = $this->ossClient->signUrl($this->bucket, $object, $timeout, "PUT", $options); + $request = new RequestCore($signedUrl); + $request->set_method('PUT'); + $request->add_header('Content-Type', 'txt'); + $request->set_read_file($file); + $request->set_read_stream_size(filesize($file)); + $request->send_request(); + $res = new ResponseCore($request->get_response_header(), + $request->get_response_body(), $request->get_response_code()); + $this->assertTrue($res->isOK()); + } catch (OssException $e) { + $this->assertFalse(true); + } + + } + + public function tearDown() + { + $this->ossClient->deleteObject($this->bucket, "a.file"); + parent::tearDown(); + } + + public function setUp() + { + parent::setUp(); + /** + * 上传本地变量到bucket + */ + $object = "a.file"; + $content = file_get_contents(__FILE__); + $options = array( + OssClient::OSS_LENGTH => strlen($content), + OssClient::OSS_HEADERS => array( + 'Expires' => 'Fri, 28 Feb 2020 05:38:42 GMT', + 'Cache-Control' => 'no-cache', + 'Content-Disposition' => 'attachment;filename=oss_download.log', + 'Content-Encoding' => 'utf-8', + 'Content-Language' => 'zh-CN', + 'x-oss-server-side-encryption' => 'AES256', + 'x-oss-meta-self-define-title' => 'user define meta info', + ), + ); + + try { + $this->ossClient->putObject($this->bucket, $object, $content, $options); + } catch (OssException $e) { + $this->assertFalse(true); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientTest.php new file mode 100644 index 0000000..f92b346 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssClientTest.php @@ -0,0 +1,216 @@ +assertFalse($ossClient->isUseSSL()); + $ossClient->setUseSSL(true); + $this->assertTrue($ossClient->isUseSSL()); + $this->assertTrue(true); + $this->assertEquals(3, $ossClient->getMaxRetries()); + $ossClient->setMaxTries(4); + $this->assertEquals(4, $ossClient->getMaxRetries()); + $ossClient->setTimeout(10); + $ossClient->setConnectTimeout(20); + } catch (OssException $e) { + assertFalse(true); + } + } + + public function testConstrunct2() + { + try { + $ossClient = new OssClient('id', "", 'http://oss-cn-hangzhou.aliyuncs.com'); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals("access key secret is empty", $e->getMessage()); + } + } + + public function testConstrunct3() + { + try { + $ossClient = new OssClient("", 'key', 'http://oss-cn-hangzhou.aliyuncs.com'); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals("access key id is empty", $e->getMessage()); + } + } + + public function testConstrunct4() + { + try { + $ossClient = new OssClient('id', 'key', ""); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('endpoint is empty', $e->getMessage()); + } + } + + public function testConstrunct5() + { + try { + $ossClient = new OssClient('id', 'key', "123.123.123.1"); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testConstrunct6() + { + try { + $ossClient = new OssClient('id', 'key', "https://123.123.123.1"); + $this->assertTrue($ossClient->isUseSSL()); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testConstrunct7() + { + try { + $ossClient = new OssClient('id', 'key', "http://123.123.123.1"); + $this->assertFalse($ossClient->isUseSSL()); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + public function testConstrunct8() + { + try { + $ossClient = new OssClient('id', 'key', "http://123.123.123.1", true); + $ossClient->listBuckets(); + $this->assertFalse(true); + } catch (OssException $e) { + + } + } + + public function testConstrunct9() + { + try { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false); + $ossClient->listBuckets(); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testSupportPutEmptyObject() + { + try { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $bucket = getenv('OSS_BUCKET'); + $ossClient = new OssClient($accessKeyId, $accessKeySecret , $endpoint, false); + $ossClient->putObject($bucket,'test_emptybody',''); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testCreateObjectDir() + { + try { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $bucket = getenv('OSS_BUCKET'); + $object='test-dir'; + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false); + $ossClient->createObjectDir($bucket,$object); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testGetBucketCors() + { + try { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $bucket = getenv('OSS_BUCKET'); + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false); + $ossClient->getBucketCors($bucket); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testGetBucketCname() + { + try { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $bucket = getenv('OSS_BUCKET'); + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false); + $ossClient->getBucketCname($bucket); + } catch (OssException $e) { + $this->assertFalse(true); + } + } + + public function testProxySupport() + { + $accessKeyId = ' ' . getenv('OSS_ACCESS_KEY_ID') . ' '; + $accessKeySecret = ' ' . getenv('OSS_ACCESS_KEY_SECRET') . ' '; + $endpoint = ' ' . getenv('OSS_ENDPOINT') . '/ '; + $bucket = getenv('OSS_BUCKET') . '-proxy'; + $requestProxy = getenv('OSS_PROXY'); + $key = 'test-proxy-srv-object'; + $content = 'test-content'; + $proxys = parse_url($requestProxy); + + $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, null, $requestProxy); + + $result = $ossClient->createBucket($bucket); + $this->checkProxy($result, $proxys); + + $result = $ossClient->putObject($bucket, $key, $content); + $this->checkProxy($result, $proxys); + $result = $ossClient->getObject($bucket, $key); + $this->assertEquals($content, $result); + + // list object + $objectListInfo = $ossClient->listObjects($bucket); + $objectList = $objectListInfo->getObjectList(); + $this->assertNotNull($objectList); + $this->assertTrue(is_array($objectList)); + $objects = array(); + foreach ($objectList as $value) { + $objects[] = $value->getKey(); + } + $this->assertEquals(1, count($objects)); + $this->assertTrue(in_array($key, $objects)); + + $result = $ossClient->deleteObject($bucket, $key); + $this->checkProxy($result,$proxys); + + $result = $ossClient->deleteBucket($bucket); + $this->checkProxy($result, $proxys); + } + + private function checkProxy($result, $proxys) + { + $this->assertEquals($result['info']['primary_ip'], $proxys['host']); + $this->assertEquals($result['info']['primary_port'], $proxys['port']); + $this->assertTrue(array_key_exists('via', $result)); + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssExceptionTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssExceptionTest.php new file mode 100644 index 0000000..4a418d5 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssExceptionTest.php @@ -0,0 +1,19 @@ +assertTrue(false); + } catch (OssException $e) { + $this->assertNotNull($e); + $this->assertEquals($e->getMessage(), "ERR"); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssUtilTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssUtilTest.php new file mode 100644 index 0000000..adf6457 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/OssUtilTest.php @@ -0,0 +1,225 @@ +assertEquals(OssUtil::chkChinese("hello,world"), 0); + $str = '你好,这里是卖咖啡!'; + $strGBK = OssUtil::encodePath($str); + $this->assertEquals(OssUtil::chkChinese($str), 1); + $this->assertEquals(OssUtil::chkChinese($strGBK), 1); + } + + public function testIsGB2312() + { + $str = '你好,这里是卖咖啡!'; + $this->assertFalse(OssUtil::isGb2312($str)); + } + + public function testCheckChar() + { + $str = '你好,这里是卖咖啡!'; + $this->assertFalse(OssUtil::checkChar($str)); + $this->assertTrue(OssUtil::checkChar(iconv("UTF-8", "GB2312//IGNORE", $str))); + } + + public function testIsIpFormat() + { + $this->assertTrue(OssUtil::isIPFormat("10.101.160.147")); + $this->assertTrue(OssUtil::isIPFormat("12.12.12.34")); + $this->assertTrue(OssUtil::isIPFormat("12.12.12.12")); + $this->assertTrue(OssUtil::isIPFormat("255.255.255.255")); + $this->assertTrue(OssUtil::isIPFormat("0.1.1.1")); + $this->assertFalse(OssUtil::isIPFormat("0.1.1.x")); + $this->assertFalse(OssUtil::isIPFormat("0.1.1.256")); + $this->assertFalse(OssUtil::isIPFormat("256.1.1.1")); + $this->assertFalse(OssUtil::isIPFormat("0.1.1.0.1")); + $this->assertTrue(OssUtil::isIPFormat("10.10.10.10:123")); + } + + public function testToQueryString() + { + $option = array("a" => "b"); + $this->assertEquals('a=b', OssUtil::toQueryString($option)); + } + + public function testSReplace() + { + $str = "<>&'\""; + $this->assertEquals("&lt;&gt;&'"", OssUtil::sReplace($str)); + } + + public function testCheckChinese() + { + $str = '你好,这里是卖咖啡!'; + $this->assertEquals(OssUtil::chkChinese($str), 1); + if (OssUtil::isWin()) { + $strGB = OssUtil::encodePath($str); + $this->assertEquals($str, iconv("GB2312", "UTF-8", $strGB)); + } + } + + public function testValidateOption() + { + $option = 'string'; + + try { + OssUtil::validateOptions($option); + $this->assertFalse(true); + } catch (OssException $e) { + $this->assertEquals("string:option must be array", $e->getMessage()); + } + + $option = null; + + try { + OssUtil::validateOptions($option); + $this->assertTrue(true); + } catch (OssException $e) { + $this->assertFalse(true); + } + + } + + public function testCreateDeleteObjectsXmlBody() + { + $xml = <<trueobj1 +BBBB; + $a = array('obj1'); + $this->assertEquals($xml, $this->cleanXml(OssUtil::createDeleteObjectsXmlBody($a, 'true'))); + } + + public function testCreateCompleteMultipartUploadXmlBody() + { + $xml = <<2xx +BBBB; + $a = array(array("PartNumber" => 2, "ETag" => "xx")); + $this->assertEquals($this->cleanXml(OssUtil::createCompleteMultipartUploadXmlBody($a)), $xml); + } + + public function testCreateBucketXmlBody() + { + $xml = <<Standard +BBBB; + $storageClass ="Standard"; + $this->assertEquals($this->cleanXml(OssUtil::createBucketXmlBody($storageClass)), $xml); + } + + public function testValidateBucket() + { + $this->assertTrue(OssUtil::validateBucket("xxx")); + $this->assertFalse(OssUtil::validateBucket("XXXqwe123")); + $this->assertFalse(OssUtil::validateBucket("XX")); + $this->assertFalse(OssUtil::validateBucket("/X")); + $this->assertFalse(OssUtil::validateBucket("")); + } + + public function testValidateObject() + { + $this->assertTrue(OssUtil::validateObject("xxx")); + $this->assertTrue(OssUtil::validateObject("xxx23")); + $this->assertTrue(OssUtil::validateObject("12321-xxx")); + $this->assertTrue(OssUtil::validateObject("x")); + $this->assertFalse(OssUtil::validateObject("/aa")); + $this->assertFalse(OssUtil::validateObject("\\aa")); + $this->assertFalse(OssUtil::validateObject("")); + } + + public function testStartWith() + { + $this->assertTrue(OssUtil::startsWith("xxab", "xx"), true); + } + + public function testReadDir() + { + $list = OssUtil::readDir("./src", ".|..|.svn|.git", true); + $this->assertNotNull($list); + } + + public function testIsWin() + { + //$this->assertTrue(OssUtil::isWin()); + } + + public function testGetMd5SumForFile() + { + $this->assertEquals(OssUtil::getMd5SumForFile(__FILE__, 0, filesize(__FILE__) - 1), base64_encode(md5(file_get_contents(__FILE__), true))); + } + + public function testGenerateFile() + { + $path = __DIR__ . DIRECTORY_SEPARATOR . "generatedFile.txt"; + OssUtil::generateFile($path, 1024 * 1024); + $this->assertEquals(filesize($path), 1024 * 1024); + unlink($path); + } + + public function testThrowOssExceptionWithMessageIfEmpty() + { + $null = null; + try { + OssUtil::throwOssExceptionWithMessageIfEmpty($null, "xx"); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('xx', $e->getMessage()); + } + } + + public function testThrowOssExceptionWithMessageIfEmpty2() + { + $null = ""; + try { + OssUtil::throwOssExceptionWithMessageIfEmpty($null, "xx"); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('xx', $e->getMessage()); + } + } + + public function testValidContent() + { + $null = ""; + try { + OssUtil::validateContent($null); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('http body content is invalid', $e->getMessage()); + } + + $notnull = "x"; + try { + OssUtil::validateContent($notnull); + $this->assertTrue(true); + } catch (OssException $e) { + $this->assertEquals('http body content is invalid', $e->getMessage()); + } + } + + public function testThrowOssExceptionWithMessageIfEmpty3() + { + $null = "xx"; + try { + OssUtil::throwOssExceptionWithMessageIfEmpty($null, "xx"); + $this->assertTrue(True); + } catch (OssException $e) { + $this->assertTrue(false); + } + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } + +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/PutSetDeleteResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/PutSetDeleteResultTest.php new file mode 100644 index 0000000..b298e44 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/PutSetDeleteResultTest.php @@ -0,0 +1,66 @@ +assertFalse(true); + } catch (OssException $e) { + $this->assertEquals('raw response is null', $e->getMessage()); + } + } + + public function testOkResponse() + { + $header= array( + 'x-oss-request-id' => '582AA51E004C4550BD27E0E4', + 'etag' => '595FA1EA77945233921DF12427F9C7CE', + 'content-md5' => 'WV+h6neUUjOSHfEkJ/nHzg==', + 'info' => array( + 'http_code' => '200', + 'method' => 'PUT' + ), + ); + $response = new ResponseCore($header, "this is a mock body, just for test", 200); + $result = new PutSetDeleteResult($response); + $data = $result->getData(); + $this->assertTrue($result->isOK()); + $this->assertEquals("this is a mock body, just for test", $data['body']); + $this->assertEquals('582AA51E004C4550BD27E0E4', $data['x-oss-request-id']); + $this->assertEquals('595FA1EA77945233921DF12427F9C7CE', $data['etag']); + $this->assertEquals('WV+h6neUUjOSHfEkJ/nHzg==', $data['content-md5']); + $this->assertEquals('200', $data['info']['http_code']); + $this->assertEquals('PUT', $data['info']['method']); + } + + public function testFailResponse() + { + $response = new ResponseCore(array(), "", 301); + try { + new PutSetDeleteResult($response); + $this->assertFalse(true); + } catch (OssException $e) { + + } + } + + public function setUp() + { + + } + + public function tearDown() + { + + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/RefererConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/RefererConfigTest.php new file mode 100644 index 0000000..8360a24 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/RefererConfigTest.php @@ -0,0 +1,54 @@ + + +true + +http://www.aliyun.com +https://www.aliyun.com +http://www.*.com +https://www.?.aliyuncs.com + + +BBBB; + + private $validXml2 = << + +true + +http://www.aliyun.com + + +BBBB; + + public function testParseValidXml() + { + $refererConfig = new RefererConfig(); + $refererConfig->parseFromXml($this->validXml); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($refererConfig->serializeToXml())); + } + + public function testParseValidXml2() + { + $refererConfig = new RefererConfig(); + $refererConfig->parseFromXml($this->validXml2); + $this->assertEquals(true, $refererConfig->isAllowEmptyReferer()); + $this->assertEquals(1, count($refererConfig->getRefererList())); + $this->assertEquals($this->cleanXml($this->validXml2), $this->cleanXml(strval($refererConfig))); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/StorageCapacityTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/StorageCapacityTest.php new file mode 100644 index 0000000..4562da7 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/StorageCapacityTest.php @@ -0,0 +1,59 @@ + + + 1 + +BBBB; + + private $validXml = << + + 1 + +BBBB; + + public function testParseInValidXml() + { + $response = new ResponseCore(array(), $this->inValidXml, 300); + try { + new GetStorageCapacityResult($response); + $this->assertTrue(false); + } catch (OssException $e) {} + } + + public function testParseEmptyXml() + { + $response = new ResponseCore(array(), "", 300); + try { + new GetStorageCapacityResult($response); + $this->assertTrue(false); + } catch (OssException $e) {} + } + + public function testParseValidXml() + { + $response = new ResponseCore(array(), $this->validXml, 200); + $result = new GetStorageCapacityResult($response); + $this->assertEquals($result->getData(), 1); + } + + public function testSerializeToXml() + { + $xml = "\n1\n"; + + $storageCapacityConfig = new StorageCapacityConfig(1); + $content = $storageCapacityConfig->serializeToXml(); + $this->assertEquals($content, $xml); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/SymlinkTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/SymlinkTest.php new file mode 100644 index 0000000..d257c94 --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/SymlinkTest.php @@ -0,0 +1,74 @@ +ossClient ->putObject($bucket, $object, 'test_content'); + $this->ossClient->putSymlink($bucket, $symlink, $object); + $result = $this->ossClient->getObject($bucket, $symlink); + $this->assertEquals('test_content', $result); + + $this->ossClient ->putObject($bucket, $special_object, 'test_content'); + $this->ossClient->putSymlink($bucket, $symlink, $special_object); + $result = $this->ossClient->getObject($bucket, $symlink); + $this->assertEquals('test_content', $result); + } + + public function testGetSymlink() + { + $bucket = getenv('OSS_BUCKET'); + $symlink = 'test-link'; + $object = 'exist_object^$#!~'; + + $result = $this->ossClient->getSymlink($bucket, $symlink); + $this->assertEquals($result[OssClient::OSS_SYMLINK_TARGET], $object); + $this->assertEquals('200', $result[OssClient::OSS_INFO][OssClient::OSS_HTTP_CODE]); + $this->assertTrue(isset($result[OssClient::OSS_ETAG])); + $this->assertTrue(isset($result[OssClient::OSS_REQUEST_ID])); + } + + public function testPutNullSymlink() + { + $bucket = getenv('OSS_BUCKET'); + $symlink = 'null-link'; + $object_not_exist = 'not_exist_object+$#!b不'; + $this->ossClient->putSymlink($bucket, $symlink, $object_not_exist); + + try{ + $this->ossClient->getObject($bucket, $symlink); + $this->assertTrue(false); + }catch (OssException $e){ + $this->assertEquals('The symlink target object does not exist', $e->getErrorMessage()); + } + } + + public function testGetNullSymlink() + { + $bucket = getenv('OSS_BUCKET'); + $symlink = 'null-link-new'; + + try{ + $result = $this->ossClient->getSymlink($bucket, $symlink); + $this->assertTrue(false); + }catch (OssException $e){ + $this->assertEquals('The specified key does not exist.', $e->getErrorMessage()); + } + } +} + + diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/TestOssClientBase.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/TestOssClientBase.php new file mode 100644 index 0000000..4abd31f --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/TestOssClientBase.php @@ -0,0 +1,51 @@ +bucket = Common::getBucketName() . rand(100000, 999999); + $this->ossClient = Common::getOssClient(); + $this->ossClient->createBucket($this->bucket); + Common::waitMetaSync(); + } + + public function tearDown() + { + if (!$this->ossClient->doesBucketExist($this->bucket)) { + return; + } + + $objects = $this->ossClient->listObjects( + $this->bucket, array('max-keys' => 1000, 'delimiter' => ''))->getObjectList(); + $keys = array(); + foreach ($objects as $obj) { + $keys[] = $obj->getKey(); + } + if (count($keys) > 0) { + $this->ossClient->deleteObjects($this->bucket, $keys); + } + $uploads = $this->ossClient->listMultipartUploads($this->bucket)->getUploads(); + foreach ($uploads as $up) { + $this->ossClient->abortMultipartUpload($this->bucket, $up->getKey(), $up->getUploadId()); + } + + $this->ossClient->deleteBucket($this->bucket); + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/UploadPartResultTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/UploadPartResultTest.php new file mode 100644 index 0000000..e4789ef --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/UploadPartResultTest.php @@ -0,0 +1,33 @@ + '7265F4D211B56873A381D321F586E4A9'); + private $invalidHeader = array(); + + public function testParseValidHeader() + { + $response = new ResponseCore($this->validHeader, "", 200); + $result = new UploadPartResult($response); + $eTag = $result->getData(); + $this->assertEquals('7265F4D211B56873A381D321F586E4A9', $eTag); + } + + public function testParseInvalidHeader() + { + $response = new ResponseCore($this->invalidHeader, "", 200); + try { + new UploadPartResult($response); + $this->assertTrue(false); + } catch (OssException $e) { + $this->assertEquals('cannot get ETag', $e->getMessage()); + } + } +} diff --git a/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/WebsiteConfigTest.php b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/WebsiteConfigTest.php new file mode 100644 index 0000000..2ec0fcb --- /dev/null +++ b/source/vendor/aliyuncs/oss-sdk-php/tests/OSS/Tests/WebsiteConfigTest.php @@ -0,0 +1,56 @@ + + + +index.html + + +errorDocument.html + + +BBBB; + + private $nullXml = << +BBBB; + private $nullXml2 = << +BBBB; + + public function testParseValidXml() + { + $websiteConfig = new WebsiteConfig("index"); + $websiteConfig->parseFromXml($this->validXml); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($websiteConfig->serializeToXml())); + } + + public function testParsenullXml() + { + $websiteConfig = new WebsiteConfig(); + $websiteConfig->parseFromXml($this->nullXml); + $this->assertTrue($this->cleanXml($this->nullXml) === $this->cleanXml($websiteConfig->serializeToXml()) || + $this->cleanXml($this->nullXml2) === $this->cleanXml($websiteConfig->serializeToXml())); + } + + public function testWebsiteConstruct() + { + $websiteConfig = new WebsiteConfig("index.html", "errorDocument.html"); + $this->assertEquals('index.html', $websiteConfig->getIndexDocument()); + $this->assertEquals('errorDocument.html', $websiteConfig->getErrorDocument()); + $this->assertEquals($this->cleanXml($this->validXml), $this->cleanXml($websiteConfig->serializeToXml())); + } + + private function cleanXml($xml) + { + return str_replace("\n", "", str_replace("\r", "", $xml)); + } +} diff --git a/source/vendor/autoload.php b/source/vendor/autoload.php new file mode 100644 index 0000000..845e1f4 --- /dev/null +++ b/source/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/source/vendor/composer/LICENSE b/source/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/source/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/source/vendor/composer/autoload_classmap.php b/source/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..7a91153 --- /dev/null +++ b/source/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/qiniu/php-sdk/src/Qiniu/functions.php', +); diff --git a/source/vendor/composer/autoload_namespaces.php b/source/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..3fde43e --- /dev/null +++ b/source/vendor/composer/autoload_namespaces.php @@ -0,0 +1,12 @@ + array($vendorDir . '/qcloud/cos-sdk-v5/src'), + 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), + 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), +); diff --git a/source/vendor/composer/autoload_psr4.php b/source/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000..9d3e5b2 --- /dev/null +++ b/source/vendor/composer/autoload_psr4.php @@ -0,0 +1,18 @@ + array($vendorDir . '/topthink/think-installer/src'), + 'think\\' => array($baseDir . '/thinkphp/library/think'), + 'app\\' => array($baseDir . '/application'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Qiniu\\' => array($vendorDir . '/qiniu/php-sdk/src/Qiniu'), + 'OSS\\' => array($vendorDir . '/aliyuncs/oss-sdk-php/src/OSS'), + 'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'), + 'Lvht\\' => array($vendorDir . '/lvht/geohash/src'), + 'Grafika\\' => array($vendorDir . '/kosinix/grafika/src/Grafika'), +); diff --git a/source/vendor/composer/autoload_real.php b/source/vendor/composer/autoload_real.php new file mode 100644 index 0000000..155a29d --- /dev/null +++ b/source/vendor/composer/autoload_real.php @@ -0,0 +1,70 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire34a41e2841af1a67f3ddef099fc7b348($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire34a41e2841af1a67f3ddef099fc7b348($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/source/vendor/composer/autoload_static.php b/source/vendor/composer/autoload_static.php new file mode 100644 index 0000000..5483b0d --- /dev/null +++ b/source/vendor/composer/autoload_static.php @@ -0,0 +1,118 @@ + __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu/functions.php', + ); + + public static $prefixLengthsPsr4 = array ( + 't' => + array ( + 'think\\composer\\' => 15, + 'think\\' => 6, + ), + 'a' => + array ( + 'app\\' => 4, + ), + 'S' => + array ( + 'Symfony\\Component\\EventDispatcher\\' => 34, + ), + 'Q' => + array ( + 'Qiniu\\' => 6, + ), + 'O' => + array ( + 'OSS\\' => 4, + ), + 'M' => + array ( + 'MyCLabs\\Enum\\' => 13, + ), + 'L' => + array ( + 'Lvht\\' => 5, + ), + 'G' => + array ( + 'Grafika\\' => 8, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'think\\composer\\' => + array ( + 0 => __DIR__ . '/..' . '/topthink/think-installer/src', + ), + 'think\\' => + array ( + 0 => __DIR__ . '/../..' . '/thinkphp/library/think', + ), + 'app\\' => + array ( + 0 => __DIR__ . '/../..' . '/application', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Qiniu\\' => + array ( + 0 => __DIR__ . '/..' . '/qiniu/php-sdk/src/Qiniu', + ), + 'OSS\\' => + array ( + 0 => __DIR__ . '/..' . '/aliyuncs/oss-sdk-php/src/OSS', + ), + 'MyCLabs\\Enum\\' => + array ( + 0 => __DIR__ . '/..' . '/myclabs/php-enum/src', + ), + 'Lvht\\' => + array ( + 0 => __DIR__ . '/..' . '/lvht/geohash/src', + ), + 'Grafika\\' => + array ( + 0 => __DIR__ . '/..' . '/kosinix/grafika/src/Grafika', + ), + ); + + public static $prefixesPsr0 = array ( + 'Q' => + array ( + 'Qcloud\\Cos\\' => + array ( + 0 => __DIR__ . '/..' . '/qcloud/cos-sdk-v5/src', + ), + ), + 'G' => + array ( + 'Guzzle\\Tests' => + array ( + 0 => __DIR__ . '/..' . '/guzzle/guzzle/tests', + ), + 'Guzzle' => + array ( + 0 => __DIR__ . '/..' . '/guzzle/guzzle/src', + ), + ), + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit34a41e2841af1a67f3ddef099fc7b348::$prefixesPsr0; + + }, null, ClassLoader::class); + } +} diff --git a/source/vendor/composer/installed.json b/source/vendor/composer/installed.json new file mode 100644 index 0000000..fd68367 --- /dev/null +++ b/source/vendor/composer/installed.json @@ -0,0 +1,534 @@ +[ + { + "name": "aliyuncs/oss-sdk-php", + "version": "v2.3.0", + "version_normalized": "2.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/aliyun/aliyun-oss-php-sdk.git", + "reference": "e69f57916678458642ac9d2fd341ae78a56996c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/e69f57916678458642ac9d2fd341ae78a56996c8", + "reference": "e69f57916678458642ac9d2fd341ae78a56996c8", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "~1.0" + }, + "time": "2018-01-08T06:59:35+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "OSS\\": "src/OSS" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aliyuncs", + "homepage": "http://www.aliyun.com" + } + ], + "description": "Aliyun OSS SDK for PHP", + "homepage": "http://www.aliyun.com/product/oss/" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "version_normalized": "3.9.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "time": "2015-03-18T18:23:50+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "abandoned": "guzzlehttp/guzzle" + }, + { + "name": "kosinix/grafika", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/kosinix/grafika.git", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kosinix/grafika/zipball/211f61fc334b8b36616b23e8af7c5727971d96ee", + "reference": "211f61fc334b8b36616b23e8af7c5727971d96ee", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "time": "2017-06-20T03:13:49+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Grafika\\": "src/Grafika" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT", + "GPL-2.0+" + ], + "authors": [ + { + "name": "Nico Amarilla", + "homepage": "https://www.kosinix.com" + } + ], + "description": "An image manipulation library for PHP.", + "homepage": "http://kosinix.github.io/grafika", + "keywords": [ + "grafika" + ] + }, + { + "name": "lvht/geohash", + "version": "v1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/lvht/geohash.git", + "reference": "bbba3e1b487f0ec2e5e666c1bc9d1d4277990a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lvht/geohash/zipball/bbba3e1b487f0ec2e5e666c1bc9d1d4277990a29", + "reference": "bbba3e1b487f0ec2e5e666c1bc9d1d4277990a29", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "time": "2017-08-24T11:05:30+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Lvht\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "吕海涛", + "email": "git@lvht.net", + "homepage": "https://github.com/lvht" + } + ], + "description": "geohash like python-geohash", + "homepage": "http://github.com/lvht/geohash", + "keywords": [ + "geohash" + ] + }, + { + "name": "myclabs/php-enum", + "version": "1.6.4", + "version_normalized": "1.6.4.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "550d2334d77f91b0816a5cbd6965272fe20146b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/550d2334d77f91b0816a5cbd6965272fe20146b8", + "reference": "550d2334d77f91b0816a5cbd6965272fe20146b8", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "squizlabs/php_codesniffer": "1.*" + }, + "time": "2018-10-30T14:36:18+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ] + }, + { + "name": "qcloud/cos-sdk-v5", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/tencentyun/cos-php-sdk-v5.git", + "reference": "989c087a5aaf9b5df020b1d2633644e7c9f694e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tencentyun/cos-php-sdk-v5/zipball/989c087a5aaf9b5df020b1d2633644e7c9f694e0", + "reference": "989c087a5aaf9b5df020b1d2633644e7c9f694e0", + "shasum": "" + }, + "require": { + "guzzle/guzzle": "~3.7", + "php": ">=5.3.0" + }, + "time": "2018-11-27T13:31:54+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Qcloud\\Cos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "yaozongyou", + "email": "yaozongyou@vip.qq.com" + }, + { + "name": "lewzylu", + "email": "327874225@qq.com" + } + ], + "description": "PHP SDK for QCloud COS", + "keywords": [ + "cos", + "php", + "qcloud" + ] + }, + { + "name": "qiniu/php-sdk", + "version": "v7.2.7", + "version_normalized": "7.2.7.0", + "source": { + "type": "git", + "url": "https://github.com/qiniu/php-sdk.git", + "reference": "88d11a5857ebc6871204e9be6ceec54bf5f381e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/88d11a5857ebc6871204e9be6ceec54bf5f381e6", + "reference": "88d11a5857ebc6871204e9be6ceec54bf5f381e6", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.3" + }, + "time": "2018-11-06T13:34:32+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Qiniu\\": "src/Qiniu" + }, + "files": [ + "src/Qiniu/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Qiniu", + "email": "sdk@qiniu.com", + "homepage": "http://www.qiniu.com" + } + ], + "description": "Qiniu Resource (Cloud) Storage SDK for PHP", + "homepage": "http://developer.qiniu.com/", + "keywords": [ + "cloud", + "qiniu", + "sdk", + "storage" + ] + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.8.49", + "version_normalized": "2.8.49.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0", + "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2018-11-21T14:20:20+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "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": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com" + }, + { + "name": "topthink/framework", + "version": "v5.0.24", + "version_normalized": "5.0.24.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/framework.git", + "reference": "c255c22b2f5fa30f320ecf6c1d29f7740eb3e8be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/framework/zipball/c255c22b2f5fa30f320ecf6c1d29f7740eb3e8be", + "reference": "c255c22b2f5fa30f320ecf6c1d29f7740eb3e8be", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "topthink/think-installer": "~1.0" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.0", + "mikey179/vfsstream": "~1.6", + "phpdocumentor/reflection-docblock": "^2.0", + "phploc/phploc": "2.*", + "phpunit/phpunit": "4.8.*", + "sebastian/phpcpd": "2.*" + }, + "time": "2019-01-11T08:04:58+00:00", + "type": "think-framework", + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\": "library/think" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "description": "the new thinkphp framework", + "homepage": "http://thinkphp.cn/", + "keywords": [ + "framework", + "orm", + "thinkphp" + ] + }, + { + "name": "topthink/think-installer", + "version": "v1.0.12", + "version_normalized": "1.0.12.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-installer.git", + "reference": "1be326e68f63de4e95977ed50f46ae75f017556d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-installer/zipball/1be326e68f63de4e95977ed50f46ae75f017556d", + "reference": "1be326e68f63de4e95977ed50f46ae75f017556d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev" + }, + "time": "2017-05-27T06:58:09+00:00", + "type": "composer-plugin", + "extra": { + "class": "think\\composer\\Plugin" + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "think\\composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ] + } +] diff --git a/source/vendor/guzzle/guzzle/.gitignore b/source/vendor/guzzle/guzzle/.gitignore new file mode 100644 index 0000000..893035d --- /dev/null +++ b/source/vendor/guzzle/guzzle/.gitignore @@ -0,0 +1,27 @@ +# Ingore common cruft +.DS_STORE +coverage +.idea + +# Ignore binary files +guzzle.phar +guzzle-min.phar + +# Ignore potentially sensitive phpunit file +phpunit.xml + +# Ignore composer generated files +composer.phar +composer.lock +composer-test.lock +vendor/ + +# Ignore build files +build/ +phing/build.properties + +# Ignore subsplit working directory +.subsplit + +docs/_build +docs/*.pyc diff --git a/source/vendor/guzzle/guzzle/.travis.yml b/source/vendor/guzzle/guzzle/.travis.yml new file mode 100644 index 0000000..209e05c --- /dev/null +++ b/source/vendor/guzzle/guzzle/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl --version + - pecl install uri_template-beta || echo "pecl uri_template not available" + - composer self-update + - composer install --no-interaction --prefer-source --dev + - ~/.nvm/nvm.sh install v0.6.14 + +script: composer test diff --git a/source/vendor/guzzle/guzzle/CHANGELOG.md b/source/vendor/guzzle/guzzle/CHANGELOG.md new file mode 100644 index 0000000..f0dc544 --- /dev/null +++ b/source/vendor/guzzle/guzzle/CHANGELOG.md @@ -0,0 +1,751 @@ +# CHANGELOG + +## 3.9.3 - 2015-03-18 + +* Ensuring Content-Length is not stripped from a request when it is `0`. +* Added more information to stream wrapper exceptions. +* Message parser will no longer throw warnings for malformed messages. +* Giving a valid cache TTL when max-age is 0. + +## 3.9.2 - 2014-09-10 + +* Retrying "Connection died, retrying a fresh connect" curl errors. +* Automatically extracting the cacert from the phar in client constructor. +* Added EntityBody support for OPTIONS requests. + +## 3.9.1 - 2014-05-07 + +* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. +* Added a fix to the stream checksum function so that when the first read + returns a falsey value, it still continues to consume the stream until EOF. + +## 3.9.0 - 2014-04-23 + +* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value + with no trailing "=". See dc1d824277. +* No longer performing an MD5 check on the cacert each time the phar is used, + but rather copying the cacert to the temp directory. +* `"0"` can now be added as a URL path +* Deleting cookies that are set to empty +* If-Modified-Since is no longer unnecessarily added to the CachePlugin +* Cookie path matching now follows RFC 6265 s5.1.4 +* Updated service descriptions are now added to a service client's composite + factory. +* MockPlugin now throws an exception if the queue is empty. +* Properly parsing URLs that start with "http" but are not absolute +* Added the ability to configure the curl_multi_select timeout setting +* OAuth parameters are now sorted using lexicographical byte value ordering +* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin + +## 3.8.1 -2014-01-28 + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +## 3.8.0 - 2013-12-05 + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +## 3.7.4 - 2013-10-02 + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +## 3.7.3 - 2013-09-08 + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +## 3.7.2 - 2013-08-02 + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +## 3.7.1 - 2013-07-05 + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +## 3.7.0 - 2013-06-10 + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +## 3.6.0 - 2013-05-29 + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +## 3.5.0 - 2013-05-13 + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +## 3.4.3 - 2013-04-30 + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +## 3.4.2 - 2013-04-29 + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +## 3.4.1 - 2013-04-16 + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +## 3.4.0 - 2013-04-11 + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +## 3.3.1 - 2013-03-10 + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +## 3.3.0 - 2013-03-03 + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +## 3.2.0 - 2013-02-14 + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +## 3.1.2 - 2013-01-27 + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +## 3.1.1 - 2013-01-20 + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +## 3.1.0 - 2013-01-12 + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +## 3.0.7 - 2012-12-19 + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +## 3.0.6 - 2012-12-09 + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +## 3.0.5 - 2012-11-18 + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +## 3.0.4 - 2012-11-11 + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +## 3.0.3 - 2012-11-04 + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +## 3.0.2 - 2012-10-25 + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +## 3.0.1 - 2012-10-22 + +* Models can now be used like regular collection objects by calling filter, map, etc +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +## 3.0.0 - 2012-10-15 + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +## 2.8.8 - 2012-10-15 + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +## 2.8.7 - 2012-09-30 + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +## 2.8.6 - 2012-09-05 + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +## 2.8.5 - 2012-08-29 + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +## 2.8.4 - 2012-08-15 + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +## 2.8.3 - 2012-07-30 + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +## 2.8.2 - 2012-07-24 + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` +* Bug: ``+`` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +## 2.8.1 - 2012-07-16 + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +## 2.8.0 - 2012-07-15 + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +## 2.7.2 - 2012-07-02 + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +## 2.7.1 - 2012-06-26 + +* Minor patch to update version number in UA string +* Updating build process + +## 2.7.0 - 2012-06-25 + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +## 2.6.6 - 2012-06-10 + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +## 2.6.5 - 2012-06-03 + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +## 2.6.4 - 2012-05-30 + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +## 2.6.3 - 2012-05-23 + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +## 2.6.2 - 2012-05-19 + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +## 2.6.1 - 2012-05-19 + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +## 2.6.0 - 2012-05-15 + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +## 2.5.0 - 2012-05-08 + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/source/vendor/guzzle/guzzle/LICENSE b/source/vendor/guzzle/guzzle/LICENSE new file mode 100644 index 0000000..d51aa69 --- /dev/null +++ b/source/vendor/guzzle/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/source/vendor/guzzle/guzzle/README.md b/source/vendor/guzzle/guzzle/README.md new file mode 100644 index 0000000..6be06bf --- /dev/null +++ b/source/vendor/guzzle/guzzle/README.md @@ -0,0 +1,57 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +# This is an old version of Guzzle + +This repository is for Guzzle 3.x. Guzzle 5.x, the new version of Guzzle, has +been released and is available at +[https://github.com/guzzle/guzzle](https://github.com/guzzle/guzzle). The +documentation for Guzzle version 5+ can be found at +[http://guzzlephp.org](http://guzzlephp.org). + +Guzzle 3 is only maintained for bug and security fixes. Guzzle 3 will be EOL +at some point in late 2015. + +### About Guzzle 3 + +[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) + [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3) + +- Extremely powerful API provides all the power of cURL with a simple interface. +- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. +- Service description DSL allows you build awesome web service clients faster. +- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. + +Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) + +### Installing via Composer + +The recommended way to install Guzzle is through [Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/guzzle:~3.9 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` +## Known Issues + +1. Problem following a specific redirect: https://github.com/guzzle/guzzle/issues/385. + This has been fixed in Guzzle 4/5. +2. Root XML attributes not serialized in a service description: https://github.com/guzzle/guzzle3/issues/5. + This has been fixed in Guzzle 4/5. +3. Accept-Encoding not preserved when following redirect: https://github.com/guzzle/guzzle3/issues/9 + Fixed in Guzzle 4/5. +4. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. +5. Recursive model references with array items: https://github.com/guzzle/guzzle3/issues/13 + Fixed in Guzzle 4/5 +6. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 + Fixed in Guzzle 4/5. diff --git a/source/vendor/guzzle/guzzle/UPGRADING.md b/source/vendor/guzzle/guzzle/UPGRADING.md new file mode 100644 index 0000000..f58bf11 --- /dev/null +++ b/source/vendor/guzzle/guzzle/UPGRADING.md @@ -0,0 +1,537 @@ +Guzzle Upgrade Guide +==================== + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/source/vendor/guzzle/guzzle/build.xml b/source/vendor/guzzle/guzzle/build.xml new file mode 100644 index 0000000..2aa62ba --- /dev/null +++ b/source/vendor/guzzle/guzzle/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/vendor/guzzle/guzzle/composer.json b/source/vendor/guzzle/guzzle/composer.json new file mode 100644 index 0000000..59424b3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/composer.json @@ -0,0 +1,82 @@ +{ + "name": "guzzle/guzzle", + "type": "library", + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + + "require": { + "php": ">=5.3.3", + "ext-curl": "*", + "symfony/event-dispatcher": "~2.1" + }, + + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + + "scripts": { + "test": "phpunit" + }, + + "require-dev": { + "doctrine/cache": "~1.3", + "symfony/class-loader": "~2.1", + "monolog/monolog": "~1.0", + "psr/log": "~1.0", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3", + "phpunit/phpunit": "3.7.*" + }, + + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/docs/Makefile b/source/vendor/guzzle/guzzle/docs/Makefile new file mode 100644 index 0000000..d92e03f --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/source/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json b/source/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json new file mode 100644 index 0000000..8168302 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/_downloads/guzzle-schema-1.0.json @@ -0,0 +1,176 @@ +{ + "additionalProperties": true, + "name": { + "type": "string", + "description": "Name of the web service" + }, + "apiVersion": { + "type": ["string", "number"], + "description": "Version identifier that the service description is compatible with" + }, + "baseUrl": { + "type": "string", + "description": "Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the process defined in RFC 2396" + }, + "basePath": { + "type": "string", + "description": "Alias of baseUrl" + }, + "_description": { + "type": "string", + "description": "Short summary of the web service. This is actually called 'description' but this JSON schema wont validate using just description." + }, + "operations": { + "description": "Operations of the web service", + "type": "object", + "properties": { + "extends": { + "type": "string", + "description": "Extend from another operation by name. The parent operation must be defined before the child." + }, + "httpMethod": { + "type": "string", + "description": "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + }, + "uri": { + "type": "string", + "description": "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + }, + "summary": { + "type": "string", + "description": "Short summary of what the operation does" + }, + "class": { + "type": "string", + "description": "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand" + }, + "responseClass": { + "type": "string", + "description": "This is what is returned from the method. Can be a primitive, class name, or model name." + }, + "responseNotes": { + "type": "string", + "description": "A description of the response returned by the operation" + }, + "responseType": { + "type": "string", + "description": "The type of response that the operation creates. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default.", + "enum": [ "primitive", "class", "model", "documentation" ] + }, + "deprecated": { + "type": "boolean", + "description": "Whether or not the operation is deprecated" + }, + "errorResponses": { + "description": "Errors that could occur while executing the operation", + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "number", + "description": "HTTP response status code of the error" + }, + "reason": { + "type": "string", + "description": "Response reason phrase or description of the error" + }, + "class": { + "type": "string", + "description": "A custom exception class that would be thrown if the error is encountered" + } + } + } + }, + "data": { + "type": "object", + "additionalProperties": "true" + }, + "parameters": { + "$ref": "parameters", + "description": "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + }, + "additionalParameters": { + "$ref": "parameters", + "description": "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + } + } + }, + "models": { + "description": "Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP response is parsed into a Guzzle\\Service\\Resource\\Model object.", + "type": "object", + "properties": { + "$ref": "parameters", + "description": "Parameters of the model. When a model is referenced in a responseClass attribute of an operation, parameters define how a HTTP response message is parsed into a Guzzle\\Service\\Resource\\Model." + } + }, + "includes": { + "description": "Service description files to include and extend from (can be a .json, .js, or .php file)", + "type": "array", + "items": { + "type": "string", + "pattern": ".+\\.(js|json|php)$" + } + }, + "definitions": { + "parameters": { + "extends": "http://json-schema.org/schema", + "id": "parameters", + "name": { + "type": "string", + "description": "Unique name of the parameter" + }, + "type": { + "type": ["string", "array"], + "description": "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + }, + "instanceOf": { + "type": "string", + "description": "When the type is an object, you can specify the class that the object must implement" + }, + "required": { + "type": "boolean", + "description": "Whether or not the parameter is required" + }, + "default": { + "description": "Default value to use if no value is supplied" + }, + "static": { + "type": "bool", + "description": "Set to true to specify that the parameter value cannot be changed from the default setting" + }, + "description": { + "type": "string", + "description": "Documentation of the parameter" + }, + "location": { + "type": "string", + "description": "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + }, + "sentAs": { + "type": "string", + "description": "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + }, + "filters": { + "type": "array", + "description": "Array of static method names to to run a parameter value through. Each value in the array must be a string containing the full class path to a static method or an array of complex filter information. You can specify static methods of classes using the full namespace class name followed by ‘::’ (e.g. FooBar::baz()). Some filters require arguments in order to properly filter a value. For complex filters, use a hash containing a ‘method’ key pointing to a static method, and an ‘args’ key containing an array of positional arguments to pass to the method. Arguments can contain keywords that are replaced when filtering a value: '@value‘ is replaced with the value being validated, '@api‘ is replaced with the Parameter object.", + "items": { + "type": ["string", { + "object": { + "properties": { + "method": { + "type": "string", + "description": "PHP function to call", + "required": true + }, + "args": { + "type": "array" + } + } + } + }] + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png b/source/vendor/guzzle/guzzle/docs/_static/guzzle-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f1017f7e6028c14a9e0694c66a6cfbb2d546adf5 GIT binary patch literal 803 zcmV+;1Kj+HP)@ zosv>1reIXPlo1y){RjSw2_NWGX5O-#W+NK3tBQ_IN%bh7xZ1Yehws80|4azI;aWIJ z_%xhlcTubTO7Dbx z)F-R8gg5MzGv|t4=e_El4GCwW0m6?C;0bG4DRC^TH6-pa>y8_h*QBud6Ms>Qf{oN> z=Q($Dn|DINB{`Ea{)h&^x;i{)XQ{?Z&id71eOkRPqgl17&E%|dJIC&SCwnd zY4oUR`OVorB8A||QZjLWS~cr&OD?HEtM@^bIovNUC5An6?z`xDgJL2H2Mya@dku<1YUfi&QvS8KS8=~uOs!oaF z8OMF7-5yyh}yDkaCp7Ob8b;wv(27WLL#lglguF0fh3d(d@ zP%vrDIA~G}dL)X;YnCMSE4ZM-gfVsYTLItd3J`~_vw^k=W%C_MlG002ovPDHLkV1oLqbt3=( literal 0 HcmV?d00001 diff --git a/source/vendor/guzzle/guzzle/docs/_static/homepage.css b/source/vendor/guzzle/guzzle/docs/_static/homepage.css new file mode 100644 index 0000000..70c46d8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/_static/homepage.css @@ -0,0 +1,122 @@ +/* Hero unit on homepage */ + +.hero-unit h1 { + font-size: 49px; + margin-bottom: 12px; +} + +.hero-unit { + padding: 40px; +} + +.hero-unit p { + font-size: 17px; +} + +.masthead img { + float: left; + margin-right: 17px; +} + +.hero-unit ul li { + margin-left: 220px; +} + +.hero-unit .buttons { + text-align: center; +} + +.jumbotron { + position: relative; + padding: 40px 0; + color: #fff; + text-shadow: 0 1px 3px rgba(0,0,0,.4), 0 0 30px rgba(0,0,0,.075); + background: #00312F; + background: -moz-linear-gradient(45deg, #002F31 0%, #335A6D 100%); + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#00312D), color-stop(100%,#33566D)); + background: -webkit-linear-gradient(45deg, #020031 0%,#334F6D 100%); + background: -o-linear-gradient(45deg, #002D31 0%,#334D6D 100%); + background: -ms-linear-gradient(45deg, #002F31 0%,#33516D 100%); + background: linear-gradient(45deg, #020031 0%,#33516D 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#020031', endColorstr='#6d3353',GradientType=1 ); + -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); + -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2), inset 0 -3px 7px rgba(0,0,0,.2); + box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); +} + +.jumbotron h1 { + font-size: 80px; + font-weight: bold; + letter-spacing: -1px; + line-height: 1; +} + +.jumbotron p { + font-size: 24px; + font-weight: 300; + line-height: 1.25; + margin-bottom: 30px; +} + +.masthead { + padding: 40px 0 30px; + margin-bottom: 0; + color: #fff; + margin-top: -19px; +} + +.masthead h1 { + display: none; +} + +.masthead p { + font-size: 40px; + font-weight: 200; + line-height: 1.25; + margin: 12px 0 0 0; +} + +.masthead .btn { + padding: 19px 24px; + font-size: 24px; + font-weight: 200; + border: 0; +} + +/* Social bar on homepage */ + +.social { + padding: 2px 0; + text-align: center; + background-color: #f5f5f5; + border-top: 1px solid #fff; + border-bottom: 1px solid #ddd; + margin: 0 0 20px 0; +} + +.social ul { + margin-top: 0; +} + +.social-buttons { + margin-left: 0; + margin-bottom: 0; + padding-left: 0; + list-style: none; +} + +.social-buttons li { + display: inline-block; + padding: 5px 8px; + line-height: 1; + *display: inline; + *zoom: 1; +} + +.center-announcement { + padding: 10px; + background-color: rgb(238, 243, 255); + border-radius: 8px; + text-align: center; + margin: 24px 0; +} diff --git a/source/vendor/guzzle/guzzle/docs/_static/logo.png b/source/vendor/guzzle/guzzle/docs/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..965a4ef4139180ea7d33594feba2394dfce6774a GIT binary patch literal 247678 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%0ARzX0ax0O>7%athq>_G8o_) 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 Q^3R@b2)1Zk0&5+MNyUSR-V=Sej)GD zf%hzY$;Is)$q;k@3upO$*n+;dgCk4_Ls3z^J6K5@d)1bU*5 z$(0$%FjPzzufhaXoOb48#bWjpU}B3vW%Z&j@KzZj?Z}ZcM&rDH!U8`F(Xbt!VSi@x z=}vCttP21?VigFa2m~ScUZ@ZRQv+nvr5YlA&_uEj2u=i;wyB)0>;onD0ou_4q#gMz zT@5+yK^Yordi|YW8N@_EFddSRrdJ@VX%0eEsMv|r2_Ww=Sria9_rnZNTDsAS1!oR| zL^dASV&Z$n<(Y?KXHO^{+i;u=fFTy?$9(pYK-)vT?@Hi+quLKg&*n9OOWo+?@uG(-l$#`1BR zm$s;g`_P*E>gsZEuPjW;J5T%P4J5r# zMs^hmQ}JZ`uT68tS1Uonj9jPnB*BIj00d<s(;}tx!o*8S_D9v*({69RW9E_!7Ny1rCw2iYymSn+jEYb&kxLG&DSokdC zpvDNyj06M%4sijHDj#JOqpzyE;rZvH(S)S)NE|B_9z3u!@oxMGxKKDN-nv}U-^*KS z<6DM?p3bS6ZA?-Y96?WMN6-FTCE&SvruX@<}bKBDNJg? z29W1nGd9-uAEO-a^>f$D=yI{3y(|bQq(nb1KB4}4F7$)q#1t7 z^(kom%P{8uX)s^>Ie4l|x*`+I`0P6Tjv+}JME&`S}|Nx*#(KrMh(NINLxvec=m zU>JObWdBAbwe>I-Lf70tvAvqov)%JScF%f|1@|~Y_|K^V!wXKKbjsx))-kNpc0xn_}U(4EaohX z!H7UJVA!IQkb8xKgUd^KfjL{P!S@OXj}Bd@1!<@vF6ZTiZr7BsC+Bof zs5T5o8$n5+Rr!Q-p<I;VD#WuvBK3ytU&}z${k47RP)8oAi8W_M=L}ZhzqizG0`dkFDM&9h{`61i}?>h0U zLVO?v+S}(w&VIlvl}wi=e!g zkqsbt%iGNjK!ZfdphX?Pnfy1+;cIXsIq@opk+i}aQP5;I2m<5gv|xvZba9<3!shllsl^WC(Y0K%>o5swz@G zliFBN)tn>f$lkqt-2v=(b`t_t#5F;Dzmr;dxwrU@!EfaLmhbUK#5VAL3T@EQ*iqh& z{IeYzNZf%=ek{jYzNggH)cyztU>pjrZvwDtVimfGYeaG#$a9v?-x0#!|^ z>PTN7v!$<(ThuuJLc|Snrd2wF1;N-hnrqQ@<8M)AH2apb>{_(y8Ouq1s0;7G~E3?}a!pndb)siz1 zK!HpZF3%_qAHJgJpxzm!AxX{Pb`(wNr%*iqMXxPYEW{Swj=!s&143QzN=-0H(PYlY7eb z0E)o^*vvRt6u@`};+^hpse@>Nlam~CP-Gx6h$Tx-i?72HXI{1o)m~p4O*X*wF?B5R zD-9u`bB=3}?)(*Kyx5!`M{;04PA@}^Ux{3~m7J%>PPTow22FW2U}`o5F1A4cO`%I> z0ScEnVLw)^^LB$tOd`y2^cMWtY9UwP(225ymLqo(&EDNZ2J3grF=GK>b7 z7e;`luj6TRJv_@VL#{snvVSKsN^kVqY*3oduf}*B;9xcvaGc=;TBVxw|7|@)LMrtU z#1C|M6fg6O4v%4StKj0A*D)!030$+zJ7XqsGreu!_xh-2#m$haxizc7d!6+^|3yPAoJ0v2It*FztkkaWJ3{$g5Q6Q7+c&rf4nx!^qMjyry zw4*c+<`pA>P3X@>;sa#a?cUzr2dSneqHRw!1RJc+-;H&GJ_XX6J%A>U@hpcj^j1pI zjEw9QBZr>clWf1Wqq4`;D=ZVqF8eNnFCB$s+1InQSJgBTPbmZnrr*v{{P} zVf*2#MN3-v7#uEO4ZH64yJr*_x&_~FCl*d+NAgMpW<&MNDwmzR1bNaraLi9t5-Nzm ze0txAKW0-nH^XcFe6Hk*C0`J-ep6h~s28ElK~enmy!oEU$8ld?mO>Y>HsLQc-B=3B zMjL1dIdFdLN##qwL$1G`Vcn z%VIUHLq19>7R}7y7+V`wvrmB7|5rc}U_4^=Yizv?fnllyY+8Ly@emS(jv~v76k;Yn zlU9C1AaLXZDdY6IB-v2{D{0j=zoi}HnRgAI+BfL;A7=|TFOhtiVkUX*qQ>;)(5fGK zr0Y!6)lJ`5HR@K@X1^0$Cuc-TuYuVV4Q06pMo=~uqSQUEYh(eB<(rzEoaMR_P%ENn zypABZ-syHUz$C2$1nVhe)RdorMq3&LukN%l9DMLYprC$K76fmeny3&%`h32$MufUY zB(_{u3z3qeZ|@f@5b9%Jx`$Ak?f^qkOBSR`7_SN^38Bii`HVGR5QIi>ZW1@lq00B? z!)sY!yw{HKY_2~?%gtb!{}e+PXgg)!{`-?BU)|U`7lIIqF4|^HxRtK(JWNdi0^rPm z7Kcy+U9Coa5aT3+RN_Xg+AARUz7XE z>Q29VOZuaLIA?&azW-QCs3Q`$5eHY;26nTQ9kPFH+#fzb0}Vb zMA8!pldASm^|4qsZV#lP6~fJFxpYbhc2d--j<3rMvpICj zL1+o4G{mWm@rIbHANa)QC0spF#Gr(LU#7^Lmo_xa?*+y+r6}1xBBV4?M0^C5jd?mP z>0p|iYLGFY^2OcXQz4VtsND?U)inv$_4_6j!|xe-{0s{;scvDO;B`JHX@p( zWOH4ONdI3UvVX7L7tEu&(d`aD-v=09FE`iSA!zK!N}{+I`%DSn*HN3V-q!#Oi*hZ5 z$Ms+;9uF?@?Lo);ak18Ke)Ahf^#~j6CNH26M8c(cb-!OeakSlk>Q)F_s7bQ=3>yvU5DiV5m*MVz)gW=ON`^z z%)&U!fzF!}ka&c`{YeK%joeTt)PacqN=DrX62MprBrLaOT(HXHNz zqQ)YNT?89Q{mcWSNd@FM*@9qsQ6H|jqRt<`-~%6Os%kt3=IZ0HC>#QO!eD{pQCsy8m8mnxf)Kp$$`{8h71{9IwW^-; zb0Yii`FQ%JjSJ7sJVl@0*G3lM*TxdH$c#E`7_>L175d5in%>~%vp)RAuZ~02{~Yi? zs8nUh$SFwY3i;!dV3G+`&hDmM6APYF6l_>Ai5EDajCd)=+Ge2ku_8^vACgYy$%;XEp8F_{kNr0ZlG^kXK_13>B-8G+~5 z0{i<6vK1e|Gb%4^W%mx*&dtwaO>0$JS05E=+mmolG-BU>HyEnF-sLi|m}#$#r?$d{ z^l?N{7a%Ua1IIA5oT7qxJPP1#QqvUGC3^fOS3E>#Nzo}4`5mUe1Vo%cYs4unb49jn zvO5eO>mkFALt2HYRQc#+eNA^6MK z8n_yXbA;$xPYR~+kfAcZ!l;$339(!j6_345?eeRKI+a^HI?4+bF_&xDk{w{R*G?D3 zH{2>J;ng^-p+TznXy_@<4r!W#ULf~Me3t>QbQTOl_C(nNu6cu(sqCtdY-I!5y zfwd6bWMiU!(G|XgRcZvn?eAzq+X!pvqi{Ji=XmyuxK6SNOpFt}dB58Or%wxr->in# zn#o#9l?_yKZ|l?KXC!+v++Nt9harCy(AbS3LpEC3wg`bCF;w+g*1vUC<;(Nr6$;pU zYs8t2a^-6gjCvxg7Qa!*W$pn?`~a=<7xS^0jU2t&URMqL;?*b}dm3QV``~dQ22~1%x_Rz(p_~iR(EcAj!UEp}??B*lq|UJ28(V9Nz>Dh*Zn~sJ;oA zM|q8=0BbT6$Q*OQXL}*g4xfY@WD}|geGjFD9)zRouPu)IVj|G^!RLpfvk2H9hei?z zRcwS-Hl5~>WHR9~lN4Z1lpG<}IX)!-Nq>k2d$2EsNhvl_hJot?e8IBnAVjWl+x+Wh zF27`6C-x=VR$B8cZ&k_AzJX$%5IQF06LSg)B}afM?jg`N!hIOkVm!ekUIex)Q2LG` z-PQC9MrgyNAJ<#$c_Wb<`(C8)=%c})`*(fu{+%ZS?nATP;#nk)3}3yfobmLZO&$^E z@i{OZqPYTWFk}BvruogWMC-9+eo=R_RF6KF2lWMQZH1az_cQi!`_790Z0dS0y)*;} z)Mr6b+=Yn!7^;3$`Q<7BLJ!nMlx_Arhlr}E@ON8W(8tP^W0L(<6A5b+)mt#tZOCSc zLvx0#Ge3Uej71@wm?k_QDogFl>&7f3Xw)LOw*!@FUn`}?Jv8opr4Qs4TFng9pes>1 z{im^7`%!3u2shy<2HP7&gTs!ccT18~{`JfDyugx2ho00K%wJ1{Sd>+S594}o9 z9_ZGtWN%?*AdpQRJUFDQiTh~_roCaEEr=5qNQ~8D2tJ1AS*ho$eWnn-u7!OL<&rxY{E;yYpW+nCt8)aK)7x|CZHL&xLjlWiG zS~xFlrPnT5xO5?s6mc^)-9=;urCWlfa=`GKs;Wp*i#-S$ z&%YH|_EApYzbWNXe=YSN>Xrr2xx%Wz3<9S#HH;EdmpKKqNwt)fAl&%7puHfDXOv&U z2z&{std+o%w{Gq{H(o&Q-TT>Q7#;xxcOD(!7R<)+n1?C*>q(yujOBu(*{mP(1DH?o zm!h8k&hSX@Z;RRdn^68{7K)kcDt!lIQVQrRr6zgeza@C8?DMC3J!dfge>v%8z?EXo zz%9%6`cxscK+xmR?)oiGw-qzci_r2$ZYJ=uRh+;zMT<(!*pY7Hv9w+rFD^DHy@gUd zU;s0p1`ApjqocUx!Uni2CK{N;yePg{Fj07i)^jI|!3ll{M*zhzDP|qcuoW80mn}uR zTNn6m>8kcC%zLsDsx(d20&y_|rq{(7_@9Ax@_+1%@U@lA^X9<>JtsIt2K+A65kr3dZ z7;@C6HI(%$@+^G>_Umsk;J-tu+Sd^%_>xSUcOWnQ_F_Ewm$v@?vt&^r14=NJVMGub z0X~~UjUq_^4b}rzc&Cl$=Fs>-Apmrk(A+5u!Y%~cjwXureJQdiOb8gfQZR-6XOg-3 z1JUdfDO``TamsA?V9SHBT5>nQ!~B;vnfVK+Wcy1bEPn$L<#iB#<@1q@x}&E?9Vh*{ zbB;-l%QN;y0N7m+nzbNJnkg{sN?NdHBf}Qi&w8mC*8+eXHUWa%Llug3xYtwT%&W6c zz_)r&R@MIkO5_tT6IIX>U#Pj{b^CBVOK;x9qe4eB_>YmIk1|4;!lR`k5?TDzhVNW= z-8uJFE1<#}pfMmIuiDnt1zKBu238-MXZ5}cX9mcReKuleupE5RJ*4}%$wn>ybcyFj zK`T_xPpo)G#V7A?URO z)PKy;zkGcJk`Hc_K9WCPpuvMn>Cg)G%FE%eYp|UU5y6$6eF93z=*{u~fDA z{ftWG2k2bxwUBJ>`{1IvaB>V(vfg0LY|-JkS+h(eVsVQr-8N(gtR>)reD&ujM3vv)&)4_dq1gx% zZyQVwz!!T1=aqtk2*%r4YYlBOPob<;*^I;#;RKQ2#Tr@y<%Q;^eXK?_F)>hgqrotM zab?tG$;`NAdr>jy{*GxrR*#*jQ4OX|Ef~l@;4&BDP7-aqt#WP-%{j>fn) z%@!Ty!cAd5o*oUmMEh+RT%QG}@NO{53`3mGL7<@pX$>E-S&~kljclf7BHwAU*_zts zaCV^s9KB1hct8%JNVi5^jf%Uqh~xAuxV-M~uxTXhaEK|Lxe|Z~49QT zRq^vNCNI9usOsKmx{$f?kq4~PcWrXLa<;38<0Ka%xJizL^7fCS`oa|)r8fdm@$p2g z@Z6rqz7uKt#Ft*tO4*m3Ua@&NO)X6u#u^kcO*Bz;-k(CRy>-%ujKJtg;IBrd6E5u_#8=n){4Le3}B_$ib8>F z{YZKt+BY5s{zvX%jgzo*6Gegik!C$!V;jC1GuGE~+xE0R7L~A%GDt5RT_ddJsIHNbY2Tw6tQtwjXcA*mnYt ztJcpNlPRW#qPbcW6>xxLK7a}iqc9sh5ar`E%BLU>kgv0cl31((ju7JTmmfs8jBDo3 z&0>rk!TWCW@Hj7fQo_rp&0>An!MjaHFlg&C%#CYcz1XnmK5XQM4sF~RFgp7_2{LBi z#a>fMUw!AD5m<->o`$97Pv82pv1VlMwZOe5%OZWW*wtlKDuaN1Q~=4&$`9Xt_u1~T z9LhzDOMn=?8eFY_&6&TK;q#|&+`?MF*=X2Hxsr<2S3n&dK!X4D7xG`6@jVM)C~D@; zvD|O5lHUyh@OhL-N8*xk&?@A1Up8k#_|9nX??Hfz$~o1N&Fek+XW_X;K$L6&iwYsR z;&~pKpRxnw;HufkdZ4dPYa+@-7fe1Xack`G+TzsI~)_8ik~@?HyaurBEdpv)G<1rNtFP3Rbmdasul&` z!jyIaWiKx}6MIagsp5T_@>agVYh(LlD013&lW!w$Y#$FTS(#>OwmM|9K?|XhANU*y z@+<)}ydVWBkq+BzH7QHD2bp_E1)6`=)Jm5?OZY2rTn>W|PT9N0} z&Z@%q{rD>jrQbh%UmPjZZz5Pkbtm&rmx`rCZ`e0R_QhgCwn42A0@}A(Lt0ka^=Efh zN3?iLdtlyi{6@AbA7>yvwm;;lc@CKdpMkq*sl8-fwqe7DUuS1}j$MaJM30>uYr!IS zwTot+ad6hFszY1g>>dejZmlV2Z{?3f2JCIY-yz-n78%S}a8dao#2{;#HG*kx1I%MF zh7Vy_E=bcW;rMr$7>5>DQF)~0!u#p+VaG8Jy zM3n*8snHn~B>Z9#Y1~JHK`?7hHarJ%90Mhi$(+cqYAF9uA_*a!!goULu0}>^sozGA z-Q2vWjPpJM(`B)))Yy1_34A}%k92g5V6Q1Fw0wj=(Lb1}(F6!Hml_)B&xB1S9fAb}OhAmVo)>ev>tS;EN3p z5$3SUPOpzA7r^#=4nD~qo##H1esycSVa=L=s!5N-y|EP8`5#S}qR$YL6S7ZNEO|Ao z-noWu#o!+t2h1o6d70NVwwTS93OnY66O$7U@LLKCX%`_cCj-2!O;yfIUVj2ip_iw^ zUZ*t@MS3+=x58brCzMaXk4nsWt!0_ZrSfdJhALbX4i|A_&bc=@1l=7kE;TuDBWUu; zmLQ$`&HmGY+N$6hh1QO|dg|zzyOu2rCl&fuP*FUEk}?rPU~Wc`>Mq`-ztEE!+14DW z{UtR1CRNckwpF>$;so^ROz}>baQ`49qp){;ystXods(ooZE%64p<%=|RoOPb%0nPM z01cp?Uhn7|oq z2hx9^I1^b3-ra6v?%1&{ ze^RFiH!9_hP-JkM>A6+(f%3uO;}zB23` zc-NcpK7IL~R|E3ET#Kul!Op)Z%d*jwksW0P{R4(g`otYeIu>?9qCxJKZyE`b9f}ug z3HqU~qXQJN>`zgYpc^T`jquG53&rf#9j(DB^LFx{ScRj`-ZL+}aJFO8+@yNF1qjf6 zDC=DkIQpaTp?x;(u3f!#AlHgCms#8T>iK1!5&>bj= z0{YZEb=&zQC`Qq&Zd$t$ru#eFMl6)m1ut1td&I!Z5qq!&*yrWYL}t2ZZct^jd+n*wDXq71 zq--g95H9l<;01tt)>`T3gq58bGvxQd|2Pu?wZ_>H=dF7Ifx!||$g?Gu&TA|oJuh#Q z-B;6tsQur-VtKe|m^)yx96}&$1WLdlu3#Ukb$r+5WdH4h7qw48jWofr49El!1JQX< zI#%@!Cj?B?cPuQGKK>8Bmd7PQZ;&zdc9aaxYfX+j0~EyyVF_HZqON!g_!y8%o9Yh) z&YVsS?xU++2ckQlCdfLB=|8M3>FeWfTX9J%G~p>fI(g;@To0f)R!lJ!!teXOxEzSj z8NyURARE9bYIkb<6mj!RzczDmb#;^(=zZNX^FPX{);+4K{|F$Z2zXDMz(mr#M3&34 z?Z}A}B=(a=UAT80zkk@>`k&eAJqzF8orpRR9J<5g=+7Vm@g+Os`YWAcKZsgd>nK6E zlNwF{pEx00>^|EJaNpkG^;S24o<)9Cshcu0Drg4ASrDotlv@ zYh{tzT93%ERRGc#@_H$18!DS-6ro5=Q7K6bQbv7!eTfi!6SEEA1907R+63nxMhm&$#U{eI24Ocjl}`PJ`v|KrJ?Ro?`|80o}j}whl}H>Foz> zMjw!9f(yXVLF3>rl|}tD+^hjx(Mtr6QPLwe5FXr!(mS6=w%l>}vmf^IV$~-XEpouu z`>clE+hyCj8?=u=^NRWSR>DM_f-pXm)FXcG6yx^<`gwe zfbPD3I2$?e;`jZdWWk!g581D5d4MquEC*2tU%Sd$pQ8HmWI>|VeY%-9jM4@~us?y+ z+Ye(IxI&?nxqwj3iWiIJJ`$u#bl3BzGgT$ z&J`))+fF7(91>HDKT0|!0Zq&41$t)Zv_R>f@_z6E-c1`+02LS`$hq>RIT9>=XCsy_ zn@Xu3luvRqEUKIvFDcpLzk;@Yf86gcI|}DGY9&k-)6N<1m}Boe;-B`LKlp+DXWgpx z@>M9+9Y&zzAYCJK5P--5>M{!N>^!V&cd?>y7uKg6 zC_((1$+xC0A{rw!U+6|LJ`o!4RMgzKwIg0tpopfEUH2TnWm9tLzNB z1Q}|GyO=K(Y5E!E@ZkaxjJzVPMid>Rls-!9rGZN?#Un=3JRYCykvr_e@fF7+IhZ;V z(Rrf9!Z%H@!Bc~CdLQ2YzCKtx8>N$O!NcwXf8Z!Q+{~CZ3Y@W($11LivfT@KSqp0+ z)wwX1GcO? zaL-eKlJ5qJVm4)CJ}*$h%y>qB8c?nuphnp|+>0A%RYe*meF~TIt&>}~#_wIThS~YV zFJtRwg(8LC-VtOz9YxuCB-fRF)MY_Lf-NyEcK`^kJ*#H~qTp1P_eIi8%-oAEZt_-l zyxd!ynz-2#wb14FTm{~ab`k2dK^e!gg8!w9!_puTu=n0eTO`3^=XkDd%kJG%+7`L& z{e9_uARXF<8K#45;}S+t)^y^TpVaHDfGiGbl>Qvh^UpXP;;SLM_#2;PJ^R4=^<{II z2$C8E2gz|#QDbay_{9EFDgLsj*0YQ6Id&EZ8y~dfbM7z4NMIc_=fSOmGWXUJ&^P`; z&zGFzd~6YCRa@JPd1aVdrGI1rC1Z%BZ<91>yw)ATvwJy`Y-46Ex)?E<57R7jiAd+> zcTR(u6$|Mxpm#v|08Zx4KC;OF$s4VsB&ZB^j-_S+Ju?=JMBZUxRyt)1g&Z`ce#7LM zjS$uwXvA(1D_J^=FY~5Q_MppM0M%$^`}67bJ>OhtS4f>;GHgK~QUvp;B-qBz3=Ho) zTNq2PZoXgug|*~JPY**%yY#&D^k~E4%b%y+%tjP_|2-yX45^)S;2!vpQZRpjG`1=2 zwtSOhv6a{KIrLDJ9<2+GfWE9qGN8innHH)7-VLtX zR(u_?yO-23T;RZ1>cHlQ?;rZF%rGJhpYKmzp_{q~_XH`+q%yG8Jj*#}ps$qSoff>- zRyq%uRxc}Yq4~u?Y*bNDfrdJQK+2R~jpU$L`^B&&`I-^czS}7~_W2@_`)BUm8+&!> z(osVIHXoreVj-SN=KTPD?#262M9PCS%nehsj_I_PMm6JfXLt8wGiz&KkYOrr?3J+m&GLPjFa$`um zRME{Fk0jX9UjZjb7!m$YD*drnKQ%Kr_VB)u7ZFI+ z5S=uGot^X!?1V*uj1#MKG#lXOUeKbL>`WPm8n%W$a=f zmi;5PA42IPCy9C?-Dy8yU!q0jYQ1&HJO~lDB z07qcO)%VBg=^u0yyPaNGDljK7pTTot?|F8|4ikaMciq>fXY-GvVJt2M1wse1 zCm)Gfx~C3&GG}w+t}5}3EX7Q@o!<38@57hxS@>3$Qez3ubJh~rR@P=cl^n=z`NYze z;?^FZ%V;)VFwHUBP_seYtIey|yEQ{?hOk&=s>%a`mOhg+y?dQbF$^BCF2}KxDisb- z-3Hy#8h{_I6)fpPLy6q;+qZ8&X=`hfOC|nVC-^%3!KQ&yv5-s@GOxf@DIAW+x{7!WyNRg&XMF6sa(4<7*j^vR1tAY;V$Bw<4ENI?xGTric7XH$g} z7kea)hRfr?onN^Qfv77jf#15}_AhTak)?X#xukvn!8gY5n#{(U>MY(Mx#I|wKFD9% z3HJtq#*{mu7f~OK--Aq)A~KeXiBj7$S>cF7OyGb}|HzPQ_0lWmgy@0jBL`PY2`~($Kwm_#zZvgH{1K<^L3hgjW2~R%x!ym@l z;BT4MOjrokuHj4vl1jdX%sano>L=mSIYmQS*Z6#8^MA1KNMC5ij9em7h(Oa-S65F^ zO%lzNZfyGiFLR%SIk*g|q9``aZN$QGY&wT-ra1PdiwW%l&iXp2^{as3SiRk{p6EdT zr}}~kh|lFl+#HN*+=?vZk0M|@1E_66vr%Aq9)#*vDi6jsAM)5iCfqhU7XKdQtD_ta zFuCcV3*uP(WX=#x{BD?a0%N{BdD#DzFYsNDNi_etJZAugW~A(t09YEB{J>$1XGM5R zkS$N+Rxx>~N(OSvBOi=EDVXM<5lhp z7R}73B(cM^2i$gr4nPBrTB2tFt6Ozg(ofnAI#9}`x!28$H z>d99I6z`E1gLQc!%x(qD2V2`_h2rEl3*;t;61fzzYXBm!7Qv;_lvnbvK~=3@*tYC` zbg>M)uRJ&`DArcENylD0xwom&;m&HdnVF*cVx6R)9UChh$~#rFd&`zWc*BN+YKiZ5 zHZ<_^K%aIa5D=BV42f0qMIBrTI4CIAzN?$10_YJpvm*0(N)dWG;m+uQ+?+h86>>PD z`^S0&75BOqP#h99Ujui-;b@cs?1;Pc(n}$`q1~5>jk@^VLm`0#ZOHz!9gg1PvDgpo zyweufvOrn0`FTyw47B@qOf*96r&{{wB!-!TsMrgf!Tc@fqWi`ZhI!ou7r+%_Q5#pR zC_>N=hr7Dit;5CITsjBzzyxZA<+WZgX5a__Gp#Y=JN01gRVTVwX0Ii(o9R7dR2hjWWf@V`h(#jH0 zIiw-C3@rA@^Z>R68Z5BRSrk&-0~V8GsAw?)x5D@YKQ;ATxE?xC&7yN1f_UVj|LKY= zoWn(}0FLc+$sP3GI}IB6r@ASqd%af>_{+?KmhMjt0PYsFNc^Yiy4!8Hw1vw3tn_8( z%$Z8pc*aTbKq?7>)5$Yl;LR7HYDqa+h-?RuYn*R_m-OpB3tw`Oj>h0P0x&!TFF*^Z zV}M5ZGdqEo?cTh_td#N4jwepgO`hY(3?f*jHemCXB7X}LME@O=r%IJ_-{hFJwa!LR ze_e#?q^EG}4kb8a6knSrC)!I!K?Y&BhT1R=ton;p!&(Y~&T$~bPb5JeOr3CZ$)!Eh*l=K zUMDn4VrrMoamwR4N2Nf1{>8|SodxaipOL2hQXLb{#;8;S7^%Lha@=k5c>3=*|Ipn} z0)dm~WoG!QXK9|0KCf{uJ9p{25{D9$s6S)!8p9nc7E9g9BwWIhCDhCT^QPrFoJb7$ z3Wazbgm7yhFa}9WHvpZ5`%)IVB#?%}o{o!u{NofBpz?W~Va}H=GHu`}-~i!zlVY>S zSGXX=kq<@$FKJ|Qtoi6|KB}gvMW(guz|-{-wBBuGLESc#{|WZ}6@}K@MCnM{LyaYUtc9K^d%gtcEIQUY@N*}92pqUNR*$Ps|UH=V7LD$ z@G1c;22Ze>_FLpO$4Z=K(+wtsiVn3lB%r`0v({p*VZo{^4Gs=UaYNt=q@<>4WZ?_W z2;ks1P-=XJO`doj$le3_1*+s5il4Hq4f^D9T(l0?cv`VvWBODE+>O z%8uA`;BIm$MlQ-Tf?v9ur>X1B>KQ-8W%t6`qhG)M?slL>TsnY6Sh$+jU=HKA%6R*e z+?|eE-$-YadO}2r8psiZZD{(%6VYncR))rl^7ZQg06+jqL_t(PJ-rh|)*O!KnvkV( ziNG_1FaQbZstrKs^U}{dz3)WH`eQW9;q$roM)UJdW-LFMh~%KBy1MB0Pk$l&2!dAR zGl9ua0{uH)H@^cyn$6HYz7HOQe@*6-&$fr(BBL%x+c_%83kZo=AemMRT2nZz6L=9z z6|U!Lre4wMe*>-GcaWv@7@Vowv6}b55z1@u{X&afkKod+@B@4k!r~!IXTBjjJQtIq znWP_;wj$W1@X%*9|fYeoL-dic^iz>4@mjxm>W4t|ZE*EgV2cH3_Oarb?2M|=jp)=w$TgijVLe9zwF$_dwgyTJbfXC-OYh^25h zgzKbPq#u>ztZnk5biMf)0-{K6F#twDxxZW}Pxl$GWm}sqFdwf8%vwQQ7-ZX_Q>UCr z%9sa>DI*BfcdZ%zt%Z8;?(w9r)5hd}YZk4C7*@Tfs2e9CjQX<3DC9hK4rDT4jyNos zzSyg3T^qX2B?D$YLvNG??D3j9Fr!K+^u)=;%A1 z_5alUAwrg-uZh!mvkvx7%r0Jh)CG#Op zV!j21e>>_bE_nNP=i6r%v}}KrKJ`bmS;!GziAt`XCCeA5e(=xs!sRPer^ZQ_g)}QY z!{f-nrzz$+p;=(X1`x2@KuMF&=!KN8v3{tAmOnt4=BK8n5KG17o}sWB?JQ`_2U=nSdbRVF-&NaEl(EWfr5Z z+Z@h8A3o)h#~UYC@}oJ;ZW9@JH0cH`n4`f_VIY&~CHz=yO^$pdTv640il##2E zHrocB{;?uu{tEOlxd1I3zTP2BX>FChS6<uk!IguYP{**^pM_beP1d5TP!qlMSrbV~hKH#QoLjYtds&bRUn7c7%`j#&k1UX2GH z_k@#g&Z&8;l+bXRDIsg?|C09};Bl2_+VJURdeNx&V%d^=!PpoqW5Ad=5J*fy2_+#W zOMrxwP5W5eo4&~g5>kLP2n2$?K&Yk|Ecf1ITW*r9-exqto&MkN*b=fK-|m$d7QX*n z;2B9Xr@ZGq&-;}7x$he|iYTp9r3yvOwQGgRwwaRIWbi|WRWFhvS3?4e>jfYImUvTO~IKIOpaekQ-_87e0 zipAd%x3qnAJ|7PC(cGfx5nZEsJFn>LFRt+RRR-*8BgO%%&c7hP3ql|3O->o=>OZ<) z?*t!FA|7{U<5;vU`gWvjpa#SIHD(t(8gWy?E-@&K4ECv#T#79WWkp{<~PCTs@0bUrU1-1{A zo~XPE^U$A^^~=Jp@t^y!y+3cpw@20IMp7!eJQ)q#j$+<~oyDu&7_+{3 zVm>%ZKS$a{8Bu=h@&a$9Sf-R>w=MZOr!-8d@_ukBqhW)pgLnKrO3(fm#Y#P3zN$vU z0X$+Vt>~HU)n|@X?GlxNXc|q+e3Vrpd;4u!L6JPO9gNuIq1f8Ka5hc}d>{BKugmcW zq_cm1?Lo_3|9MxU_f(=KJg+~eB(Q_k^7#5luv;JY~plL zsT;PpLFMfW@!Wk8W846vb{NI)N0z$nB)%aXHpV8?6m1HcP(U&t@Yqf~BnatMAHWqp z{`lim`OG<=hfd+=lDaw{w4BqBPMn9RZj+{HPhno*Y3R;1NIQ&qk$Z+ymjM<#2kb;1OV?hL3q4y2 z^lL|d^3&*H|Fxo1BY?u(_}cMZ2hSz`*xDcdI`}=mgEsYlr6l(=niaHwuf7uw-~CuY z55`BrpII`gg5-~LTPLW)+B!N!S#z1ZQ@kE^|JewHJ($505SMR(m+&)}!wKp@y**su zJ=XM2!Rz!6&iR*&g|mK^r=ZB0Dud1$Cc>D3ehVyP4lC=UcpHjp<^&4@74Rf$z!3qG zdGj$v$^HUPy-$+(Z-aupph4CLyP? zcQ3`G356JvvEzg8`>qdJZ1#JbbO&Jsvs6W=D*L6kVUL=|4?oN7fQ7_ULsZZOC1B`&Y_Sc0Hg)w2n(^3qzPp z?>V1FzjMA`xArA&)hX?+tie15K9@nD7*>L*b2TrUo3mN&Qz#TpxuV3oV*GP;G?@N8 z&6$e;_1Ob{?+oVju^k6OJ8x@x_w#YS&hvci=e!i$Je8G>w8=R*)^HhVElI_D1{^6j zux5D$w8RWoR{K;^BteKX0dOTzl-v`u$#k#8a+^$!-_lV;DYMR84av(IwN9_hNG*^S z5)qo7>R7ur2a&QkwFgq*s7=!qs9AmC(&}kX?CuYDhfOY3fJ}Ro%6e!{o&m20g*14ibbH8Pj^6*3l)0 zgfa52x1#WOk;w9P(Mi$G_^#dQE`(Pc#JO*c8_}9f_##FNAfTUT4}bt4Vxu(brqr$Qfa^Hy6`dK6pwM<0^};7$ zv|sn|#^AP8%Ak1Ptc#?gUKj;}wMM?-&vj=*@j8XMgXZ%G#1 zuXsZAtI!wgN5Vf8ibbDa9w;FBUgXSab?0EF5U7Qj6t4^dlh_`o)!?Yt#BjdDMmDwq z-p+N%?cOYk;E|-PJ@B+UbeYpc-3|nWc?BxrDUDLviv|GBF9QE8YqOyjgaOC`YVC1& zh5mQl6zm6 zc{|8Weq&MEpYXVVY}QiLC|l%m(HEfrnL;tE+rV-|6^uLZ-+4C zhSr!)xu>FFh7LrkP3A2`y6jvyo+nk)h-=xA`@@+`s*!RDZD1as>v0xjbr$o5*)?m$ ziV%*H&gv$*$1SkFQR}iAkh_%SP3TY?68SuIJ=$PX3j~VIN8z$15$Q;*IzG%&XJCx* zn3#RTF$Zwn8^EOujt^$We7pTU7qZh=^wIs7@I^Ftx?kblk=@xeT_Z4-4`GxBm}gIR zV4yE;tBHx_(L%d9ry7OdL!-|bxnfn%G-5sL#q22d>2xK2MHbM%TgSlP&dZiQytl)u zomMN`-OCW}-7Z_~T9lzdyU8p=N2uizxaY@vz2uimwC+ndy%`AG0(P2h6FArJ zU{78@_p|5aB+t`M&hi5P(S1U)Bp-K6(jX{VkW4Bh^p+*JPX{HK)9F-NTbEldmkZX_ zdc&gCH8d;!CyD_3(1v`p!VP2)woU!PhNE%I$7u;*!{NKCloy4WY;^)g=FE1Xk4n%2 z^=tSnZvc4^MtYzTPt9%cgjT_8`#yjIpMiHx*koQrdo$>Ab6%jBQq)VZY6>ve`)!uz zU7`Bv-9r{pXJ*>Gh12$QD^;q-HX}tFQ|HU$I={y=HEMu)4wN33L(H=mZL z!w^vvdlBsKk6{^1a*ToTra$h+yuKc@mw5Ltq9yo9uzTk$pbyBN7%GCrjDka9JqvI5 z_b|W^HMH_Y##|H}7>E<5`@YR#4GZK$#=l( zI}!N5#Xu~C=(PEUx7s_FTPA!yLPn)h)I^=^t6KZADJH+U;U_Jc$J z&o=vz{~)ly--R;AP86+fqW$zu%MuCSNL5iUB<`L|Yw7<0%Hsg?yr1nzB`@8F7Fij} z1xO{}KkJyT>wk(W**9K(^igz7QxK<9{3yzO3SoRT=n>auv)0cqD$66)-v9QT2)`>B z7B9zSydLs8RmhRX(f#$)cq;Qfq;7yR2zGp_tY&L^lW`p(J9_ zKa*wiS0Zh$hS7)Q4)qX+P7&t^Y0rU-G89=Oo|4nA{n^}zie5PK?YQU?%!ir z)V41@(^Y6@BHL0K^_M#ydu%A(*O#&lcZL>&Hgi#t`#@aDdtFnkhv4z7fn?SGgV_k$ zDy*9&Nxsr4=*8Ur%M;6j?C0ogt9Nb$lc@kIg@=qGD2B%k zA(CUMfqk_0Hlnf=1gx{@o)O$`_djjl&OO%o{P~|=8RpEH!}ro+b;iir1m3m_UW>I* zw;{EtqG-j0JRVQBw0t`BRbp52JbMa$maQ+Qd1aTdC>~C7aNY6rptK4F8J1`qH)kkaFFxC3JuEB-dJMr+KDBk za+{kh;Hs^Hu2v8D7S;mmx;K}bJTro%0G7vFvl|+wQX>P=-5#&|2f*1bMmCaFRP~KO zsc%~?*v%L8`6pwb#^Y~-HT;{HOAU51tWe@zE za&em4%k*6`#Z4e$V`D-N{1C=vf-2cruiYVAoUExTjZAJYyolu6UBL~yC0>v2+dP_P zH}_?R|7&?|ZIIw=FTX4K6e5NO$O$<`p1#=;qF$u~^Z*IfX4TghL$WOJ!l7jE{^sV9 z4I4M2xnrfddi8^`MPIz{8KiI~BkGt49Q1V{%K16za2p#0yirR$hZCnpevUBlD@gX2 zqJWwYxElZ`)(DCw&wHu#bC*mkNa9fB9#c&#o5)LUOT=ie0a`$`%pL=&kHl#F=8FTa zTuOv|fQXd(=UwW*bj4R{(OlrB97P%$Ibs5%L?_kyf|ZmnzO^Ir06Zw#soMCkX3Bo( z?IjYL@45>vU56>CYVx%m*%6941q=JZ*9IR3>Y(g#Lj^THN#d;-0U zHH~go;VuN3RX3UI22%K@$$Sa`d5^#=DHO*kJ|bdY7m7Dmbb$}km8pu|rKPInQChR3)m^WVZ5j=Xc0r{6_% zdJr`P^(u_TkV$71Q(G=`?8}51@Z8USn124JjqeGeAmA`nR)VO+5Qt%6kgT?>t7kw9 zrPvCI8hphE;BQodQUnxf3@=g!-7c@(Xh5S!gdWJ~V4PateWG$v{#&eNN9|0{2Tuwo zkdXEa*TWkvLw3>Yo`qu2C4$dcjjl6BQPO>Asyr4{;;rSBt>aj^Bfh-Qan?hW=?=Xu z)aUI_ghCNibaM|nIVLgWMV_K70eSiayzvr$A-mFw8$rdep6A?Le-tajJn9|nXRh;W z!Uc`UC*JFGP{*%MCjS7k08)T@*ES#|MVrBAKyvdE%QK$@>BrsWmD4(5ybmr%Sk;*t z-Y_KR?SrAc92pkSKdQqPn|<6iSOCM?N}Y;E#ci_eQjZ=D^%j2;?r@_DaVYNkj@sv8$iED2-Zxs4c`x4T9rB2RoPBAo@mT>EIG zr$kzdN)&gXttYyn)zEhCt`78)FU(3Rje|<&SW6_ezb}@^tEK8FkOK_^MpX(v=?ccw zxysz0Kf3>j6yKc{jCq2_poz_D0V!|U=+&r-0cJNwf_I2!8!Cgjr~(zBgXcj@>qZ#J zAj~Z5lqjhQ;`WZ-z-WI*$evdc2N{*-QOSosxNR4dQWpHn*WcoF{ria4>TwRB7PV?< zyPc#){&en{L=Np7Dw;a?#olCs;oZV>0z_O#i2faA6EW34v<1QJcMkMFAxhqt(ZzMSh=!>!gn>WWPPJ1XdWc&nb zzK^0gqZ&epE8NpBECF)BvsH&bXi;Ib7_|W2^-C?0d6P}>S%)KL*UQSPq97u-QSxQZ zS+hJO6l{Va!?}+wROIrAf<%eCY!*5e?qezO0W3;i#Y1-$#^(~=q8Eeb;MT(YwEsds zokV-`UqRNk83t#6^}NzC52k1Ho4`O*&tL%nq|KB;$9Z!)uOg3lzD82%2Ic&zlx`MV zjM?zyO*c8namn_HN>4XZA!`9MBloY~r1VdME;2x93-=BTfEn&|_)3r_-eUL0bjtWy ze`n&~G`sKLd)f}YI3F~Y$m@^Yi`wFfNvz*<(}vE$3ha+2uqVC5bA+W7VtCK;bawXk z`cSd`7Cg2av4bKDs1{&W-5kYEMDg_&6cc|YiQ*ko+lTU2V$4!%imGzRX1Pt836zI? zxF?4GQ*9T;67>Upp3WN`01v+>LWErFhkYaWN-^m*>_(JGI+g;6L=fr;6S z5rMv2)J=(>(*_0#&;9I&>F0mC$>L~tRux5MP&0)n$dX(RF5F4FjG9gxI*S;lD;6v$ zg#v9HB6q!baMCDH@3TV8+lvd}g<%7Ph^cE>0Ht@f2|*I_=CqDXZ_5o{IcI#c1yzOM z3i%;mnf1V-wj)eB1S~4BrY0y4XrwdgT6mGOk{P}I(|7&kRGsg?FOUr;(sNPyFGNM* zzX-=JaHlgN3m!q*VKRhs^PPHTOM!H{Ie?twkfi`7$v{hs?lKs53Jmuor`x^n4|{h7 zC(pcaf-Fe4p#}E~2F*+;DJbht2RyxybxC(oS{r(DYhbjmLcvO57_k~vc@b9$7PZXF zu0mgP_V}SJTUItro00e4jq?YbnJ&zNj2f8E^|e8|Z@WXuA(LgVLF&Pe zF)V?x*E|w`1rP-OFFK_|#b*`^KpBi`udGZeab+qcD*Il|4876xm*5$HRg`%7P?&13 zb#akOe4ost+|kDUb0TXl6)EOU&_ZqlTH;wn)!$@zdOy6kUKH_6;FAL=7F|rr4Y3pU zBZG1Z4281n;8i81XsX>c`D9gI#%~iCC)&cY=x(&B13j(ia64+bz} zqsp6hmSYaX+j0P2)hSnd$8w12Y<50AIt9_vI@qQAqK1B7a%A{kh1I@G709VO`4|sJS&?23NoGF6Q;!es3yI$ z7vv-Zfs{;no>gP|FAYt999=`Trp15$s>V5!MMnBMj&T)G1NrDiyB?nBHyxow4ZhFJ zC#ebD{3B%oiVBT2Fw@)s(Z)hiG&b%Z$lqNLfkLLf9=#(_`J<&O*tK-hNT0+w9iCWQ zNJvG-pGNL|Z%=HB&T`+xh0Td&vn`+$`X=S!zZ)Hn`~)zxZ)2jq2FLOhSm9_xu*sV1 zqvDs<6&=SlBnf8stz&(D;yII@b?nIR1ae?4jBXa*uLC;Mw@{tIye4=~VMl%px!_F3 zR1cqM2;|t_awmX)REisho$mv35KTu`jmUBL0L-^gqnUynfAi=E|C?8`kf3l^g+h^r zBHqn;eCD24`$Bd2TJFNf&n0pn8P!5#h}8->@BLVTexDUH&x9h{A8j_zD&Vy528Q~V z01tyPhr4Ak5OWxibdFkD=2${gRE=XA#9m(Jh0B1paX_AfMFdvX6Z>dG&D3u3zo)w7Cj?RvAo5xT9?6TPB3hhRX0DQgYW|ex-5$d z@#yU$-e*R$xFIvVhEnA4A3G!8b=INx zuU!lJI_3zX;a4a_2NX?zjAQ7nq9Ov_`QhEp$AISKOh~9mh;!C}-K>1U$EG*;L?-VC zQH!1D@IabS_+gI&P2l1M{xhJuEwvY;5BT2 zXIqA<&Q&wW92ZVny8ET61qHC};WzUNynic>dWKMd0aTYdVyAP`GUM-@MY{0j*81Xx zuKfv}SsO);T?Tyaw{%@?1b@OGvy}QZq%gL?TU>0a=ItOxF9ZY#v=W#$3>}AY+XEx~ zpR#JMo8j@qR-9>wnCtT;ASHg-Fto6e$gbB8A8wzGZaSz&=I(_8OVp*t7v5~q-_xO$j%kx?g!z6kR0@-KrmFoU`7Bv zI|9_nD^N~d&9Rw3Tm?Sg@dNjNdq2=N=)z2JLAdmf0DSB~!^QOlg7oFB{htGpFOEUu$!5d`ei^L&;`u3;~}WlHRG7Us9Tt{m^2+0a)T&;QjQYlk#shyomT0{No;un084PgFz>jj= z?v%m}JR?p~Gun&3q$f3c{BvH#BkP_E&b<1Qzcgr}hB3{LiGuKJBbj<`Fq0oCa*ZTy zf?SN=g&XsoLSNchy$^Nh0+3q2$m!G?-Ey9oQ+b-mecXPnkzp;y*p1B>Rpp1Ms(d4- z3hbf^8%i7e$St>wq6uzayx1NKs~6gN2)!`$ki@f{$nTA%5}M$#uC57mhA5qXn)|BKgz~^Z1^VZ2gSCNj&pqJd6&Wp>CH#p=M0re*7JIU3999+a}26 zBCF9i1I+=1q45G|QqNlsPfK)bTdK_mPqv@{BRqEd#(k5b2o`k&opu_<+tFr0+w5Wz zck*XQ6&ykRwM=5!`>{wZxA8Jsm5H>yZ5VvB?SyX@=nZRAUraY>rL2TyeKfs#5X8&l z4T>2IwyOD_62dXefR2vo>Z=)PoD}-8z<9y7XOXpm85R?{9;Vfn9dVU(a#O7j*Em z0H1A0Hu2>8qrE@E!NzI<#1^+kQ}a+&Tq@J%VUTKh;2f0+Jl%fYPwG7G@Vpyef0kv_ zc5YWjQ5BvOO3;34gG|pHh8FW_yBR@|NskQzw!?ASBV0LKQ^ABe%ghu33A~d|PD3(D)2PC9SqVJ61CKGMu z&3Ee}S{z`HuXhQ_=BG~#Y`k&8gtJ2E(IYXG5J3L4*(mkwh39>i_qH<}cBv{=Dsocw z^rhDgCRwwU&lnHG1g=6~Y-3X9{L8DT;9knVPa91B1v%*Nb3A(=Z?bP^)YN}C?A-e4 z1s+oL8*eN|4^3TM>&Q(ec%$e~k@AcE!Ue3!U2Uk@@`8$K_rjZRS&oMZWfMX2qh5y` z2P}pM%lo|Mw!W~M<|4w>0wdVkYU131nV_glCS%P;69+y!W(oC??k>q$vOx#!q)k!t z*6)pkE^BIvZR?9ZgAjZ<&>z(_%g#?I+~rW4eij6&7GvYto8a+}p6tf8ze!)V;=X4= zw;GY1+^)3K8EYa&KdA}@W$r6~cQCpepY6o|W5>mNAWXt8pToBRdD*2v2G&V3N~bh^ z7)sMGkFd_{vLfnm%TQz$7PZHb6TePZ&4cjl{~DV*b#!q70E60v+~o}{>*{=K;JyuS zw+)YIS|;uE`B4^N#XMhmHDzcQVLo*sa~@p-&z`0|kWO05-1qZK3*Dj_OQ`C9F{}}0 z6>%EqeBHs_fbUn5GyG_7s~Q_YXQ9fB!@dL9HgC%6Ay^K4yPGH}U?FO?%lUzR;el}ZPD6G~!@0aYda7i89}Fc@_*;!aTcvQ)z<_A}Hs)}2kACLE`1(&5+|IqI zW{*9?r(;JPR9*`wsJ*J9Z%4)T7{XH-jtC@jOu&uoZiQg<}s?#j>s6pJpASn3Y=yWK`Q z{p6KiXU=$c6cvG|SN;qM`Vm^6z|8V+mZW`huxqF>5I8HuBG;jX`iF)}3)&Y#D{eg1 zCNaEpivF0ubGP9-K5MXSqhK*xQc8F~t|bqGeOHi1)sA@T=<$)EOd&4>D3+ZEuj(*5 z-JW8)GY99?<&)y#S;mMQb95u(lnKQsmAv9YG>WXiwR|4j2vfknP-$6eNFGu45q2;= z+N0|>??qroLHG?WVv4F^QE8!NCXY^J%RI7F=9Rn^8QETC+6&8Xy6@gVIL1r++72oc zii@=}pHIE)(4G;W+h52C!emOO`~UR7k0Y7lK*(+LR3pze9Zg_>MsxX$NcTHLzR}6B z6T6}VCkc%uIbY&|wj{&trIA4U^ina|iG1MZxGV?qDR+wI@38SkS$lA#?ZNJL<>P0H zBP93kE+}3`^Xw`(DkbpRAp%e5;eK9-Y{x7?O||cI%cm4IlaY1y8Wb;M5Jz3DmUy>) zY+TBtJC%$A<#Hu*wo8E@z5qqjX@Xs@r!;*Q7Q9PY%D4mSK~*SN{w13VtS80SRja@p zG{-JFMCWyuQ|hI`C(em^px0FEP(?4-e3?BT<$@cUX$;mb4XA0w9}ykL`aOK8a@_Mn z?%7+Xh6_;Uy-lLUufP$x3D4rCcpffByZd5vhs~6ETjWS8)>Y#`=k=KlNn{WY$u7*n z0-a_Ce(w-Qn*J|yc=UV!>??7az)B~Pf@@bKvy=vW4t(01(WHgcwOI-5GE?W0_l-YJ zERDt)OMfYvR@X@xVS5543oenJxNop`@WXm<&kL*B_(34=NxP##yUUO+_$=77ntcWS zT^&2y=s;z`ay&Ra86~)>=`p5YoWs$FH7(#x7m zMg7TypYqwnyh7gz1Yu*7jC}U*Z6v?6dnj=oPVJ?U3*==7(h?u98EVrjy&>w z_^C*N(nBbk#n4(i6(M|t){UFim-~*c#6z`hF#a)yVt#DW<|G3b4@#L7)GyLV7bwVy zq7e@DUJ^*-75hrE9NU^P^=D%W^)_H)xrJ%hKy0=t=$?3=zWs_b(kxpBqfLOLJqt*n z4+|lEs$PZ z%ygu#Nkk%SGV#qwY7*(ig7?sU2WpP@Gl zL@q+~26-Z_tEIp5C8ALCeh&$WkZb}w*OKW|YD&8t*zhHQZ0tp37JxD>5Krd1rV`JC zFgQS zyVZ=(SW@_I!%I{$E-RWWUsRu$^H9lzB@=B8^vi%w_#EJ*y@D?NJ{t)opcLgDN)Gl? z{#vuU)YVZj6n`AK(qFftS7&o@y!3 z0(Kr1X?T(tO*r&qNki$3fpi6!-0)jtqChcoeS-d7T-S5tx^^B29$H#gOIU)4MyL@Y3wUqYBojE8Ru>Wn>P`1_7hl3v&9VSO1ql*J}Q=5 z&@xx5^+$!#+^@jWvvFDt@L+zJu+q&<)!r27FHfbs!2$H;HDc@*FtoS)aLdl4Q)_3g zY|#gLZ+j2Q%;n1))F)b6{#NEG>O#-@oxtb*|6zO!l;}uA6pkl^*)^&&Eyo$24*9uB2yFf_uo-6lF z6K8uPv~lV7k&!LBCaP2Ns_H|Cgj;& z3J-xso#{u%)d6skKE79t_ucb%S{n&;YFmG(PRsK1oD6pb;B)gup2@=l5LPYgS6b3| zEn|jyqm}jP>x(_+o`HeKpac2lV5Gm}nu3Wij21;I*vQBJ85+Dtuxwobq(wbxA4St2 zqHY*pmiRZO43N4D#<>I`cnJ)16(|_*p5dRk5o0rW&(gD7FUtY~y4P?4JoC-dNLWjt0H6TC! zyZW**9$3&F72sj!Oy0=k4+d1QcAqJF2HJNH#}AP6mv+)v~I?6%n06!kRPlk=JH#JBf z@XP~rmWm0K;ey|BKh3F+Y+Czr6o`n?kA7%g-xtr9^i=QaOC5%U&4Xq}=`=*P2vL3+ zljiqO6N++S@8}449eG6{@mPwSZy5Z%y_>oQ!br#^GIn2*igsp4!_Jk&I|ipHT2Xf} zfxtda1;Z_3Oj1n}WskY~!PUtPD_4#^aKw|nuzCt0w-%%VEtS=*&SdTBuAkLt%SDkw zBSwAnJmL^zgpYV1Z%7patuls*kAKu&BO?jU?XjOyjCjv8JIv&@7dgRTo#t1scGFkf z?Ev(H!c^e4cD(jlgz%|Qu()JHo4JL2ylMKT<9VRm@gy&dQkDMk_;KeXdr>`K;1-t+ z3>>A4ir%wOeZI$P4bE>}f8s?wn>B1MEab(Wl-U)Us&m{YUj9 zjz)+mZ6*Byncr0d13Lbp+B6WpLT32i00m*g?}w0=>P9A^4N3(aSo z(_lGEC)L61cxEHwh1C{Kn1iZU+w%|qXNcSk^57l|T8@4t`UQ~t)PUL&sHV&hK0cwe ze@7T1;YwmEh;v5F3jfI_7zr|Xo1xSox$Q5Y|Dy^yQZW8&&rmMUBY%A2(V^Qu_UYAt zvD^W{#%I%6<2RK~Z*OOG$RkR&`;qc+r8DWTjX1cGH+3&dLH2tE0OAOT83PRhs}f1$S6A2PCCK+kD4FJLRUSJ({PNF#9v+=&=k9ad&m{4X zI{|p-G!`uSp`C5^$))+f6Ilv*YY^BP)b)Ca`}w9b*FE}Ol9M&knNlXtepKJTKY^l} zj}Z>XUZGMUU0(*8}Z zk6`sN^pi%>41QA>#O`@lx8b+epk01DQGPNFF^um3Dz(@rg~C?^x093SH2woj|7GWi?7 zeDW7)3^@h^*S|NE{wdNw-{p9I5d;n|LV+%e94z=#tW64K{AS&w51-E02TIGMhITC{ zJLCx?nLmJBvj2+Zp9jUA^=))XUPA5bw4l>*TlxjtxeSm-+3aKzJ`TNK^K}U40&SPgLox5YFlia@gr?1h=hC690e|W<&-wC=Q z^FuU`+<|Vo2`G@2SS)o4MU4OSGM+89XACdN=T={KnIo0SxE#C#-2-TtXF-MTlSh!t z9-B;8*VpGJcmv|QmpdZQYL;;|Pcs)`wV4dB?uvOsLtUMjjFA?+Fz(cac$f;LB6%Zj zFrxmm$M5BV0b7ALFq@&o8CL}3udHo;{S*l+iII5$-jK&uFa_20{~55%Lxao(2=x0^Z;O2$}lsDePboORZ&)}5Z}E^_r9KeVrfDm{IF zNB8?dz3zSa(d@;y-up(1GG}|7QV|3;>(hp{D54shaoua)YvD9Cehw-h6EPg74QH=iT_y2oJY@;lArIqsvh9X;nqDhoh}D&zrrOZ~!`A@0=Df5Sw~q zA<<$U$;!ssJhwYF+}Vj-y2ER!Tp{q9wZOPeFf6tfkg_tcR~6s^v|G?2L*;e`f=)Dz zLD|hT)+aOS@X7**c?eBQhNbMZvrJ7koh~LVy{3&j(R3!v&SrvJB-ZbwBcbuj>K+#D9jT8e5 zrj+LjJ0!onu(C0)_>v`+szS{YXy-IgQx+qUi(9YvW&2`pZqBe9&`5#M_-y03tlHbt z+?RM9Ps&83TMD6eaTBeH`v*AD2WZU=p!^e5-I!x#tSixg@yAhE*yt$wIeT$d08~J$ zzcqh=_Rtx$M%PL+F8Rx=$3o$^`-4t4#Z1>2<2Do_Cc&5u(1!k7Z;^N4+y#*A>u@wG z^C@%cQ@ugSXHwIFx|qbV`Wi3*Z4xCW9f_p?!uRHk>(bi7qcCnKi;EkJ971Z3p&IQF zG95EsE_-PKYZ+dc`;&Nr7YlaDetFHz@2Tzk_ES?`EofV9RRJOCJ2GUHTvVUE@C@v) zwKwk5C3X=Ya{>Ih7<$Pk0e#C3Ha8o5)igh3a70MSyk?iZPtMDuatj#7jb1}h?Nm$4 zCevGj2a~y8qpeA0?pXwE_pXg~*=7QJeVt2W8`8Q4kO=c4^7s_Ix{vfm3{bbCY>TH& zq;RyZbP~qb`OYP+|MRa;F8bnEzQO9|4VVuXU;*=i3CxTt))1sF4yr2u{9j&wvTebg zcb;X-5Dm&k!nE1{7n$Ekx~+LIx(*VnuQ)~e6vzWk*(~PqjE!k|_2fz6jiDh9L@fCm z9)38tfRn3+2A~dBS_hDJUenNn9KfINjK+uK-FY{@YZ@AmFfg2!&e{?3UrD4Cpr01a zrkPWtNCT4hB&#vG%g9yz&ZQ_unY%Qn+|W>2iL3*Kwoz&bx#Ppbc!=2a5XX9?0#Y;r ze56)mm^$D*i6Gz%z=le}cz;m#dUIV+9gJDZYfLzjldenl138-QW-B_ZByyH zahilL;)aq-gd{*%T$cS57QI(MIWv(rw7CIW;o80Zd$n`CNNXVBX9LM_y)1j zB4ayxO=>FY6g~E>W;**fg%_9GO!||8NU!GWr~epmhmwS94Q3AS+k7?V!030#LcA}W zT*8~k0JF>l%h30Ou(DOo^lT$@<@KIOoTPR^Ce-2`=bfEdNQ~xiSTXa;M(`Qkr1`<5 z+KNA&2FH>4wrJ5JQn0ifin%MzFy%VOhAmT#3=gMvpUVL~dQKm;*K}I6TShzs3?rXU|sd3(dA;yZ^VcDx_daESmktFW7^!Bi?*3@MZM#) z&wS}SIKkYaY>12l@g1(kIDvw`B!`1R6zu=^&i_4*eBO=k_19l-u0I-oMCFDwyUpju zf>WEJ*}m2xXXo5f%0vh$inTM#c6SDob1DPFN1B^6)j$A@J-IkZ7+&%tX^kb%3!Iw* zlB+p|$<#%)wr&sDY>Z?JK$%wm9AGEkgm-(wO!sP(55z~hb`hMRNwfhPZr#lAys+Ei zQ@9}>i+JT*R@__aO!p*=B zf0bo9P}1su4#&03%6z*?ihMO?Wxl|i#Z{bPUIs$hPoU~k1Ufr_*Nq*($MztuhYEl- z3!YU2$C@SRvAdg>l%2SSgQE|`=>50~9`VMZ@H5~Po(@Hhd>B#4?pTkZPyO*|WT2Jt z6feNvv~<)Y7-VYUJ^xxPYvw0KMT_k2?|;tkDSHBW>=|&gi=jT;2(NJhiY^K!-yjx% zR>f4;9!*#;-8E}g?)#(1C9lLVU0r`s4ejP9bB3BPu_%x-#~vB6!AIj~`!kQGI<1omD45L!lx!d^;KqA4K;E$S18| zJDZxekRqJZg45!n!Ydxxlb{WwKBX`lDM9BkZx+d-dF1WEm|2Sh+qLvi+!B+Z6%F)u z$EJjJEioc785#QacBph(6J}Lm;w*qlM!RJwn@2f7h*5v@)X41T+6Fr?k%DXVXmXc=b3D)~TgUv<< z4;?b^f%X?EN|pMH&H-2~yC-;U_iudga{j-WbE*Ug#Z zRPEkR%8W2UHMA~~VxOIyZy!eYjng5SGbmPA3>l!S5YE@b*x7MHYdFn(I;@C$r_{p;5YAr+kACD#-IE6_vF5i6?6RWnxoN5yTZKz6tb-7+C$*lefYJ99!g&QFJHY6_LKnXdaG!BAK>+LweL9FW-eB8bIU&!gn(Q zuV2b2=A{Tn8&it$J4!eH!3F|SWr+$IFNZm82l#q{h;-B_(a||J*_J}my~*s;F!5N_-1$b-yn(#?qRA&L#r&F z`%}1tS3}#^zaFWs77?9|Jy+le`8a4#mtoGe#jWgb2?O5yb?*P@e$R{Nd*#ZN4kH-- z8kQYkR-p>UP&wP@}_&xZA|6hYM2~7nbP8)hccNHR|lL% z6LinkuB8sNF3+=8p1EOEX(?16S_L1JD3Ch13ukdHLiqJ)iR*4E^o;Boj{OQ%^NCR7 zxE(Ru$sN71c9Szw0J0ndjrkKtV!bRiuaxM_<&0OJPbgEe#{J-oEki#hB#%rx5nD$3 ztR>;_G%qcqV&qy&A%6s~ve~Sgz8-23wV3919H+?36^+qdlFyB9OD5$F?&yy{3oneo zd;Ea@T(87~uTz0QdlSi5p9%TMgR=CC_u1fCBM@UIYl5}Z-lski$#nlLzpzm5SM5dt zmsC3cS@%|E~Vvpi?HfmM#7A)_2!B-OC-3(;7@A;RGW5O>1l>!Ls}1ZPF9kx@e)zqdCT zH=S^b8X0)XxQA9-P~joiX&5QX4xv^T`uV1@0#_zwqlYn8n=wFRx$CnA>m%?(K_0`z zOtz5MvrawEF0o_=sT%VXnLDc%EwV0O@nzf>_D0!G_iqa-f2IOrJ1)yw{Mj$nB6|Eb z*0n;_)XK~_&k`a#t8OCcl>-?I-8DsC`o50Knj=T}8gI!I92!titsb{wg2d9Bn~?@= zI`b@pet8R8$UhE7x*8L zw)0|qQzv?zPLF323~C0Pc7L-_!_F}5CsyLL-ls= zaE!>R!-M9(gS_&fX>hL%yW7wWR~v5thx7O?6sCq#rY2H}iN9$LzIrgztt@gD-^kJO8$-H-^K#~d8@{^o z?PEQ?9#N(%<3$a_4wjj##j|$7E$&@|@E(U6dsxFms>R#*N0e;n$)@_soYA({EGO9C z=Uq1W9jv9P=@80ND6K5CD`OeLC0hhW8^9Cs$n_$o_DMf$Pi7*0Q zvM-|<1>p4X;@-I-XW{^A#0CSBCs3#ZfB-h5(Gp0P2#fu&U4YIj)H{z2QYH+p7vwB3 z)`lX3=?DD&lR<&nhTwuev&Nk>QXh>UJEPHTo)e6KoRCkMTE9bJo_Wh*i@@_C^%ZLS zK=c%nKHy!UZ#R@|$=h9tUpqXG2qSUdgoA*JDIG;5=krrKDb*#o-~g}L1Unh86HbiH z`#22MVBjwtKo{8cStVPA zd}CNbkG-MEn^S6Ro7q!5K-Ex|>YD@(4=A*8d?p1ReR8oruQ$G-xp^mav{IHC)`$L9c{GUAJx>J!$DudNEZ(zL#6ch>^hiF~f`ZV;k`huekHRnZWbj zX(?JcV~C&Mc3{J^b7suLbIE-Z+FyCA;i0X>s+!3vZOD;~pvvyRy0H(!g0CcNYg1!8 z8~0`@^0UZWKLGdEOm;@M$`V_TiHkfdH8n#S1SZx4o{HRe*9|5EPKrU|nnC{8I4FwJ z_IZWxKzYXPHurYp8q4I8~9-4L1nyi!Ir9ZU1nj)sW>I0RGb~nF`}eD#(C1OHdBsK8_h)auSzRdyfEq zc7hZ;$Dh>P5?|kuaBLSA*c*8@@xrjf@wQ>J1*eux@)gLA{t9#xVhk*tAA^RSLl~F6 z@$}#;rqG}zgS|{qi7BzN-G=TwKbi;fVP^{s_W$GVO~B)*uC(FWm)={g*1lPiEnD)6 zH*CPj><)wwCMFOaMQ3Rdw&V=brPP_oXW|`1%G6vtzThFeXjt zB_oOgRHzel4kjGXn)0uCfO1XSRG00zz{i#W&!p|Z@Sf4`)7$!9`28WMI{(KN@Asl| z=Oqx+cn#BVeK?sk=zt&h5wIYh?apb|9)w`_AR>5~%n9qPoE`(#`g#naeuQu~px7nk zb&)8!$L_a>Bn21x1*aXS=W}3IhNS`Hrg*1}^0U5{1qRl?I%Bp zz^?sK%$F-s$ir%ycK+(ci?=1R)B(ngzz9p%FM~m#U=V0oYCt`C8*ERb6E?TkPNle zv5bungnOkX8yzF%+|$P($c3|2Y>|5wUc1)ynWm;J^}qx4#+z^E`A|sW##LT5LrQs- zSop(Do6LE#Y$5hWqhJB-3+6VR7A zOpYZsj;CKD&u3Hsc+V1k3w`W+Kcq$RHAqKR=($6$#XxD z$Q?n|WYWch9$j=7bY(LY#OcMwSn*_Tk1PuJ4z~|>SIw^6Es4%gK!+LzrwCN!t@S6O z)FbnXUB0e(55jtd&fx(a7_9;|sbr|29)Yc>IIE*`u5MJ4Qx5wX&s}cQ0(=yjFFd`+ zW!Ra?Quwe_8}Kd;w3O+4X^PtgL~{pLI=_Xg!2FG!lf+xk7jW&3MvJqkx3?Gy;0~(0 zwtqlF`0iXa^PC-rIafz&#?t25D$nt_Mty^0*`I@5YOW}7cTG^BaXh(ATFGo8V;#f7 zqLYyKHa_{})Idc{c1D44kyTW+f^g?f?|-(}gHZpy{Ejaz28``ii5EYA{nDj#NO*zl z3y)OXF*Gp?nAh7fE`Nh;vrhmy@Eu@Qqa4LNpir(m1h2`1IR-+RT%#{gPnIJT6F1rz zmAQ{93p_s0?N803_Z`xGWlO{O1yh4qpDu z@qyBK0Ln^=HSQH@PxZPt9g7&u`^f|z zi3sVk5&z!D9y8WL&I;-zv8yUWt%hSiXBf=EWVRY?SNCh(l^aZ0UdNuFpCJ-po z2%FO%#e4F1kFIlmpU{jxU*Tkxl!q33a3T3??@L7DnrO1sGs>$Zw*X~qT0mKU5`|79 zh`sw~g`5h7Qm(4?k&n!q znS^?7zEgZ;I;lYHc(tL} z?hNbR^Vmb*)v6j7eSl+x2LNfh5k>=XLVFiwWLj*C83yg*HCUKHnV7!_#EHeAUX)R- zv9TCc0+k_G=I4H4;-ja9FxujoxkHImWj1S@Km4|!!V}LMf;i%?4?eiZYHqH{xBcOO zD^?v>Kq1Pm1isnFaoijb%3gsG%tCmrG8{4qRCnO~<*!6%zf!j{bL*E}#Eomo*e|~2 z&P^YeTqj|8t1FqY3`wUC;~L7qUoZ?-{yi{uj~N;LCnK56qlNL}gLDD!6bXr#%scRMYL1xTm86#hp-+SwMwd9uH5Kh1{Me;I;Px2Z+@j-64xiM4*fj%FMNS zMtX-I?>aoVa{kOwMleB*>x2ZG1K+AJGsakRc}f8Ei;_8>?k)F7|09ZEy;q3c7?2dI zrUv;kc{RcZZpG9$Evu`}Cocz*+G2rW???KzsIG5v&x~SU`hn9r20>z!dWzR_6n{f- z){LDEg@x07#~B(+v?biqzJHosm-mW>ER;o?=_Mi~VD7+#$p||ybMg5&^l68D&;B}L zYAWd3Y7hzwfvhUtROD^P#pL@c*5i_G%0H3a{0&GD-vFZva&qTu`1C2PATEOH*-h{y zZ^E)aB~K3T_~48g+Q)Cdd)O^7$M!zmC_gf>!+RrW8EhZ_ei;2 zzS)ObUK&}j_`;GI3zuBdJ(!LVoAAicKyr>0-o5JKAC7!{;YD@2!!ATZb;y;>zE)o7 z%jZUhqLwPT*#c-QHbM;sbvx?LLYHg0PyE$wd%M1HPK_^VB=PEQyZ(A}!|@}dwOXiS z^Xx#X`AkpJA+prd5Ax-qp^(d>7b=>06lMO9iE(cAx!9dyw|GYO5^Nd1SSEWPJg%9* zOh17ey9smvRmWLwc zvYa2V3CW4S~WgO{WS%#a1YuJUhJ_ee+-)X%1qGNKCUbojH7U`8*F1;8y zn{r1rW-+^>&w@}(fHeE}4f-C)yxKln9XOYDC6$f1jtkFR7<0BjnACm4XIyW9I{iRwBsyJp0{`T0?Q zeZ7Babu}B$GUD7>GpmYup_VhKO6b%z(nysuZaItcqDcUrhgn1I8rt>B!1N@G?s3+w z`DFk2^}&PGjPmjdE2oVg99EC6sGLfqcBjaG($ph1h3%ka~iUM-+ zwYbK+kf;g4xcp&eft(M+?3|eJuBt3f%)taZed7MavG*}BS60lUvaDumEJaUYv zR+I;XY!o#)VD>LZ;x0JKQaftpsXp;;damar<~w~sWSP5fG~M`0MGmHzt>P|f3CBX8|$8u>PoUKftjLkTvvd)p8yU;F^%e z%_m(BjwI)R%*RY;vcnnCOp(~LN0Okx#GqhWsV8z!IfmVb>wM2|y$Q8?JeCoCqBhBf z!#nJR3dkfgxcIc>C&?+iI+E<;ft&{d=MUgGd1PsMMSKNS;oun`xb~iVqUns|g7|Bp z5=R!Jbn3R?DNH?xx9Kh^!AhSj@C~W7G9XK?nRp&Ot+s0p%i5J_XfL9-$4%Aotoy-9 z3dwcrXzI4RZvhq_4m+aNmeq}McZJLaUqcg*gGgkIMvR7FSV?#E0$|+>0hk!ahVe?Ft zhhulEx;d=V(A67@CUpDYcGQ)bvyGdWiBb;5c^99qk&nWX2Un(8rNW$opd zRViw*g$y?N<&2FG0hLO=IuI9C?ujPcMc+UQPp&zYFVo>$)Z*UYZ;d9OJvmi5_^BlfkJVv*w$57z%2s% z$^pkq4YJwzkS+%daLJu7+6-;vCedcsS^L2m4SyH^K_;<1F{O%?*~(Gby|JjP>v+de z>UjIwMsv}0$l+xBu{!HV*?ONzN$Y?3^3%ge(&eumhM7#_1FtB_pMm}NGD?Cr^*gi$ zO99LnO98^mVFBr&AHoNGhmOB!121AH9=sqeLITe;8w>ykq@0?9Dg2HA@qAHR!Dr6xcNA@z)9im?1ABPv_9q*=uec%-;i_DR zXi-=L#xysp+b1Gbl)kp&e})v1k-sNKy)0SjXTUPngqe3eDi=4IJhK!z@vVp%YXoQN z7&`7B!n)Y>?xKEScJdj-d7BXI_kqdsMl(J7;=1CJV<*uV6EJ9-{qB-W5kFmGptJy1 zqMxdWT*QDMqBDh)7dpH~rwHLpI=pD+{RvE9nvy?YG| z7-tzBqU1xP?#kFPO{`sXP_;Ezk;HX!B7M{aBIEd2cNb*mHL40kWkOD<6WFkNLz8iP zKpG&Y=-KkzXgpp!vheCzKD3X@=zcy7r`GE$3<1aN6Bspsl3x*|PM);Dfy+7%|7c(3mC;JJ>Kh3vb_qVNF_*uBCKtDG=n>i67$dD zOQ)1CrVl_8hGB1!>bAsVdJGvLzjY9B-FxA^&Oz6L*X|sG4Lt~Qre@%+y9SfVyz!0p zbqPhKB^Zw!Dzk;+oH~AxR7gfNmpcTZ8CGx|2Ggdb`Te9QlfZG>r0Ajt{&y>O&&R3J3_W9fLE2!4e zJ=tu|qd6<26#ZK~CggVYR$lakY1XPzc`$=m=!Bq>Bn^1rfd`zex7`JSUVbKWGC*xm zUqdu8lA`QAXH;~28@kuZQ9Jyi&-S`5bQ2xfF>O*vP>OZ{|}zoh7a`$1BQo8MhtuunCq zAF-_Zt!z)*i8o_VPixv4zb-GJQqbmnSVvaM;n=mBV9kN9CBn@5!-%VXL(y|TEJ*u@ z@uj@y(A9MEC>EbbscaR4QZdTVqY#-qit^w#Up9KA8BniFXB0(A znK79dr`wB=kJvVroY5LdEk6)VuR!5S-MV#CC=_xn_+0YJ(#L`4`XZiv4V>$}fB|j< zgiVLc6v9Iu0#qh;@QkfI6bgWy>Mmq#KW-J+ccKX667(Axj2~s-SRMxm=|L}*`KD&6 z-_ST}-v~k-#<#?;BxrEZ1DqUIWIZJ zdu1ceg5^ji)IB&&8HX=})%eMc{638aB5!5X_#q=c^xM-*ai{m)IuL8ZGyfOF)<0|{ z&DzHM?>}{%>3!agS2W)?QuHVS9fc{cMbu&#My8L_PAUaVepaE(R8pm0;UR!P&QGq_ zX65Hh%knd*N=2{P0qEJrB3D8B?P%ZIb<8siMT66?TG8DeSL}F+wkfH&91UVVinn#sTz|obXf&eJ%UZIgJ*oPDa+8S$^*kmS+GTy zdM9vV%K0mk23^~qIWfEWn)N1SeGXcbgyknSzZT9k8KmB}9ZoIvOaav5xIta1Pz1?F*7uEV_qi)sJz6D}} zrvd?z-9N+ll5=#$EPFC%3YIJD56KlZ!aS-dD+jv_!&#U$=q5L(A0NnAJc8_2Wf#S( ztWfQ7x$7-M84tT?%m-7)glC8)I(HKm37i2szH2B7kT#HBO1wXn){1z+qhSEBMlv1a zz3ws2OvQH{&Zc8)ij4=zqoW$9+cy1*uT#|Cq4cbPyWnzg!_J4fMq-g>k?+B*V>Kg$q7h|5Ny+Qe*QOn4Ipm#xVZ!U7&O-^YSGS!D`eWmD$0v=) zZmcUKp^H3vNRC8+OlrNT@ZQCOYLh}9!A1Xo_<|-2A1VrY$s9O0U2IOv@qpm9VdhfH z!8&(8ZmLGe*swf?oV;@nyuIrzUM%+@`NOuc-}dNoQ3APkTyg|$Q!EtqF(4(TT1l(HY&4pP=mI?= zI7TnzSq==0^|ik6kgt1jY3W4$Op3Z>bTqVcY%JW0c8^!Y_cK;j_4A zpi9^!o`Vl7lAFL7cOP3lXEpJ>Vi4xQ=OMHKa{yf}u&((9CR3Z??8BPDKD?kJG>W56 zS&cUiPx)N#LQs23_!hX1sR2-;{S4vV|I}FZKT%vAVL9e*J#F0uriM=h7609WbLbu>scd~CA z0jU~O)mJE8m<8gQvySy=iQJPAxQXGUucE_#m^>fg)=kvj*ZUX4_`XA12%@{XZBCL< zSCHct^~BXGVB4WR&ebBVmeLq{4&i^8ZVrmBw47-hib35Ct>QxPLnn}6u5q#4)np$U zD@~89_$sF!*`0Ukc1}EB#7BMAA!!N5_ncB6|BRh8UN?DuHuzFSktjd-IqA99k$u$S zGuXUrlA*^>{?>Q}hMy-&Sqba8Fymzku3xaES5wq&;5vL9dJ94abG;`p9iSy;Xj&TXlSkrSUh~kkeNWH+Y9=j%oipcYu zm!(arNKmbyTNWZY#aL@t<{lU8jvKUgJ_uy~AW}T4d+2+>NVFxPV(CT8bz>2=AM`zx z=_%4h1@R49IktCseP+$cHj?@0U?Q=_(49&O*vG7v-mz-&RMpq=+2t{mcqbQ6-7{z2 ziWSte1ry$b6WrpGV99(8h)Tfa5bma;`^VB98Ag4Xute1WFlSgz>mK(2_?z582;%tyxU(< z(8C!93L${Aw0)wSFJ3}PJ_Iym@5)$)0 zh1X>qZ5BoIL1gM5NPFpiYSLWjqQyHl^u~I&-}bp0sCM6=JIpKi&YpMT8vfdI{N2Wv z7{Vux4K6D3`Y*-86j*09&{r8evEuN+#Bk6*2gKl@!ZlXZRI=rgR)`!mLlH!Iy_Sa7Ht_)r76l} z)>D-F7BA|Pb5|EhG`GwY>~SbB%mmR9M>RJ;g5#d@3=fW{SK&;b(rFVZIaY)xAPa)D zh_?A}jb%rk+4j({^;3iREO**%S@Q&zg}rlnAvLs@vg~a%ZS5T!=o~~?Mx{<>mdRJc z6&2aBzR>}AimOEk)X|g@^D03b%4sDDOw*`V$ym0kh)~97Bo@^g_ zvCv<(0O6f4<{WD%>@LhNFM(fm2PCQ_gIUjF^(@s#sjcFO%Mm=6gU+(d#qrPybZXEq zRZX!m2JSy~kKT*FaJ{{XUOT&;Hmt_nWI{$i%U_1%_YLK8`k2gG*Yn<@z889b=kN&rpi$}*NnL5JT>y58jD;LY#xcWAQGpAeKUj zBsQ+=%HTk+5-y!FiaO)-Pys!F3G)iH<&V?tv3OyWv+X|An3qG}xX$I48&H;9ixT7; zfpLb0gK-oEPe+K2BFCTp-n-@XEwka@(l7=(WddWT0TYS(SZW6Rd{;~4Xb!UKufrqa zS(b{YC-hUX$FrSdWo4y`+w(Li0-I>wdplHwzk*S5B^vmW!x}uPI3tF=_%)ebKT zG`!Ab$WkrED!&s_Ki`|kj&5m2yl;9xOy1&#){a6mK$EQfl3ZLLR84&Y3`U6ylIzp^ zZHYu6PeK1PvhOOzioXCM$9Hk=&0veXLST%`ykbpWrkX5gcg8YJFAt6`CE=9m?~_+l z^rd0cUj`iNLDtm1k*Nqid0}~BCy9rXk9Q12YhUh}nAL*N2pkH{ZM$}Y#3yVj5IwZm zJatn})n7;DtCtm&qnB5Ndl(9(unx7@7bx+Hw*5S`(*}40TCxtiQOw1osGNIL7Q|g! zzWL3(M@^K1SNeT%lDxw?-@G{~13+LG9eV#~zVyD6pUqRjn*i&}b|PeCS>HfI|I0L(Z-RXETR^O=hirT=_<84JrSPdMYZjK^oOutH zG#5`2c9y2tJzLeaJYdJW?z1Q(MGlpq9|)^Z_Jir;qtwd?n#7l-UkI>{O)$4mwv;_fH(| z8A1N#QODs*z)W^u(B-;MF~onhUGkSOCjSj3PET4qR3^JRKv@WOUhFE5u(F0@KG7jl>B& z&m_W?_oATneF!_00*bkTWvmv1G9NKC>RbI*;>CBP;U(Abci&ucVMvGp;zX-p@>mLF zz+$T}HokJ<`NI<`n*u~c*Ck);w&F)b8IB>T_o9xQAjZ4nA&l(^T+@- zf4)FsU`t9%m4{E9@XT}<&XJ3Y=V`FGnxnDMwY=KNsogwh?i-3hajj~q>^m$M%+wuo zHsA>1{>e2|zC4;RatR_^ROoZT+v22p;6MbYCq70&T|kqwLyk`8Oa@SA#u{J>#0xA- zAM$~;7J5|+9&KyuD-M($!M#X$Vv7B0w6wdDpBVO%`nML3vSw`qkyOtTZ~W3HfC@gj zVG_$56LN0E(ml}ZD$6L!1TeZw5bIokWM3^uSxp*EZv?um153-i;U-_NFRY#egLyla zp4FhP{Dn;^-ye!i3{|_Ud=3il;ZW2_0vnycBl81WiSP#sjaV6h+*?uZZNQ7lq8xg} zwzXH_g#a4J4coeYVB0#%Zv14u=m%%48^ROg>CtDIni?zGH5w=hc%$F~X#^Wt11mAG z9n=7R^4E}lE%Z9^bgyacXCQ+kd%~(s_#38IAnnih_tsrq`#Th`?5!!qN)y{t$Bt?5 zpSKVWG0PzJ(1Ew?WX=Fb5ce{Ou3V^pgfbNC$8&8Ok7*Igd&V3}4)Gq`g2<~SM2nY8 zU^N+3-Q){z4vZvztuu5EGw~Amg4OU4mSf-rF}QrV7l1pm@uZ4Xmke#JimdizBsY1@ zg7d?+YTP*?mH!+!q7Ogs$mF!y?@uj{DZ{+Pd4ri0^#JX4?suH{zR4^=hLi{|mqno! zpvx3;%Tr0!7Y8S!P>U%*p6NNxQhIWRFc^%7ay?b!Vzs1;i>z?@mY%o?H4oCK-c{S* zZG6eqkPIq8FjTF&66!X#o0FMF=xEh@SiO&~4DV}38~`y`XJ0JSkjv#hwmYK!Hc}C6 z$Cx_90FxMS3q`%kj*cGobMAt~xRy3X_C-UBFDy0{6-vd{m4)FjDOza9@wDV{_j5h6 zOoyXU8-H6nVzmJ$Wna}}(5YYbQ7?zY(0K{P^tw^cgzVqNylL*|;9}a6n02|#10#=> zs>tT%H_I}D&|q;vb}!ezxKx{JTS264A~OOvQ}BUfjBFB-x)-T{p;O~2Wmuz18}jdy z|JGJipaP4^Ytiiivr^xr6D}*N3K^PSgpX%|R3M|8b|*1WWdGseVWTium4dCTvP>2P z>hvOuqtm!6n|(i5+hhQsEgo4YbUyS)^~Wl9cwdg*=22*J?yBcVs01-$bD zQ4U>PUtEE5X0{ad8P!3T^hZcPuZD5F0GY>n#85%J!z3rEF0r0^gB8uBt!7}FYau~r zaJ=!SNq?y8+E9oXwak%d@+T(i@+P7ak<|tNZSNoZVjRlSIGhdo2c{R9nir61i~w^w zgw?}DiQt@h~p>9^29jd}8QpT z&UI?AdlrornEm1st-PV8)V6$zBxV1c}R8jLEL8ZR~eup{oM*{tXOk!X7aTX^z^3?~pLndwP>c+ASkoO<3o! zsC#2uR2mp)jmLkE()gEL99x4MM2b^tP*q(6v``t+&_RLcJ|j5Ju;t>0%4}~Wlgot> z%K8MQnp>X4MEK@$`o_`E?Qi5f{Q`RwFw#q6qIV_yvFC9H6pNa>pxSc1W^qweGuDx^ z@e2y4#(}Lf+fZ!r0$pF&_7W!9`%fmqjRao~dB&9tM-@W#r4PsX-)VfwBOr+@40-_G zJsNNhD?w15K}A~6W-PB)oE%K(?u{sp{RqaH>gkGoo2oC!lbw+7PjAi08EqJHvT_Z5 zgC`_~sNm9GRyR(7#r}DXqLY?SLtD&>=&II!!;=qZ9h5`YK$gei_ye|SXJNyfw4?6g+}@t#G4r@p zG@M(zxDjEMsW|zCw=af|Nz*@oTI=02XX^Whhh<6%7CK5gB=YVOv$(3J3__WG2on^A zLhe{nzZnY5OMr=n3No#uJnjkJWH;M9eaJDKo8YB3Lv5r1O2a{%BaQ3CKTIyb;1X>K z7{r{W%A?!9x6Xz_2>HmFTjVGAou&e%0C3z-?H@_C5!@H7FG9OiT-D-FEh{fiti!$! zARe@_vr8!O7GIUJ(*b0vA8)8H(G%n8+4xQ)5-WPR*hayG;Dh?fl?eItGqm%|f$_-5 z^6(U-?eM@rkP&@%1{eq7C#xDWHCP~N!|(F$!DQc8wDQu$T7j?-LXdj~w>~-OoQ$al z9j?qYjY`SGA2K?FI#o=XPhnA1oJHF@FD7co9U}qRE6qk}WImBQwa0t+{~d~=ozKu| zkS9^Kji(@i^#07*naR6307K02{G!dKM=eU?F$TC`Dx$+iX( zS#^-xDYZ@dJe$pvR)z7k03%Bk@zz$r%@&{GB)y$?$uhFTAio+#A0Zsx455Jh0_6{D}b98*%d7^%;)trB4>c7;aZG}B-1|{bWAkH6tH^zO> ziRU|g&7kp9y1KgBM>(&M$}#=>8pBZxTbp=d4(D5erRfa_0EZBxTa6giA21M(lh3EW zKmA%-2KW3ypgQQET+ww=oAH-XwxbwTfmPCI>kgEw)_theI+Vk2FB2<|@%F5+u#2iys=GhsMR z8Kl<+E!E!Y%cjlxHERl#q~@Y6K8pZ#jOPIk(WMtn8nYbyupit4?<8nFhiM z9?>3Da^{H3&rh(bccSBk*7WTt=_Q7jY_;=?%_GPAp7VM`>DaEpG_|(T`XE^i2cwB5Fi#a87)kbGNeu;b^BRViE6};hz*1I1^UR}TvDmB7VoM>8 zx(V^%0OUX}f_ze^tr@THDGQ8}$%=_)aC_;Mxbejnug-Xak|KCJKfu8JZ@|z|(9Po% zI^%<9oo~m<|InN%BTd?L{par6X&6Qq9>!d}eW@e{KL{@6T~`LU-1WnmkinP0_){N4 zuwoM5`%~+aPiAVHulhY^k&6-Q^&{4_5|J;Bq4eEY3~Wbv=>S?lrN8rt<7TH%ILZ01 z07KFzLAW#wn$Xu#1-T4?G8b#9DTJt;(iNBb5xo5zB!f1Fmo9DJ5|4ZQCOw0Z`8gnP zP?dzyx%^EyRa_0}Z#FfnbOK)8?zJbcd-|O3)z>?gEHPV$ll{P$dm%K)U^#lmrJTC% z6%{}WQR)K@HGVNg9Z@!Qv4u-YgC=E^g8QQeG1MB+NX}s>ZVhD`b(K&LKjT1e^B?h^ zo?Kyl)9Wl_q+ximIze~=vHiW{>JgNRHv`IPs2-~V{r4FB73XC2=v!JP47QEr8Ww=v zjZ4auF+lcx%TE8`H%J!noHqd(YmVF$5_7B#me0+Y&ec$SxC#~9)mWk)hW?I$wPuHI zS`WvoLmg)u07qi!m4G*W%GBxYyx@4SJgR|p5aD7yNso_gpXRY87*eHn;b#Tj8BZv- z4|+|j01y0Ku0Qz_TaZFDvgFDja9o00J~kZfYA0$9f9ZmI##1xXNJ0Q6wiwGC0c-)? zj&A>XBsI1_0D^A2*0%uc|MfZuXrmpGKV5r4L%L;1sZbnc4NBo($C79sjJ2deuT0{F zeh7=TZ*18ZgW|FYBlea_=43nIcupBK@;8CIp#UYM#MDYbmS*G>{g}%wmSU%m!kGkg z%MM}bcoalIPauc;NhDMjV7U&#DrOkm51*2Xy_*5v1IbRGG$9xMJ8;r>0WtCHNPqhF zYfeJ_+DB8%bdKGGl}Q0I$#->+^sb*)i3MlIXOO`77Mda$mh&Zf!uz8oBR5cwr)|DOnc=H>HQqqSmtY7%J%rd@=ZT?(jxqiS19|9M1A@njY zyre=NA{bR@pud2UF4fQ`uHsCY_XJ!E1y(EvjKLfB3s2U%-BI-Ld?zQ=lc?-EV_80Z zP5&ESTjRN_dCF+?#O?L92($dZUK9EjB1RP5f_YhtM0m;LDMH!qha65Aaz#9ULTKk5 zy=P=%fAe_R1-b!LkGu~{F29BFR4!-l{h#8v#M9k5l6)UfG!@_i*x6heJo5i~Ki;YR z&Z*}+-DqNXU+#?7hW%w1p>X~h7_keGT^_))@)783zJS$387DCR2iZy67m0iWBA2A} z^NdZ3jkTKLvhB#lS70f%OE8&Dwu@7T;?hVn;+oStk7LPEP)CnIoam@6*u&6!L&DR# zv2>>2HnQG>of93K>Q=1am6n!VCB8(AHR8i0hIwjh8%{TXw!B_5^-+PAewpR0EpC+7 z#xj!wmsC`YEiCeyJ)yXNIOog<{0j~x-R~uc;ApQqqzRAF1isJv>GHOCr8sF#uUxc zPJ8WQ5-*A?vlu~;HHdQXg%v(vR8DT3!0mQSn=HjEK#ITC2*>w6xjWlRMRw~= zE3WjbBDD#~g;Sx*=Iwsj6#O`*&yWOFv`b#wk zKao=q=^%KVBjFZ=FPihxw4{C^_Fd|*GILDZV#B^JRB;q z=pk!lBtuoyQXlxuPsUqrzx$YDv5Q=8w-}h5OduwFP<6C!;KMyHJ=PO*ZeQ#?(%x4R zlHIFnifRiV>lp17OxK>ZjlQ?^xv4Y1cmEGs##qLJe2@?2D1XIva&mH-$%>$&g7lMt z7Ao;Dmx|F#mUuCTp7Ch@iX;DRyS~}*lB29F_KfcyPiDb|;^iHt#_s~I_kW1v5>FS5 zb|Z%2RD%0~AZXro#`jz;Dd?96$1mDBkiKy9iLn_iW9e%ckMu=gM4^bvUIX}5#H5|? z=M?2f%F#owsk-$7AY3CNM}4q}bCs-H2NV0Ijd-?MTYCWv`#ynZ!-yph%=b%u%{5d$ z&N{t2i8W_)qZ=t}qU(lAf86mnkBJW5hj`{KfSfc1OG};QV8bE?*S};*(AThF#s1-m zhL)BoqzBZCYvJSGgEH0oATW2I#ZgUWCVtdbG^v2|E!f=F1_K5aON|#S+dvJ4qiea? ziRPM`{E}!n;?|m>wkDJ6;Gms+{l!N-Cl-}>#);vbZsX~%WClMtnz~vLe0RBd*H;9A z`4U&z@XxK2h=l^a)vaa`q^6NDqCqdh)H1exgGEy{P~co|FzkP5mi0#(_+gthhY}@$ zKUyID0dMgziVTS6_H6}10m3!;`-zn!!X^mFA{^5JZ#W-TAZ=-dv4=m}iQQl`>AB$A zkjVRF8}07`e24xc_=a=D)3zw?E4B5(+v_h`bGs(8UqbBttALlSqwd%Y8e zLH`0vsQtbngFqEP;C?tWg-GuWj_{fMM6wn97BClHfo7fi1Af73QX87EYi^AgD9|KjRy`g*tZ z$5y;Lo?Ap#TGQ_l$As{Z;BTin5J+4IMPOpYra%8jzM^dq-<1xav6J9O31M~#6AziR zY5Mie?aSp<*a}Vt5rOY7?epgEypw;w#pwO5Cl^_9K1zbW;0;LkNhbAgyhD8eu%rpB zIl%~MKW!@ZCJ5k-5+f)|ju&r=sMYr*RZF1mKl@oX{NM)>#2J2L8oAeAd#05byR&wL zYa1HUFop=jI?fHHB@?K0dJaqBo*Yk)@7_F_H#&$r%K(jn@{(&XSs~zUhKV5?#e5LK z{e@5+KZ1&<2HK);7i3h_N!%Pr4PEOgE_RdF`LUJ-=?F!OoPcyZ$;hAbE^cV789VS~ zMGrwckgPu0|7_c?!Dt;sx$mTS=HH;S`$5a15O;NMhJCybMHBa)7)w^^oRYB=>t_&x zd=L{<2mC`G-lGXKw0~Kl>i{J%Kh9*0|H`KA&MTo-tU1gdL5}_u!ZJCOpfyV#;l>vu zkwDA%xC>aTa=0xy#j=T)aH_pz>CMviQ9OL)MXh>4^33iSNf-8kMD5B23v#xu{Q?Ds zkE3ma@QyEmLdREtlK6L!0o@F1%>keLh@RFTM~px3&63F`R_NX2*xB_X6Qe&i2TnY! zbR;(~EBAJy;)Lw;mV43@r~EqyC(1|&qrq3P2!+5`pq9TD`(hOgkGQ3UltCW_3(-H( zd|)lSon;6+c;QVCkdSyQzR%R3cLgSp`9M8Klx#LX8RA6bTu28_UGGj^zZ?@-1MblV z&`x$rELDz`gAlB$65ovjl3e@Wx)B7xGOw4!*H1m`vmJ|cdr{=CV-4d5#L7=iLOO&? zMVVRi>R@KopL*l-*X`-@o$dQ?=X-k)N+M&GOpatC*whuPcy&0vQf!!4|J~2u(rZ{q z%)+;T*loep|MK~nJ3$6?<-dG>#o5|~a{h5JfMh7kD7)g5pUgW^XWQ=|_1?J(I}rbB z4-CLZSem-R;q5Mgq8_zPZdcZ^0i<^XKNgI*k-h>qI};cCA%UeEF}Qcjramz}P~LnK z$nxXKCn@l}y6lCwPc2KyF;b>oB0vz0=e-lk?hT{F`xY3)ELa@3oX z^50i7dNC^ie4|>4bSia*SNr1Fm@9Orr}p)6tGsnAB_EBa0V<}ulO>*xW}Jtde|y(x z*cbHPW?J^gfcDsDGs1I}ZYJSLF9zWKR)h?0$WUAd6foAWs1F=kzkWTt^!kqgZ_G3x zX;r3Z7O^=4OA9>5r~(f;B*4C~R9KGf9nGxAy|2e)cNw5%Hx~$jgOb1>v~;5c@i7oG zI$e1@V)N$Zn>KYlvSv-aMxN=b{qf&}C-jFr$9@Uw#VFXI zMTD?wT!odl#*&Fb$TMArz5<{9Wmy>FIgv`JYKfcYeg($1 z=ivdLH}95D35YGX{H3JLnMZqf|MlvyQfccs_qLi)$JxdcCS#ANF@8XSigra(Gi&z13|q*DK(R3z~SkbKY44eEJ=vws?-a}%^B9d(J^nyg_`UYP^$F})B3=NAGp z)TQgWU&fM5>_7%&H{bqt9LDxkTypy6AY^*Vus%N+pNrDpHBf|T|;0qw)TP zEEPT@c!ZDf6u;6m?HQ0%*$~mkbMxr{y(^jtqmV0$_l{N0pP%>mNE@MOQCZ~3?{|3W z0wff}n3MsTgvNwm@73BP>!`-5c(&yCtj6v)V$yNY>3<9g)1Cg(QfsVt;;`(IRzinr z1>&|hxjkjiVZYAXZ@=9*IF>$u;K+?ItO1no*R}Lf2j3ia!38>0iRakf(fC7=solw z?PQj_UGgQD4yC_$982>QXZuh=5!5ZZ$YH6lL<^+|-&JD8Rfc<521N8clVwJV5zfJ7 z=KBYmqjQo=Ky8eZk_D+hJUW!6Dc?J1dC?Hq4?otV=h1*=g5(SZKiq!WG9ZMi$r#g= zmeogj5Il6eB7kBY%lbU)+oAl4`>TNizTrivE)85=<+&BQYRCmpL()mvJ79! zFIn+Q48s}5fd*C^+NYdlXC|;P%Nul9e-jD79z4h;MZxO8@~VO!Jl2Eo2d@X#wK!`y zT_{@65A^mTduNv8X0d`4oY=N*UEbp(O3N^Iw;>?$1tj?@VUG>~hxiKWS|yO2G#7`( z5NQ+1+#?QpB{H?e_+rw890$w$SEu5+TTyZMF;tWlVulANI9*!6(xojp557kkJa-f( zE(aHvs5CUmz8L9XXWKwonNA zv%f%`_FIrL`?iIWXwl*RjO)#N^)O@h$AN`z3cJTP{OCI!vjbFrm4AlA&zh+F6;=#q zYebN;$8hX=TV%bcWr;`Kri<6D8_=c_+|yE^Hg6P$yX>kv8oB zHn>eS{uv(LXX`5}kO!r=qIhN&4!RQLEWM^zDjDyL{tHAz>p+OVe{ZDa)cMYKP4AWW z7#>p^kUBh1eKE(|Uxv5$uLz_4o9JS`%5l{HLDlFNpty50po+^e5Paxv!{7x8!-K5C zHRkO=lA8o2wt+eyGll1M=;59@r;sA=)O}Db{VFCfS)6bg$Hc6e5X5 zQ#+7kMFx2Qm8e*-+F1(=RE*hEM)&k~Ru&0mSuEWM0tpVuNo4Tpd;DX6KWCmVxeSyI zN4AZq-1Pgz4cFAR)s>sAZjKeG~vkmZom zIp1GA!$A4)izI$~`fVZ0)eO#pV4alO8 z>Rny#BF0~waa6I;;m2NitYi`nkxj*^0XD$AhOF6O<4No%WR8@!$;^7h4Cmd=Xa(r_utAHOBJJx_J) z>r?CJmGsz*Q3)%S_b6_yso^FW2iQak6_Ruah7_Ks>9GwOBxRViD5e`pgk4Aq^*LHL zI!Qo{eU2zi(u`AzEbm;w5YB4!$i4()y{|9(d)7)(`D%j0LDzQYHEi&4bb-s&+tb;4RO#u_wg{K`vyS8YKwUxP zips(f)IdMxl`D5>j#`C@D2N#ocAK9xvZ+pK?8H!W#T)3A3DrFl6XFGMjLr`-fmw#b zY(VUMPTI8Jhp@>fES>1hp_JA#f9u40xBYJjIgpzVMfjsweSs5!x&;#&x)Z6Qq!Wmd zPav%tLIol!s;x({Yro>?lN<#KDBJ$L%nRi$Fuu*0Y;S04GFrysqjt_h9ga5Z(`&um z)H}SzsBC9^a?A|&^w@z5FWIVzOx&wL2~l<*CEhIhwT&eCcADpI#$Tw2q&2uyR)J93 z0EwzDqSH6scj~- z3>8P!a;=6tvI@pTR840mZR^KSK~{nC(u)xYm;rU-`!<()&xl*?ZjTg<%F>^()&)xz ze|J*W{&)-Y;Yg;IS=9U=m1d5>E!Cs1q23^Q{ z5zFXf;7%9-cwz*QyCG4rMFY0j?;E{q?)cmn5mHfuob~Ip#y03IBXOqLuUeU?oy~Gve%}CY3etk9_HFJSD{^~W z=jTAX0WzHz?nlWv`H)P&L|GvWi$eY$N4w!t!xm4q|Cc9DAQ|T_uH8tRR zUkJ0||Dd?>iV4-e28qYHVM%nrA+bvmxM!}c@Ws9XPuV-k`v8dm6J~~gWq3UKt5u68 zPVXb+eDC%R`~`l$o7o2BbN~~}9)!>bfRT@IA~TFGcMI0&a}_0f%?zfbcWjI@WfV+i zMpHLpk~=!FV{@Kpom}I#-sI(yk9Vm#&E=^K{*kO`-^pty&$;=w&s+?cVR4#fcU8LB zCR27^8`-=$i#$NSFOY;B!+UNw4W|>&$uCOGJjP;9fL#C3(s1D5Y3-$vxZ~zm%sfw5 zo#?7+fA49Z{lkAfCt(M2!=|r(?OX^dNW< zNQcWC^XZ*ncu9=&l;LXW7%5aK&v_W6iv*kXu@(&p4464=c(!nw=uw0(j^hV;NLxUx zbqUqich*%CxOqnad2_s`&Z8e7D}tCal!wQFM(8N&i%OykZ;J;e0vj=VdbcUzW^%{ot$5K28iTIbX-oOsPWab9+aU zOA;!(z{#<**FG_IPV)JCW!w4a57J-+i~zj?{R#H#Ia_&f*Wl6h>9FT1+qPadO~?U) zEU%)1Qc2MSDa4t~ME`KQ;U(8 zLs^A?CH!TXR##Zy}qKC8+>B?&pne% zBT0Sc;k3pb_Dd;@?o$JQ`bL!br9^E|R@H$4CM)_tL_*7GUjXIvZI>-<%!d-lzON3T z6w+Nd0Ym{PND-K28DMj%S+H~~5tCkyx0*t{Ctkgvv5Vtm7yQgiP)p=R*dd2O(k2#o z$A#8bE8}t%)-GFp5yA`iNZ?&F^vvt1`z$@T1e;aQC-s8od2-zQ#AAR$ezO)9*?fc;AE&}&4T_gr*-tX!F z+T&W6z$Jpiy=~XdouStH-HBOlDbc(Tg10BPjXhYWAmfLCSVbAfxdt_BT+0xzrtQO|c!nShd*>tf`GL{+C2qhC@U zn>*C`0b!!~w6S_TIs~|rD-80J?E+;9yjz@v$1j(Aq~6n?&rXct;y`FQF5*0dV_3$` zAFcEu;Px)q{%+$-I{!Ui{`YEFpQ~M>sIeAbXZWO|yR!=2k;#=s;ts#3eZSd+jxZ5} zBgEQQAZ#-T7O%$yP3&bj^DN=rkMq=@}y<;>R@T4cLoZ=LNApQR$unuJ!`#;P0!kyR6F3CJ`c;95Pujm?tu$F5& z{q$2u_6&Pm!4Js-cQb@A8(m2ydvshW?nos(ZCeJ@O-*MSD6s20qv_a4B5P0jTv95U z93DK~h>_o4eg3JwKv@OiZG0Q5u?`}}bPyF+gCtIielria*g{3)UNNW)2%E%NFsAiT z?X5-@pxDILx^&XWr_ZSGu!DF0*q|!dX$2K65^}MaoFT}LT zEoFu}a6usOmO(*QAfb7S8>CrvRx}o)em*!zccAlC`O8B4UfQU5y%m80N|RrY0>Bc8pY&cZ^GaGuH3FzX$m68oM>ESAnzk8-mD{SeeNen=pvb z{g|=^OBP^46VhO_1#;zle2#>k1@<2h8&UR5FDq}TEAV8`Z0Xcr*Vc!#^+!6=a|(QQ z$ZR@;6Z-J%4+4*V+CV?s_uh@~4MvlLLV?tI(f`lgdjQ5!-f830XST0aTJni>2qL8C%<|ztL|W)csd_y?R$nX#n{M0|d^b zrLx_~8XW3>zDMhlhnS|~;_SwO&!vNkBJd`&43>T_@(ClHWra)Jj;YfPO7i*fm{tmd z&xgcqr{2@nJ-zvSQ1|eEd)|L|@on7jWhW@feGpTCrN;m#$mT$cbOuQm;eBYg8S^Q- zJ6z*+#&?c(C7puTgq_g{kaU5`8RsL0csAT>3832D8e{xcpNO{T;iTA_GR1nYl+9v( z|BdIB#?o>ihNuPwRacZ|Ri+JoE>@pa;Nb5Skb_5J>2pw#>4h@cz3b{6*+rp0BIWWT z{dp7`_)>E6(c~-9aYtQV4YFLH2k}geg}Pm^$efD-xQr`t2%UlSU|pU)8&{g{K(b5L z<+?HtI)oq=+rQc|d$|I;U>C}H(^3*4WxHR?-KSXbR#brb;4d#kmetD%=F`gx{k>yp zWek$?MHu6y6wAy7q@f9m$2kZ=!!4qx5K?#%MHJ7aecmx&e}9^FJK8`Xyfskh+^g@| z*}ZhmjInBuC*s8boo?UhuPC)^t|Dz(?ovPM$r`MB3| zyT~?h6r0D0T<)UIV||yP=;D#l!HLh+lt-(os)#ko5KHRxbCcgkBdQIsrOU9%xlu&Z z(&du6Dq&d>gh5Bh1m}fXs)Og}yt5q1J5O7@?t$TRE$C=bvh8AG$#Zv8Fk(&UV&xVjgBY>hshG8Wrk$tw#V}HkjG<|PqC$&B0 zv{6dt?37B2s8E;;w7xO69v;N>XFe*g4cK4UzkEr!I`TI5LW4XG)LEuc*G=!_iKUoj7U(EZU9 zjORjOdQ4};gS;UpQBqM!8UfamY>suX(bPmFi^AtmuEmKSmnaN`tfAW*R zGD`Xr={$_FJZ#Lt6?5vkKj`-V-=6;;j_EWNaqLKjwS% zzE)J9RU-Bc0R{>Ld2A7aOq*x)U35 z4N9axt~Hy+J& zzi>lgQzTL79rhdvyiGg#Vin9>ETuK34WrXy!C3ktpRez0nTWnAS%`3RBIxJEe4YUSkYSrpVfk%r zbUq|*K`F_Wh_vYd@^J>}2-)ZR0=Z7rYE)3I4NP!?=iLMY3U_O6kVs zs7#y?0XRmX(Ub;~D?k@&}dmr@g|M24b($thZ&+Z$h(njCemHrS&#B(*o&=T;$oiy*(((Lxft85cw zLXn%91Iul%EWfTA^4DQtjY8<+=eAJdt)FyPCN{437{9~>nPbmefkfHL0*8S73bmai z2(De?iz9aVg40mvQ zdA;L}j?rVexh|j0CNjw&Br0rF=+B*O|HssZ3f{xnC@b zrq!gA7u_}!l8FxJr7OA{nMp@lp(IwcL$jH-(VQtTy5NKNfv~Z4nA129;5bP@;qY0y z8y-s3Ov_MQ;%Hs~M_4+oQN9y-3N@XYA#mcifurpN0%Rveogm{O;S7@S%886mM14ga zn5>nc@m(UB(gv2X*5h{&{Sb0?1V>CNB?)3-%RuTo&6N&v-!P)Ax%fk+#?UII7#@-H zUy-5pDjUOqamuO!{4k0IA!M>b9uh%hvS*=lNRl2TQZgH7oY11fYH87hg~9zpf$DGu zg&LIMgC}&G$5kd^Y&=5{EfhF=@%5e-it5LL(z1D5EJLas2{LAO^BRPy*;%c9eOsHV ztFx?MDNZkHklgNa2h|bLwE~PuqwzGYJ<^ts%27Empi{GaWgtRd?4hZ}Fi4yz3ok$= zVII<}<$!yFPTNA*&kDoaiXeP+3|_=+g*F!2R1aU0E7+5URe*3%G4unVXq(C11aRW| zGu#1DlTO03Np-DRnr~ z<#u~AHKp?@V0V0#-ab6McenyAZ3Ct+#}1+tW+;}HpIm_|Fp}Wd3Ayxq+Hr}B9S$JV>`TW-167XgRJnE(d!`;ApHE;qow0FT2VbW)73z{Y!FnV)OtZAgrnGfb4~A!a@o{e!Y!PSkYWj>)@^t;rOi4HA~&6!Ysj*J`oz+izhm!dCbB0K$U|Ke&K6#RFi4cDXR?`{5^n;H6kH;9w#{EfS&5= z38*&6j*I8!*fNN1Dwi~!h!HBD{2IYwKMRoj5=*qPlUgQPAW8D`15l*)j*rg|JDmx`pC=G}4OLLMw)!#8V?+hp1;(@58prq0vN&{-bU=WTh zgn-*jc(5y=iDj0%q`s3wgc^k?AyVBiYAh0q6OIMaJu`eZg+M3Cyds}_@E3P$RSOnY zf+$`Be&2r6*S?oQQvLKfIeoe_`uY!Bz9(CI`Zki?QRbF=qWA}~F0zZt>34PQlG^yT z!Pp}bXEgv^f8LJK=yABZB_hwS6$R@&Oh!@FjJFV%U4mk%)ZWp=AC~|cM0P7;ft@<% zNxH`7N^kU!$A*8KX89qJqvj$2Q9&z~S77K(ylFm@@$y6VIg88Es(!UcWD6Y}^I&Q! zK7Pj?xhApd$~W9JPp4H(Nw&l0%*lov5U3qbSUO_UOg`|qBy0d$2wJSHv37&-FQ{V}W3neRoZ@1~#L z6}{q$>pTsg|GW%~Of6lyR9IG8nw1|a(Bh$xzzG0#b>W(r80P%ANwtG#!B11^2n}$Z zopU8KvfPKbsVXyi)NT{)T3Um{Ax(fW1u8ztq}?m1c@q<{gOICl&suW1gue)Vmn{^c zV1?uKCl1mF>py?1H^b5k5tBjQf+}+G)@_i2a-bR!NiJ&_=p*;w)4C`ayET)Qq2z%@ zi#@EV`eQ(090eg!0x_Xx4hb=yHj(&bFBEG>UIjBHkbhJZAqNy(SpsC5c#&sbR5fz| zg*5}Lof(;2YbR> z;ZC<^7$w)ko3fq~x_xtf6R+*)tFFnj--uGvEAR|CS+Vwr&dRJcz5GjG@{fe6m59!2 zAh>yxQ$l#(OF=>iUKhnCb#uTDf^b-PUP+{(z`=bvgM#wAcXmGfZIG#x4FPTG$IoEP zNtd}7_r0ESnAF7=pSpj&$_qkxeL*A=Q7`-2x5g36%q>Th+E}@69Jtn^eM6JIo67SC z+u=H{o#9nPf$a~Oi2JFS2c;N^LAg@P_ z##wM@mnPFE$V_;i>x7jY2gRse;+UYU$9_KpmHGrKV)Q*;IbAHp~E}k&@7|g zL@|^EMkTAJVDg|F(GtV769=0y+4E38&<}y{QwdfwM_=g+DuUfsj=WC0n@{(X>wMS; z67v7}@ir#T&e7=VU6II~!=s6rdq(0jAg@zCGL%Yl1-l&2rZ;t zLZOXUBHsSgTS`sgwkziPMw*GWh6^H8Z#sB(0QC@2oj3flKI+`m9;{8ItPO5~trxJ6 zSh8`Ha5>=PG&GmbhvbnL8^NoQxb_UEhkj$T=wD+fxREUT^sG{E9@aPfKYLh@9tlX% zXcUakyLg0I$U^mw$q#~ZFOUz7z|dBJ3Odjq zDA9pG-m-38)+6vF1|Yd^bffOW00gey&c~TMPIxy~Gm@jaZtTVL)PZ8CdyssE;YlsvX~%^=XG!Uj3u5k;fvLK4zj*5gV1t3v^lE_Lt?(m+t}~&27h~mGQMkLt=q$s zsqal`{Owg``CqRoD7$Fa2$ePa=T?+QQ7!cl;&+4CUXNN7`TWY;AIhhYg*7+i9DSp9%>s%Bg6?%d| zb%rybRL8i5S}`Fucbzk$7B1*%&Zyt|USRt5>f!)Xx2Hp9cbRQBe$u74o5!+^DK7N~>K> zF_v({izQfmh~}nsZ|iVP5aAStHL8#I1V0XC-zv~>X79ZxOu2o*csbx<@F>heEUxDS zndd=0sP`$3Z9H+l9pzBhQm1K7-OD4Ml$ zDwS@|;n^Kj%J|9Q(Y9kJH$PIxP0y{IeGaYD-!A*ajjPB)OsFbtOspT9`kULHe5Aj> zQ?8{Ldx>9U0fyBMXyN$CCRpRs*Z#=84zz`IS^kZejgtz#zglchzaKIN5Yi(Rl$Y}y zcjG3-o=3d1#&O*5C~IAOSFvi!BNFra6DdB&%oe4oz%kvvpkW4~0XP9v zQmHt1HLTvv&n^WbmTGj%* zG=rzZTM!J7!w<6dzw+Fav!n(K6*J7+w5=bnLAVJA!Pf>7i4jE?+jT=(p;FqsuYBW{ z(cijU$>w(rDmBkehbE(L$otS5Ba{JboL67&^L2;Dlt*75*S@f#PMwA8jUC=S$X6C0 z0&h&8p;Ac73p2s53vZKhCRMXL6c6tVsHQueVlL%)Y6GyahhR8Ad~KsE8)rHi%&bH; z+ZC`>yXlznR3H;phcoPFQI&QRR-06Ve-e|s@6Vg$f?3rA zBJZ9Ui;oT<02;Ta_#Q(-00CJv*+>ld1H}{f-+!l3c{2HSJcBAqfX*0$je|xLh6tj( zhB*TkX`rCT9zgY)c_@{hV>7As0>{6Aq{>{EMGd=FI3_D$=>QRcaG69Vp&U(dK7NlY&iPrWX&C-916*jr�{XQapLL3s@DiJOewb9vk!Lb} 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

    kl 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

    2-cn6gsGIE2zEilUdg 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

    Z5%j-C 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%0A=e`l9AQljxQ1J>rUJsDX<`+@ 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 literal 0 HcmV?d00001 diff --git a/source/vendor/guzzle/guzzle/docs/_static/prettify.css b/source/vendor/guzzle/guzzle/docs/_static/prettify.css new file mode 100644 index 0000000..4d410b1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/_static/prettify.css @@ -0,0 +1,41 @@ +.com { + color: #93A1A1; +} +.lit { + color: #195F91; +} +.pun, .opn, .clo { + color: #93A1A1; +} +.fun { + color: #DC322F; +} +.str, .atv { + color: #DD1144; +} +.kwd, .linenums .tag { + color: #1E347B; +} +.typ, .atn, .dec, .var { + color: teal; +} +.pln { + color: #48484C; +} +.prettyprint { + background-color: #F7F7F9; + border: 1px solid #E1E1E8; + padding: 8px; +} +.prettyprint.linenums { + box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset; +} +ol.linenums { + margin: 0 0 0 33px; +} +ol.linenums li { + color: #BEBEC5; + line-height: 18px; + padding-left: 12px; + text-shadow: 0 1px 0 #FFFFFF; +} diff --git a/source/vendor/guzzle/guzzle/docs/_static/prettify.js b/source/vendor/guzzle/guzzle/docs/_static/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/_static/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + +

    +
    + guzzle +

    Guzzle

    +

    Guzzle is a PHP HTTP client
    & framework for building RESTful web service clients.

    +

    + View Guzzle on GitHub + Read the docs +

    +
    +
    + + + +
    + +

    Introducing Guzzle

    + +

    Guzzle takes the pain out of sending HTTP requests and the redundancy out of creating web service clients. It's + a framework that includes the tools needed to create a robust web service client, including: + Service descriptions for defining the inputs and outputs of an API, resource iterators for traversing + paginated resources, batching for sending a large number of requests as efficiently as possible.

    + +
      +
    • All the power of cURL with a simple interface.
    • +
    • Persistent connections and parallel requests.
    • +
    • Streams request and response bodies
    • +
    • Service descriptions for quickly building clients.
    • +
    • Powered by the Symfony2 EventDispatcher.
    • +
    • Use all of the code or only specific components.
    • +
    • Plugins for caching, logging, OAuth, mocks, and more
    • +
    • Includes a custom node.js webserver to test your clients.
    • +
    + +
    + Guzzle is now part of Drupal 8 core and powers the official AWS SDK for PHP +
    + +

    GitHub Example

    + +
    <?php
    +require_once 'vendor/autoload.php';
    +use Guzzle\Http\Client;
    +
    +// Create a client and provide a base URL
    +$client = new Client('https://api.github.com');
    +// Create a request with basic Auth
    +$request = $client->get('/user')->setAuth('user', 'pass');
    +// Send the request and get the response
    +$response = $request->send();
    +echo $response->getBody();
    +// >>> {"type":"User", ...
    +echo $response->getHeader('Content-Length');
    +// >>> 792
    +
    + +

    Twitter Example

    +
    <?php
    +// Create a client to work with the Twitter API
    +$client = new Client('https://api.twitter.com/{version}', array(
    +    'version' => '1.1'
    +));
    +
    +// Sign all requests with the OauthPlugin
    +$client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array(
    +    'consumer_key'  => '***',
    +    'consumer_secret' => '***',
    +    'token'       => '***',
    +    'token_secret'  => '***'
    +)));
    +
    +echo $client->get('statuses/user_timeline.json')->send()->getBody();
    +// >>> {"public_gists":6,"type":"User" ...
    +
    +// Create a tweet using POST
    +$request = $client->post('statuses/update.json', null, array(
    +    'status' => 'Tweeted with Guzzle, http://guzzlephp.org'
    +));
    +
    +// Send the request and parse the JSON response into an array
    +$data = $request->send()->json();
    +echo $data['text'];
    +// >>> Tweeted with Guzzle, http://t.co/kngJMfRk
    +
    +
    + + diff --git a/source/vendor/guzzle/guzzle/docs/_templates/leftbar.html b/source/vendor/guzzle/guzzle/docs/_templates/leftbar.html new file mode 100644 index 0000000..e69de29 diff --git a/source/vendor/guzzle/guzzle/docs/_templates/nav_links.html b/source/vendor/guzzle/guzzle/docs/_templates/nav_links.html new file mode 100644 index 0000000..d4f2165 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/_templates/nav_links.html @@ -0,0 +1,5 @@ +
  • Docs
  • +
  • API
  • +
  • GitHub
  • +
  • Forum
  • +
  • IRC
  • diff --git a/source/vendor/guzzle/guzzle/docs/batching/batching.rst b/source/vendor/guzzle/guzzle/docs/batching/batching.rst new file mode 100644 index 0000000..57f04d8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/batching/batching.rst @@ -0,0 +1,183 @@ +======== +Batching +======== + +Guzzle provides a fairly generic and very customizable batching framework that allows developers to efficiently +transfer requests in parallel. + +Sending requests and commands in parallel +----------------------------------------- + +You can send HTTP requests in parallel by passing an array of ``Guzzle\Http\Message\RequestInterface`` objects to +``Guzzle\Http\Client::send()``: + +.. code-block:: php + + $responses = $client->send(array( + $client->get('http://www.example.com/foo'), + $client->get('http://www.example.com/baz') + $client->get('http://www.example.com/bar') + )); + +You can send commands in parallel by passing an array of ``Guzzle\Service\Command\CommandInterface`` objects +``Guzzle\Service\Client::execute()``: + +.. code-block:: php + + $commands = $client->execute(array( + $client->getCommand('foo'), + $client->getCommand('baz'), + $client->getCommand('bar') + )); + +These approaches work well for most use-cases. When you need more control over the requests that are sent in +parallel or you need to send a large number of requests, you need to use the functionality provided in the +``Guzzle\Batch`` namespace. + +Batching overview +----------------- + +The batch object, ``Guzzle\Batch\Batch``, is a queue. You add requests to the queue until you are ready to transfer +all of the requests. In order to efficiently transfer the items in the queue, the batch object delegates the +responsibility of dividing the queue into manageable parts to a divisor (``Guzzle\Batch\BatchDivisorInterface``). +The batch object then iterates over each array of items created by the divisor and sends them to the batch object's +``Guzzle\Batch\BatchTransferInterface``. + +.. code-block:: php + + use Guzzle\Batch\Batch; + use Guzzle\Http\BatchRequestTransfer; + + // BatchRequestTransfer acts as both the divisor and transfer strategy + $transferStrategy = new BatchRequestTransfer(10); + $divisorStrategy = $transferStrategy; + + $batch = new Batch($transferStrategy, $divisorStrategy); + + // Add some requests to the batch queue + $batch->add($request1) + ->add($request2) + ->add($request3); + + // Flush the queue and retrieve the flushed items + $arrayOfTransferredRequests = $batch->flush(); + +.. note:: + + You might find that your transfer strategy will need to act as both the divisor and transfer strategy. + +Using the BatchBuilder +---------------------- + +The ``Guzzle\Batch\BatchBuilder`` makes it easier to create batch objects. The batch builder also provides an easier +way to add additional behaviors to your batch object. + +Transferring requests +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Http\BatchRequestTransfer`` class efficiently transfers HTTP requests in parallel by grouping batches of +requests by the curl_multi handle that is used to transfer the requests. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->build(); + +Transferring commands +~~~~~~~~~~~~~~~~~~~~~ + +The ``Guzzle\Service\Command\BatchCommandTransfer`` class efficiently transfers service commands by grouping commands +by the client that is used to transfer them. You can add commands to a batch object that are transferred by different +clients, and the batch will handle the rest. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferCommands(10) + ->build(); + + $batch->add($client->getCommand('foo')) + ->add($client->getCommand('baz')) + ->add($client->getCommand('bar')); + + $commands = $batch->flush(); + +Batch behaviors +--------------- + +You can add various behaviors to your batch that allow for more customizable transfers. + +Automatically flushing a queue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\FlushingBatch`` decorator when you want to pump a large number of items into a batch queue and +have the queue automatically flush when the size of the queue reaches a certain threshold. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->build(); + +Batch builder method: ``autoFlushAt($threshold)`` + +Notifying on flush +~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\NotifyingBatch`` decorator if you want a function to be notified each time the batch queue is +flushed. This is useful when paired with the flushing batch decorator. Pass a callable to the ``notify()`` method of +a batch builder to use this decorator with the builder. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->autoFlushAt(10) + ->notify(function (array $transferredItems) { + echo 'Transferred ' . count($transferredItems) . "items\n"; + }) + ->build(); + +Batch builder method:: ``notify(callable $callback)`` + +Keeping a history +~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\HistoryBatch`` decorator if you want to maintain a history of all the items transferred with +the batch queue. + +.. code-block:: php + + use Guzzle\Batch\BatchBuilder; + + $batch = BatchBuilder::factory() + ->transferRequests(10) + ->keepHistory() + ->build(); + +After transferring items, you can use the ``getHistory()`` of a batch to retrieve an array of transferred items. Be +sure to periodically clear the history using ``clearHistory()``. + +Batch builder method: ``keepHistory()`` + +Exception buffering +~~~~~~~~~~~~~~~~~~~ + +Use the ``Guzzle\Batch\ExceptionBufferingBatch`` decorator to buffer exceptions during a transfer so that you can +transfer as many items as possible then deal with the errored batches after the transfer completes. After transfer, +use the ``getExceptions()`` method of a batch to retrieve an array of +``Guzzle\Batch\Exception\BatchTransferException`` objects. You can use these exceptions to attempt to retry the +failed batches. Be sure to clear the buffered exceptions when you are done with them by using the +``clearExceptions()`` method. + +Batch builder method: ``bufferExceptions()`` diff --git a/source/vendor/guzzle/guzzle/docs/conf.py b/source/vendor/guzzle/guzzle/docs/conf.py new file mode 100644 index 0000000..92bc46b --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/conf.py @@ -0,0 +1,94 @@ +import sys, os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +# -- General configuration ----------------------------------------------------- + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' + +project = u'Guzzle' +copyright = u'2012, Michael Dowling' +version = '3.0.0' +release = '3.0.0' + +exclude_patterns = ['_build'] + +# -- Options for HTML output --------------------------------------------------- + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "Guzzle documentation" +html_short_title = "Guzzle" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + '**': ['localtoc.html', 'leftbar.html', 'searchbox.html'] +} + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Guzzledoc' + +# -- Guzzle Sphinx theme setup ------------------------------------------------ + +sys.path.insert(0, '/Users/dowling/projects/guzzle_sphinx_theme') + +import guzzle_sphinx_theme +html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' +html_theme_path = guzzle_sphinx_theme.html_theme_path() +html_theme = 'guzzle_sphinx_theme' + +# Guzzle theme options (see theme.conf for more information) +html_theme_options = { + "index_template": "index.html", + "project_nav_name": "Guzzle", + "github_user": "guzzle", + "github_repo": "guzzle", + "disqus_comments_shortname": "guzzle", + "google_analytics_account": "UA-22752917-1" +} + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = {} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Guzzle.tex', u'Guzzle Documentation', + u'Michael Dowling', 'manual'), +] + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'guzzle', u'Guzzle Documentation', + [u'Michael Dowling'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Guzzle', u'Guzzle Documentation', + u'Michael Dowling', 'Guzzle', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/source/vendor/guzzle/guzzle/docs/docs.rst b/source/vendor/guzzle/guzzle/docs/docs.rst new file mode 100644 index 0000000..cf87908 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/docs.rst @@ -0,0 +1,73 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services + +==================== +Guzzle Documentation +==================== + +Getting started +--------------- + +.. toctree:: + :maxdepth: 1 + + getting-started/overview + getting-started/installation + getting-started/faq + +The HTTP client +--------------- + +.. toctree:: + :maxdepth: 2 + + http-client/client + http-client/request + http-client/response + http-client/entity-bodies + http-client/http-redirects + http-client/uri-templates + +Plugins +------- + +.. toctree:: + :maxdepth: 1 + + plugins/plugins-overview + plugins/creating-plugins + plugins/async-plugin + plugins/backoff-plugin + plugins/cache-plugin + plugins/cookie-plugin + plugins/curl-auth-plugin + plugins/history-plugin + plugins/log-plugin + plugins/md5-validator-plugin + plugins/mock-plugin + plugins/oauth-plugin + +The web service client +---------------------- + +.. toctree:: + :maxdepth: 1 + + webservice-client/webservice-client + webservice-client/using-the-service-builder + webservice-client/guzzle-service-descriptions + batching/batching + iterators/resource-iterators + iterators/guzzle-iterators + +Testing +------- + +.. toctree:: + :maxdepth: 2 + + testing/unit-testing + +API Docs +-------- + +`Read the API docs `_ diff --git a/source/vendor/guzzle/guzzle/docs/getting-started/faq.rst b/source/vendor/guzzle/guzzle/docs/getting-started/faq.rst new file mode 100644 index 0000000..a0a3fdb --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/getting-started/faq.rst @@ -0,0 +1,29 @@ +=== +FAQ +=== + +What should I do if I get this error: Fatal error: Maximum function nesting level of '100' reached, aborting! +------------------------------------------------------------------------------------------------------------- + +You could run into this error if you have the XDebug extension installed and you execute a lot of requests in +callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function +nesting limit. Change this setting in your php.ini to increase the limit:: + + xdebug.max_nesting_level = 1000 + +[`source `_] + +How can I speed up my client? +----------------------------- + +There are several things you can do to speed up your client: + +1. Utilize a C based HTTP message parser (e.g. ``Guzzle\Parser\Message\PeclHttpMessageParser``) +2. Disable operation validation by setting the ``command.disable_validation`` option to true on a command + +Why am I getting a 417 error response? +-------------------------------------- + +This can occur for a number of reasons, but if you are sending PUT, POST, or PATCH requests with an +``Expect: 100-Continue`` header, a server that does not support this header will return a 417 response. You can work +around this by calling ``$request->removeHeader('Expect');`` after setting the entity body of a request. diff --git a/source/vendor/guzzle/guzzle/docs/getting-started/installation.rst b/source/vendor/guzzle/guzzle/docs/getting-started/installation.rst new file mode 100644 index 0000000..77d4001 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/getting-started/installation.rst @@ -0,0 +1,154 @@ +============ +Installation +============ + +Requirements +------------ + +#. PHP 5.3.3+ compiled with the cURL extension +#. A recent version of cURL 7.16.2+ compiled with OpenSSL and zlib + +Installing Guzzle +----------------- + +Composer +~~~~~~~~ + +The recommended way to install Guzzle is with `Composer `_. Composer is a dependency +management tool for PHP that allows you to declare the dependencies your project needs and installs them into your +project. + +.. code-block:: bash + + # Install Composer + curl -sS https://getcomposer.org/installer | php + + # Add Guzzle as a dependency + php composer.phar require guzzle/guzzle:~3.9 + +After installing, you need to require Composer's autoloader: + +.. code-block:: php + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and other best-practices for defining +dependencies at `getcomposer.org `_. + +Using only specific parts of Guzzle +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While you can always just rely on ``guzzle/guzzle``, Guzzle provides several smaller parts of Guzzle as individual +packages available through Composer. + ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| Package name | Description | ++===============================================================================================+==========================================+ +| `guzzle/common `_ | Provides ``Guzzle\Common`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/http `_ | Provides ``Guzzle\Http`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/parser `_ | Provides ``Guzzle\Parser`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/batch `_ | Provides ``Guzzle\Batch`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/cache `_ | Provides ``Guzzle\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/inflection `_ | Provides ``Guzzle\Inflection`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/iterator `_ | Provides ``Guzzle\Iterator`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/log `_ | Provides ``Guzzle\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin `_ | Provides ``Guzzle\Plugin`` (all plugins) | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-async `_ | Provides ``Guzzle\Plugin\Async`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-backoff `_ | Provides ``Guzzle\Plugin\BackoffPlugin`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cache `_ | Provides ``Guzzle\Plugin\Cache`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-cookie `_ | Provides ``Guzzle\Plugin\Cookie`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-error-response `_ | Provides ``Guzzle\Plugin\ErrorResponse`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-history `_ | Provides ``Guzzle\Plugin\History`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-log `_ | Provides ``Guzzle\Plugin\Log`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-md5 `_ | Provides ``Guzzle\Plugin\Md5`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-mock `_ | Provides ``Guzzle\Plugin\Mock`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/plugin-oauth `_ | Provides ``Guzzle\Plugin\Oauth`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/service `_ | Provides ``Guzzle\Service`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ +| `guzzle/stream `_ | Provides ``Guzzle\Stream`` | ++-----------------------------------------------------------------------------------------------+------------------------------------------+ + +Bleeding edge +^^^^^^^^^^^^^ + +During your development, you can keep up with the latest changes on the master branch by setting the version +requirement for Guzzle to ``dev-master``. + +.. code-block:: js + + { + "require": { + "guzzle/guzzle": "dev-master" + } + } + +PEAR +~~~~ + +Guzzle can be installed through PEAR: + +.. code-block:: bash + + pear channel-discover guzzlephp.org/pear + pear install guzzle/guzzle + +You can install a specific version of Guzzle by providing a version number suffix: + +.. code-block:: bash + + pear install guzzle/guzzle-3.9.0 + +Contributing to Guzzle +---------------------- + +In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using +Composer: + +.. code-block:: bash + + git clone https://github.com/guzzle/guzzle.git + cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev + +Guzzle is unit tested with PHPUnit. You will need to create your own phpunit.xml file in order to run the unit tests +(or just copy phpunit.xml.dist to phpunit.xml). Run the tests using the vendored PHPUnit binary: + +.. code-block:: bash + + vendor/bin/phpunit + +You'll need to install node.js v0.5.0 or newer in order to test the cURL implementation. + +Framework integrations +---------------------- + +Using Guzzle with Symfony +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Bundles are available on GitHub: + +- `DdeboerGuzzleBundle `_ for Guzzle 2 +- `MisdGuzzleBundle `_ for Guzzle 3 + +Using Guzzle with Silex +~~~~~~~~~~~~~~~~~~~~~~~ + +A `Guzzle Silex service provider `_ is available on GitHub. diff --git a/source/vendor/guzzle/guzzle/docs/getting-started/overview.rst b/source/vendor/guzzle/guzzle/docs/getting-started/overview.rst new file mode 100644 index 0000000..505b409 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/getting-started/overview.rst @@ -0,0 +1,85 @@ +================= +Welcome to Guzzle +================= + +What is Guzzle? +~~~~~~~~~~~~~~~ + +Guzzle is a PHP HTTP client and framework for building web service clients. Guzzle takes the pain out of sending HTTP +requests and the redundancy out of creating web service clients. + +Features at a glance +-------------------- + +- All the power of cURL with a simple interface. +- Persistent connections and parallel requests. +- Streams request and response bodies +- Service descriptions for quickly building clients. +- Powered by the Symfony2 EventDispatcher. +- Use all of the code or only specific components. +- Plugins for caching, logging, OAuth, mocks, and more +- Includes a custom node.js webserver to test your clients. +- Service descriptions for defining the inputs and outputs of an API +- Resource iterators for traversing paginated resources +- Batching for sending a large number of requests as efficiently as possible + +.. code-block:: php + + // Really simple using a static facade + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + + // More control using a client class + $client = new \Guzzle\Http\Client('http://guzzlephp.org'); + $request = $client->get('/'); + $response = $request->send(); + +License +------- + +Licensed using the `MIT license `_. + + Copyright (c) 2013 Michael Dowling + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Contributing +------------ + +Guidelines +~~~~~~~~~~ + +This is still a work in progress, but there are only a few rules: + +1. Guzzle follows PSR-0, PSR-1, and PSR-2 +2. All pull requests must include unit tests to ensure the change works as expected and to prevent future regressions + +Reporting a security vulnerability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security +vulnerability in Guzzle, we appreciate your help in disclosing it to us in a +`responsible manner `_. + +Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern, +please email us at security@guzzlephp.org. We'll work with you to make sure that we understand the scope of the issue, +and that we fully address your concern. We consider correspondence sent to security@guzzlephp.org our highest priority, +and work to address any issues that arise as quickly as possible. + +After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible. diff --git a/source/vendor/guzzle/guzzle/docs/http-client/client.rst b/source/vendor/guzzle/guzzle/docs/http-client/client.rst new file mode 100644 index 0000000..723d729 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/client.rst @@ -0,0 +1,569 @@ +====================== +The Guzzle HTTP client +====================== + +Guzzle gives PHP developers complete control over HTTP requests while utilizing HTTP/1.1 best practices. Guzzle's HTTP +functionality is a robust framework built on top of the `PHP libcurl bindings `_. + +The three main parts of the Guzzle HTTP client are: + ++--------------+-------------------------------------------------------------------------------------------------------+ +| Clients | ``Guzzle\Http\Client`` (creates and sends requests, associates a response with a request) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Requests | ``Guzzle\Http\Message\Request`` (requests with no body), | +| | ``Guzzle\Http\Message\EntityEnclosingRequest`` (requests with a body) | ++--------------+-------------------------------------------------------------------------------------------------------+ +| Responses | ``Guzzle\Http\Message\Response`` | ++--------------+-------------------------------------------------------------------------------------------------------+ + +Creating a Client +----------------- + +Clients create requests, send requests, and set responses on a request object. When instantiating a client object, +you can pass an optional "base URL" and optional array of configuration options. A base URL is a +:doc:`URI template ` that contains the URL of a remote server. When creating requests with a relative +URL, the base URL of a client will be merged into the request's URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + // Create a client and provide a base URL + $client = new Client('https://api.github.com'); + + $request = $client->get('/user'); + $request->setAuth('user', 'pass'); + echo $request->getUrl(); + // >>> https://api.github.com/user + + // You must send a request in order for the transfer to occur + $response = $request->send(); + + echo $response->getBody(); + // >>> {"type":"User", ... + + echo $response->getHeader('Content-Length'); + // >>> 792 + + $data = $response->json(); + echo $data['type']; + // >>> User + +Base URLs +~~~~~~~~~ + +Notice that the URL provided to the client's ``get()`` method is relative. Relative URLs will always merge into the +base URL of the client. There are a few rules that control how the URLs are merged. + +.. tip:: + + Guzzle follows `RFC 3986 `_ when merging base URLs and + relative URLs. + +In the above example, we passed ``/user`` to the ``get()`` method of the client. This is a relative URL, so it will +merge into the base URL of the client-- resulting in the derived URL of ``https://api.github.com/users``. + +``/user`` is a relative URL but uses an absolute path because it contains the leading slash. Absolute paths will +overwrite any existing path of the base URL. If an absolute path is provided (e.g. ``/path/to/something``), then the +path specified in the base URL of the client will be replaced with the absolute path, and the query string provided +by the relative URL will replace the query string of the base URL. + +Omitting the leading slash and using relative paths will add to the path of the base URL of the client. So using a +client base URL of ``https://api.twitter.com/v1.1`` and creating a GET request with ``statuses/user_timeline.json`` +will result in a URL of ``https://api.twitter.com/v1.1/statuses/user_timeline.json``. If a relative path and a query +string are provided, then the relative path will be appended to the base URL path, and the query string provided will +be merged into the query string of the base URL. + +If an absolute URL is provided (e.g. ``http://httpbin.org/ip``), then the request will completely use the absolute URL +as-is without merging in any of the URL parts specified in the base URL. + +Configuration options +~~~~~~~~~~~~~~~~~~~~~ + +The second argument of the client's constructor is an array of configuration data. This can include URI template data +or special options that alter the client's behavior: + ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``request.options`` | Associative array of :ref:`Request options ` to apply to every | +| | request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``redirect.disable`` | Disable HTTP redirects for every request created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``curl.options`` | Associative array of cURL options to apply to every request created by the client. | +| | if either the key or value of an entry in the array is a string, Guzzle will | +| | attempt to find a matching defined cURL constant automatically (e.g. | +| | "CURLOPT_PROXY" will be converted to the constant ``CURLOPT_PROXY``). | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``ssl.certificate_authority`` | Set to true to use the Guzzle bundled SSL certificate bundle (this is used by | +| | default, 'system' to use the bundle on your system, a string pointing to a file to | +| | use a specific certificate file, a string pointing to a directory to use multiple | +| | certificates, or ``false`` to disable SSL validation (not recommended). | +| | | +| | When using Guzzle inside of a phar file, the bundled SSL certificate will be | +| | extracted to your system's temp folder, and each time a client is created an MD5 | +| | check will be performed to ensure the integrity of the certificate. | ++-------------------------------+-------------------------------------------------------------------------------------+ +| ``command.params`` | When using a ``Guzzle\Service\Client`` object, this is an associative array of | +| | default options to set on each command created by the client. | ++-------------------------------+-------------------------------------------------------------------------------------+ + +Here's an example showing how to set various configuration options, including default headers to send with each request, +default query string parameters to add to each request, a default auth scheme for each request, and a proxy to use for +each request. Values can be injected into the client's base URL using variables from the configuration array. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('https://api.twitter.com/{version}', array( + 'version' => 'v1.1', + 'request.options' => array( + 'headers' => array('Foo' => 'Bar'), + 'query' => array('testing' => '123'), + 'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'), + 'proxy' => 'tcp://localhost:80' + ) + )); + +Setting a custom User-Agent +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default Guzzle User-Agent header is ``Guzzle/ curl/ PHP/``. You can +customize the User-Agent header of a client by calling the ``setUserAgent()`` method of a Client object. + +.. code-block:: php + + // Completely override the default User-Agent + $client->setUserAgent('Test/123'); + + // Prepend a string to the default User-Agent + $client->setUserAgent('Test/123', true); + +Creating requests with a client +------------------------------- + +A Client object exposes several methods used to create Request objects: + +* Create a custom HTTP request: ``$client->createRequest($method, $uri, array $headers, $body, $options)`` +* Create a GET request: ``$client->get($uri, array $headers, $options)`` +* Create a HEAD request: ``$client->head($uri, array $headers, $options)`` +* Create a DELETE request: ``$client->delete($uri, array $headers, $body, $options)`` +* Create a POST request: ``$client->post($uri, array $headers, $postBody, $options)`` +* Create a PUT request: ``$client->put($uri, array $headers, $body, $options)`` +* Create a PATCH request: ``$client->patch($uri, array $headers, $body, $options)`` + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client('http://baseurl.com/api/v1'); + + // Create a GET request using Relative to base URL + // URL of the request: http://baseurl.com/api/v1/path?query=123&value=abc) + $request = $client->get('path?query=123&value=abc'); + $response = $request->send(); + + // Create HEAD request using a relative URL with an absolute path + // URL of the request: http://baseurl.com/path?query=123&value=abc + $request = $client->head('/path?query=123&value=abc'); + $response = $request->send(); + + // Create a DELETE request using an absolute URL + $request = $client->delete('http://www.example.com/path?query=123&value=abc'); + $response = $request->send(); + + // Create a PUT request using the contents of a PHP stream as the body + // Specify custom HTTP headers + $request = $client->put('http://www.example.com/upload', array( + 'X-Header' => 'My Header' + ), fopen('http://www.test.com/', 'r')); + $response = $request->send(); + + // Create a POST request and add the POST files manually + $request = $client->post('http://localhost:8983/solr/update') + ->addPostFiles(array('file' => '/path/to/documents.xml')); + $response = $request->send(); + + // Check if a resource supports the DELETE method + $supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE'); + $response = $request->send(); + +Client objects create Request objects using a request factory (``Guzzle\Http\Message\RequestFactoryInterface``). +You can inject a custom request factory into the Client using ``$client->setRequestFactory()``, but you can typically +rely on a Client's default request factory. + +Static clients +-------------- + +You can use Guzzle's static client facade to more easily send simple HTTP requests. + +.. code-block:: php + + // Mount the client so that you can access it at \Guzzle + Guzzle\Http\StaticClient::mount(); + $response = Guzzle::get('http://guzzlephp.org'); + +Each request method of the static client (e.g. ``get()``, ``post()`, ``put()``, etc) accepts an associative array of request +options to apply to the request. + +.. code-block:: php + + $response = Guzzle::post('http://test.com', array( + 'headers' => array('X-Foo' => 'Bar'), + 'body' => array('Test' => '123'), + 'timeout' => 10 + )); + +.. _request-options: + +Request options +--------------- + +Request options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +headers +~~~~~~~ + +Associative array of headers to apply to the request. When specified in the ``$options`` argument of a client creational +method (e.g. ``get()``, ``post()``, etc), the headers in the ``$options`` array will overwrite headers specified in the +``$headers`` array. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'headers' => array('X-Foo' => 'Bar') + )); + +Headers can be specified on a client to add default headers to every request sent by a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single header using path syntax + $client->setDefaultOption('headers/X-Foo', 'Bar'); + + // Set all headers + $client->setDefaultOption('headers', array('X-Foo' => 'Bar')); + +.. note:: + + In addition to setting request options when creating requests or using the ``setDefaultOption()`` method, any + default client request option can be set using a client's config object: + + .. code-block:: php + + $client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar'); + +query +~~~~~ + +Associative array of query string parameters to the request. When specified in the ``$options`` argument of a client +creational method, the query string parameters in the ``$options`` array will overwrite query string parameters +specified in the `$url`. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'query' => array('abc' => '123') + )); + +Query string parameters can be specified on a client to add default query string parameters to every request sent by a +client. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + + // Set a single query string parameter using path syntax + $client->setDefaultOption('query/abc', '123'); + + // Set an array of default query string parameters + $client->setDefaultOption('query', array('abc' => '123')); + +body +~~~~ + +Sets the body of a request. The value supplied to the body option can be a ``Guzzle\Http\EntityBodyInterface``, string, +fopen resource, or array when sending POST requests. When a ``body`` request option is supplied, the option value will +overwrite the ``$body`` argument of a client creational method. + +auth +~~~~ + +Specifies and array of HTTP authorization parameters parameters to use with the request. The array must contain the +username in index [0], the password in index [1], and can optionally contain the authentication type in index [2]. +The available authentication types are: "Basic" (default), "Digest", "NTLM", or "Any". + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'auth' => array('username', 'password', 'Digest') + )); + + // You can add auth headers to every request of a client + $client->setDefaultOption('auth', array('username', 'password', 'Digest')); + +cookies +~~~~~~~ + +Specifies an associative array of cookies to add to the request. + +allow_redirects +~~~~~~~~~~~~~~~ + +Specifies whether or not the request should follow redirects. Requests will follow redirects by default. Set +``allow_redirects`` to ``false`` to disable redirects. + +save_to +~~~~~~~ + +The ``save_to`` option specifies where the body of a response is downloaded. You can pass the path to a file, an fopen +resource, or a ``Guzzle\Http\EntityBodyInterface`` object. + +See :ref:`Changing where a response is downloaded ` for more information on setting the +`save_to` option. + +events +~~~~~~ + +The `events` option makes it easy to attach listeners to the various events emitted by a request object. The `events` +options must be an associative array mapping an event name to a Closure or array the contains a Closure and the +priority of the event. + +.. code-block:: php + + $request = $client->get($url, array(), array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + + // Using the static client: + Guzzle::get($url, array( + 'events' => array( + 'request.before_send' => function (\Guzzle\Common\Event $e) { + echo 'About to send ' . $e['request']; + } + ) + )); + +plugins +~~~~~~~ + +The `plugins` options makes it easy to attach an array of plugins to a request. + +.. code-block:: php + + // Using the static client: + Guzzle::get($url, array( + 'plugins' => array( + new Guzzle\Plugin\Cache\CachePlugin(), + new Guzzle\Plugin\Cookie\CookiePlugin() + ) + )); + +exceptions +~~~~~~~~~~ + +The `exceptions` option can be used to disable throwing exceptions for unsuccessful HTTP response codes +(e.g. 404, 500, etc). Set `exceptions` to false to not throw exceptions. + +params +~~~~~~ + +The `params` options can be used to specify an associative array of data parameters to add to a request. Note that +these are not query string parameters. + +timeout / connect_timeout +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify the maximum number of seconds to allow for an entire transfer to take place before timing out using +the `timeout` request option. You can specify the maximum number of seconds to wait while trying to connect using the +`connect_timeout` request option. Set either of these options to 0 to wait indefinitely. + +.. code-block:: php + + $request = $client->get('http://www.example.com', array(), array( + 'timeout' => 20, + 'connect_timeout' => 1.5 + )); + +verify +~~~~~~ + +Set to true to enable SSL certificate validation (the default), false to disable SSL certificate validation, or supply +the path to a CA bundle to enable verification using a custom certificate. + +cert +~~~~ + +The `cert` option lets you specify a PEM formatted SSL client certificate to use with servers that require one. If the +certificate requires a password, provide an array with the password as the second item. + +This would typically be used in conjunction with the `ssl_key` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => '/etc/pki/client_certificate.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd') + ) + +ssl_key +~~~~~~~ + +The `ssl_key` option lets you specify a file containing your PEM formatted private key, optionally protected by a password. +Note: your password is sensitive, keep the PHP script containing it safe. + +This would typically be used in conjunction with the `cert` option. + +.. code-block:: php + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => '/etc/pki/private_key.pem' + ) + + $request = $client->get('https://www.example.com', array(), array( + 'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd') + ) + +proxy +~~~~~ + +The `proxy` option is used to specify an HTTP proxy (e.g. `http://username:password@192.168.16.1:10`). + +debug +~~~~~ + +The `debug` option is used to show verbose cURL output for a transfer. + +stream +~~~~~~ + +When using a static client, you can set the `stream` option to true to return a `Guzzle\Stream\Stream` object that can +be used to pull data from a stream as needed (rather than have cURL download the entire contents of a response to a +stream all at once). + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } + +Sending requests +---------------- + +Requests can be sent by calling the ``send()`` method of a Request object, but you can also send requests using the +``send()`` method of a Client. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $client->send($request); + +Sending requests in parallel +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Client's ``send()`` method accept a single ``Guzzle\Http\Message\RequestInterface`` object or an array of +RequestInterface objects. When an array is specified, the requests will be sent in parallel. + +Sending many HTTP requests serially (one at a time) can cause an unnecessary delay in a script's execution. Each +request must complete before a subsequent request can be sent. By sending requests in parallel, a pool of HTTP +requests can complete at the speed of the slowest request in the pool, significantly reducing the amount of time +needed to execute multiple HTTP requests. Guzzle provides a wrapper for the curl_multi functions in PHP. + +Here's an example of sending three requests in parallel using a client object: + +.. code-block:: php + + use Guzzle\Common\Exception\MultiTransferException; + + try { + $responses = $client->send(array( + $client->get('http://www.google.com/'), + $client->head('http://www.google.com/'), + $client->get('https://www.github.com/') + )); + } catch (MultiTransferException $e) { + + echo "The following exceptions were encountered:\n"; + foreach ($e as $exception) { + echo $exception->getMessage() . "\n"; + } + + echo "The following requests failed:\n"; + foreach ($e->getFailedRequests() as $request) { + echo $request . "\n\n"; + } + + echo "The following requests succeeded:\n"; + foreach ($e->getSuccessfulRequests() as $request) { + echo $request . "\n\n"; + } + } + +If the requests succeed, an array of ``Guzzle\Http\Message\Response`` objects are returned. A single request failure +will not cause the entire pool of requests to fail. Any exceptions thrown while transferring a pool of requests will +be aggregated into a ``Guzzle\Common\Exception\MultiTransferException`` exception. + +Plugins and events +------------------ + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the +`Symfony2 Event Dispatcher component `_. Any +event listener or subscriber attached to a Client object will automatically be attached to each request created by the +client. + +Using the same cookie session for each request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Attach a ``Guzzle\Plugin\Cookie\CookiePlugin`` to a client which will in turn add support for cookies to every request +created by a client, and each request will use the same cookie session: + +.. code-block:: php + + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + // Create a new cookie plugin + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to the client + $client->addSubscriber($cookiePlugin); + +.. _client-events: + +Events emitted from a client +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.create_request | Called when a client creates a request | * client: The client | +| | | * request: The created request | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\Client; + + $client = new Client(); + + // Add a listener that will echo out requests as they are created + $client->getEventDispatcher()->addListener('client.create_request', function (Event $e) { + echo 'Client object: ' . spl_object_hash($e['client']) . "\n"; + echo "Request object: {$e['request']}\n"; + }); diff --git a/source/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst b/source/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst new file mode 100644 index 0000000..823b0c0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/entity-bodies.rst @@ -0,0 +1,151 @@ +=========================== +Request and response bodies +=========================== + +`Entity body `_ is the term used for the body of an HTTP +message. The entity body of requests and responses is inherently a +`PHP stream `_ in Guzzle. The body of the request can be either a string or +a PHP stream which are converted into a ``Guzzle\Http\EntityBody`` object using its factory method. When using a +string, the entity body is stored in a `temp PHP stream `_. The use of +temp PHP streams helps to protect your application from running out of memory when sending or receiving large entity +bodies in your messages. When more than 2MB of data is stored in a temp stream, it automatically stores the data on +disk rather than in memory. + +EntityBody objects provide a great deal of functionality: compression, decompression, calculate the Content-MD5, +calculate the Content-Length (when the resource is repeatable), guessing the Content-Type, and more. Guzzle doesn't +need to load an entire entity body into a string when sending or retrieving data; entity bodies are streamed when +being uploaded and downloaded. + +Here's an example of gzip compressing a text file then sending the file to a URL: + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/path/to/file.txt', 'r+')); + echo $body->read(1024); + $body->seek(0, SEEK_END); + $body->write('foo'); + echo $body->ftell(); + $body->rewind(); + + // Send a request using the body + $response = $client->put('http://localhost:8080/uploads', null, $body)->send(); + +The body of the request can be specified in the ``Client::put()`` or ``Client::post()`` method, or, you can specify +the body of the request by calling the ``setBody()`` method of any +``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object. + +Compression +----------- + +You can compress the contents of an EntityBody object using the ``compress()`` method. The compress method accepts a +filter that must match to one of the supported +`PHP stream filters `_ on your system (e.g. `zlib.deflate`, +``bzip2.compress``, etc). Compressing an entity body will stream the entire entity body through a stream compression +filter into a temporary PHP stream. You can uncompress an entity body using the ``uncompress()`` method and passing +the PHP stream filter to use when decompressing the stream (e.g. ``zlib.inflate``). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + + $body = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $body->getSize(); + // >>> 1048576 + + // Compress using the default zlib.deflate filter + $body->compress(); + echo $body->getSize(); + // >>> 314572 + + // Decompress the stream + $body->uncompress(); + echo $body->getSize(); + // >>> 1048576 + +Decorators +---------- + +Guzzle provides several EntityBody decorators that can be used to add functionality to an EntityBody at runtime. + +IoEmittingEntityBody +~~~~~~~~~~~~~~~~~~~~ + +This decorator will emit events when data is read from a stream or written to a stream. Add an event subscriber to the +entity body's ``body.read`` or ``body.write`` methods to receive notifications when data data is transferred. + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Http\EntityBody; + use Guzzle\Http\IoEmittingEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + $body = new IoEmittingEntityBody($original); + + // Listen for read events + $body->getEventDispatcher()->addListener('body.read', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // Amount of data retrieved from the body + $lengthOfData = $e['length']; + // The actual data that was read + $data = $e['read']; + }); + + // Listen for write events + $body->getEventDispatcher()->addListener('body.write', function (Event $e) { + // Grab data from the event + $entityBody = $e['body']; + // The data that was written + $data = $e['write']; + // The actual amount of data that was written + $data = $e['read']; + }); + +ReadLimitEntityBody +~~~~~~~~~~~~~~~~~~~ + +The ReadLimitEntityBody decorator can be used to transfer a subset or slice of an existing EntityBody object. This can +be useful for breaking a large file into smaller pieces to be sent in chunks (e.g. Amazon S3's multipart upload API). + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\ReadLimitEntityBody; + + $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); + echo $original->getSize(); + // >>> 1048576 + + // Limit the size of the body to 1024 bytes and start reading from byte 2048 + $body = new ReadLimitEntityBody($original, 1024, 2048); + echo $body->getSize(); + // >>> 1024 + echo $body->ftell(); + // >>> 0 + +CachingEntityBody +~~~~~~~~~~~~~~~~~ + +The CachingEntityBody decorator is used to allow seeking over previously read bytes on non-seekable read streams. This +can be useful when transferring a non-seekable entity body fails due to needing to rewind the stream (for example, +resulting from a redirect). Data that is read from the remote stream will be buffered in a PHP temp stream so that +previously read bytes are cached first in memory, then on disk. + +.. code-block:: php + + use Guzzle\Http\EntityBody; + use Guzzle\Http\CachingEntityBody; + + $original = EntityBody::factory(fopen('http://www.google.com', 'r')); + $body = new CachingEntityBody($original); + + $body->read(1024); + echo $body->ftell(); + // >>> 1024 + + $body->seek(0); + echo $body->ftell(); + // >>> 0 diff --git a/source/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst b/source/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst new file mode 100644 index 0000000..32ba268 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/http-redirects.rst @@ -0,0 +1,99 @@ +============== +HTTP redirects +============== + +By default, Guzzle will automatically follow redirects using the non-RFC compliant implementation used by most web +browsers. This means that redirects for POST requests are followed by a GET request. You can force RFC compliance by +enabling the strict mode on a request's parameter object: + +.. code-block:: php + + // Set per request + $request = $client->post(); + $request->getParams()->set('redirect.strict', true); + + // You can set globally on a client so all requests use strict redirects + $client->getConfig()->set('request.params', array( + 'redirect.strict' => true + )); + +By default, Guzzle will redirect up to 5 times before throwing a ``Guzzle\Http\Exception\TooManyRedirectsException``. +You can raise or lower this value using the ``redirect.max`` parameter of a request object: + +.. code-block:: php + + $request->getParams()->set('redirect.max', 2); + +Redirect history +---------------- + +You can get the number of redirects of a request using the resulting response object's ``getRedirectCount()`` method. +Similar to cURL's ``effective_url`` property, Guzzle provides the effective URL, or the last redirect URL that returned +the request, in a response's ``getEffectiveUrl()`` method. + +When testing or debugging, it is often useful to see a history of redirects for a particular request. This can be +achieved using the HistoryPlugin. + +.. code-block:: php + + $request = $client->get('/'); + $history = new Guzzle\Plugin\History\HistoryPlugin(); + $request->addSubscriber($history); + $response = $request->send(); + + // Get the last redirect URL or the URL of the request that received + // this response + echo $response->getEffectiveUrl(); + + // Get the number of redirects + echo $response->getRedirectCount(); + + // Iterate over each sent request and response + foreach ($history->getAll() as $transaction) { + // Request object + echo $transaction['request']->getUrl() . "\n"; + // Response object + echo $transaction['response']->getEffectiveUrl() . "\n"; + } + + // Or, simply cast the HistoryPlugin to a string to view each request and response + echo $history; + +Disabling redirects +------------------- + +You can disable redirects on a client by passing a configuration option in the client's constructor: + +.. code-block:: php + + $client = new Client(null, array('redirect.disable' => true)); + +You can also disable redirects per request: + +.. code-block:: php + + $request = $client->get($url, array(), array('allow_redirects' => false)); + +Redirects and non-repeatable streams +------------------------------------ + +If you are redirected when sending data from a non-repeatable stream and some of the data has been read off of the +stream, then you will get a ``Guzzle\Http\Exception\CouldNotRewindStreamException``. You can get around this error by +adding a custom rewind method to the entity body object being sent in the request. + +.. code-block:: php + + $request = $client->post( + 'http://httpbin.com/redirect/2', + null, + fopen('http://httpbin.com/get', 'r') + ); + + // Add a custom function that can be used to rewind the stream + // (reopen in this example) + $request->getBody()->setRewindFunction(function ($body) { + $body->setStream(fopen('http://httpbin.com/get', 'r')); + return true; + ); + + $response = $client->send(); diff --git a/source/vendor/guzzle/guzzle/docs/http-client/request.rst b/source/vendor/guzzle/guzzle/docs/http-client/request.rst new file mode 100644 index 0000000..a8387a9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/request.rst @@ -0,0 +1,667 @@ +===================== +Using Request objects +===================== + +HTTP request messages +--------------------- + +Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually +using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request +with each part of the request referencing the method used to change it:: + + PUT(a) /path(b)?query=123(c) HTTP/1.1(d) + X-Header(e): header + Content-Length(e): 4 + + data(f) + ++-------------------------+---------------------------------------------------------------------------------+ +| a. **Method** | The request method can only be set when instantiating a request | ++-------------------------+---------------------------------------------------------------------------------+ +| b. **Path** | ``$request->setPath('/path');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| c. **Query** | ``$request->getQuery()->set('query', '123');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| e. **Header** | ``$request->setHeader('X-Header', 'header');`` | ++-------------------------+---------------------------------------------------------------------------------+ +| f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` | ++-------------------------+---------------------------------------------------------------------------------+ + +Creating requests with a client +------------------------------- + +Client objects are responsible for creating HTTP request objects. + +GET requests +~~~~~~~~~~~~ + +`GET requests `_ are the most common form of HTTP +requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET +requests are idempotent requests that are typically used to download content (an entity) identified by a request URL. + +.. code-block:: php + + use Guzzle\Http\Client; + + $client = new Client(); + + // Create a request that has a query string and an X-Foo header + $request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar')); + + // Send the request and get the response + $response = $request->send(); + +You can change where the body of a response is downloaded on any request using the +``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to`` +option of a request: + +.. code-block:: php + + // Send the response body to a file + $request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file')); + + // Send the response body to an fopen resource + $request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w'))); + +HEAD requests +~~~~~~~~~~~~~ + +`HEAD requests `_ work exactly like GET requests except +that they do not actually download the response body (entity) of the response message. HEAD requests are useful for +retrieving meta information about an entity identified by a Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->head('http://www.amazon.com'); + $response = $request->send(); + echo $response->getContentLength(); + // >>> Will output the Content-Length header value + +DELETE requests +~~~~~~~~~~~~~~~ + +A `DELETE method `_ requests that the origin server +delete the resource identified by the Request-URI. + +.. code-block:: php + + $client = new Guzzle\Http\Client(); + $request = $client->delete('http://example.com'); + $response = $request->send(); + +POST requests +~~~~~~~~~~~~~ + +While `POST requests `_ can be used for a number of +reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity +body in the HTTP request. + +POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are +present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type +header will become ``multipart/form-data``. + +The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of +request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if +you were using the PHP ``curl_setopt`` function). + +Here's how to create a multipart/form-data POST request containing files and fields: + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), array( + 'custom_field' => 'my custom value', + 'file_field' => '@/path/to/file.xml' + )); + + $response = $request->send(); + +.. note:: + + Remember to **always** sanitize user input when sending POST requests: + + .. code-block:: php + + // Prevent users from accessing sensitive files by sanitizing input + $_POST = array('firstname' => '@/etc/passwd'); + $request = $client->post('http://www.example.com', array(), array ( + 'firstname' => str_replace('@', '', $_POST['firstname']) + )); + +You can alternatively build up the contents of a POST request. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post') + ->setPostField('custom_field', 'my custom value') + ->addPostFile('file', '/path/to/file.xml'); + + $response = $request->send(); + +Raw POST data +^^^^^^^^^^^^^ + +POST requests can also contain raw POST data that is not related to HTML forms. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post', array(), 'this is the body'); + $response = $request->send(); + +You can set the body of POST request using the ``setBody()`` method of the +``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from +``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object. + +.. code-block:: php + + $request = $client->post('http://httpbin.org/post'); + // Set the body of the POST to stream the contents of /path/to/large_body.txt + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PUT requests +~~~~~~~~~~~~ + +The `PUT method `_ requests that the enclosed entity be +stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity +body in the request message. + +The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as +a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a +remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a +callback function, or simply send a string of data. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put', array(), 'this is the body'); + $response = $request->send(); + +Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method. + +.. code-block:: php + + $request = $client->put('http://httpbin.org/put'); + $request->setBody(fopen('/path/to/large_body.txt', 'r')); + $response = $request->send(); + +PATCH requests +~~~~~~~~~~~~~~ + +`PATCH requests `_ are used to modify a resource. + +.. code-block:: php + + $request = $client->patch('http://httpbin.org', array(), 'this is the body'); + $response = $request->send(); + +OPTIONS requests +~~~~~~~~~~~~~~~~ + +The `OPTIONS method `_ represents a request for +information about the communication options available on the request/response chain identified by the Request-URI. + +.. code-block:: php + + $request = $client->options('http://httpbin.org'); + $response = $request->send(); + + // Check if the PUT method is supported by this resource + var_export($response->isMethodAllows('PUT')); + +Custom requests +~~~~~~~~~~~~~~~ + +You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a +client object. + +.. code-block:: php + + $request = $client->createRequest('COPY', 'http://example.com/foo', array( + 'Destination' => 'http://example.com/bar', + 'Overwrite' => 'T' + )); + $response = $request->send(); + +Query string parameters +----------------------- + +Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by +calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one +or more query string parameters as key value pairs. You can set a parameter on a Query object using the +``set($key, $value)`` method or access the query string object like an associative array. Any previously specified +value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string +object, and in the event of a collision with an existing value at a specific key, the value will be converted to an +array that contains all of the previously set values. + +.. code-block:: php + + $request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123'); + + $query = $request->getQuery(); + echo "{$query}\n"; + // >>> foo=bar&abc=123 + + $query->remove('abc'); + echo "{$query}\n"; + // >>> foo=bar + + $query->set('foo', 'baz'); + echo "{$query}\n"; + // >>> foo=baz + + $query->add('foo', 'bar'); + echo "{$query}\n"; + // >>> foo%5B0%5D=baz&foo%5B1%5D=bar + +Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the +aggregator associated with the Query object was used to help convert multi-value query string parameters into a string. +Let's disable URL-encoding to better see what's happening. + +.. code-block:: php + + $query->useUrlEncoding(false); + echo "{$query}\n"; + // >>> foo[0]=baz&foo[1]=bar + +.. note:: + + URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing + ``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing + ``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function). + +As you can see, the multiple values were converted into query string parameters following the default PHP convention of +adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert +multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle +ships with the following query string aggregators by default: + +1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``) +2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be + repeated in a URL (e.g. ``foo=baz&foo=bar``) +3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``) + +.. _http-message-headers: + +HTTP Message Headers +-------------------- + +HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message +(whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request +and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string, +counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of +the header values concatenated together using a glue string (typically ", "). + +A request (and response) object have several methods that allow you to retrieve and modify headers. + +* ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object. +* ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a + ``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``. +* ``hasHeader($header)``: Returns true or false based on if the message has a particular header. +* ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header. +* ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same, + then the header will contain multiple values. +* ``removeHeader($header)``: Remove a header by name from the message. + +.. code-block:: php + + $request = new Request('GET', 'http://httpbin.com/cookies'); + // addHeader will set and append to any existing header values + $request->addHeader('Foo', 'bar'); + $request->addHeader('foo', 'baz'); + // setHeader overwrites any existing values + $request->setHeader('Test', '123'); + + // Request headers can be cast as a string + echo $request->getHeader('Foo'); + // >>> bar, baz + echo $request->getHeader('Test'); + // >>> 123 + + // You can count the number of headers of a particular case insensitive name + echo count($request->getHeader('foO')); + // >>> 2 + + // You can iterate over Header objects + foreach ($request->getHeader('foo') as $header) { + echo $header . "\n"; + } + + // You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object + $headers = $request->getHeaders(); + + // Missing headers return NULL + var_export($request->getHeader('Missing')); + // >>> null + + // You can see all of the different variations of a header by calling raw() on the Header + var_export($request->getHeader('foo')->raw()); + +Setting the body of a request +----------------------------- + +Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of +``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow +you to specify the body to send with a request. + +Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a +string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body +will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body +of the request, you can optionally specify a Content-Type header and whether or not to force the request to use +chunked Transfer-Encoding. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody('{"foo":"baz"}', 'application/json'); + +Content-Type header +~~~~~~~~~~~~~~~~~~~ + +Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file +extension of the payload being sent or the file extension present in the path of a request. + +.. code-block:: php + + $request = $client->put('/user.json', array(), '{"foo":"bar"}'); + // The Content-Type was guessed based on the path of the request + echo $request->getHeader('Content-Type'); + // >>> application/json + + $request = $client->put('/user.json'); + $request->setBody(fopen('/tmp/user_data.json', 'r')); + // The Content-Type was guessed based on the path of the entity body + echo $request->getHeader('Content-Type'); + // >>> application/json + +Transfer-Encoding: chunked header +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire +message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the +size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known +before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked`` +header. + +If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add +the ``Transfer-Encoding: chunked`` header to the request. + +.. code-block:: php + + $request = $client->put('/user.json'); + $request->setBody(fopen('http://httpbin.org/get', 'r')); + + // The Content-Length could not be determined + echo $request->getHeader('Transfer-Encoding'); + // >>> chunked + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +Expect: 100-Continue header +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will +reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle +will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if +the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected). + +.. note:: + + If you find that your larger requests are taking too long to complete, you should first check if the + ``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header, + which causes cURL to sleep for `1 second `_. + +POST fields and files +~~~~~~~~~~~~~~~~~~~~~ + +Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests. +Any request that has set POST fields or files will use cURL's POST message functionality. + +.. code-block:: php + + $request = $client->post('/post'); + // Set an overwrite any previously specified value + $request->setPostField('foo', 'bar'); + // Append a value to any existing values + $request->getPostFields()->add('foo', 'baz'); + // Remove a POST field by name + $request->removePostField('fizz'); + + // Add a file to upload (forces multipart/form-data) + $request->addPostFile('my_file', '/path/to/file', 'plain/text'); + // Remove a POST file by POST key name + $request->removePostFile('my_other_file'); + +.. tip:: + + Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that + you can add and process multiple fields with a single call. Adding multiple POST files is also faster using + ``addPostFiles()``. + +Working with cookies +-------------------- + +Cookies can be modified and retrieved from a request using the following methods: + +.. code-block:: php + + $request->addCookie($name, $value); + $request->removeCookie($name); + $value = $request->getCookie($name); + $valueArray = $request->getCookies(); + +Use the :doc:`cookie plugin ` if you need to reuse cookies between requests. + +.. _request-set-response-body: + +Changing where a response is downloaded +---------------------------------------- + +When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the +location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request +option. This can be useful for downloading the contents of a URL to a specific file. + +Here's an example of using request options: + +.. code-block:: php + + $request = $this->client->get('http://example.com/large.mov', array(), array( + 'save_to' => '/tmp/large_file.mov' + )); + $request->send(); + var_export(file_exists('/tmp/large_file.mov')); + // >>> true + +Here's an example of using ``setResponseBody()``: + +.. code-block:: php + + $body = fopen('/tmp/large_file.mov', 'w'); + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody($body); + + // You can more easily specify the name of a file to save the contents + // of the response to by passing a string to ``setResponseBody()``. + + $request = $this->client->get('http://example.com/large.mov'); + $request->setResponseBody('/tmp/large_file.mov'); + +Custom cURL options +------------------- + +Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers +who need access to `cURL specific functionality `_ can still add cURL handle +specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request: + +.. code-block:: php + + $request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200); + +Other special options that can be set in the ``curl.options`` array include: + ++-------------------------+---------------------------------------------------------------------------------+ +| debug | Adds verbose cURL output to a temp stream owned by the cURL handle object | ++-------------------------+---------------------------------------------------------------------------------+ +| progress | Instructs cURL to emit events when IO events occur. This allows you to be | +| | notified when bytes are transferred over the wire by subscribing to a request's | +| | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` | +| | events. | ++-------------------------+---------------------------------------------------------------------------------+ + +Request options +--------------- + +Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These +options can control various aspects of a request including: headers to send, query string data, where the response +should be downloaded, proxies, auth, etc. + +.. code-block:: php + + $request = $client->get($url, $headers, array('proxy' => 'http://proxy.com')); + +See :ref:`Request options ` for more information. + +Working with errors +------------------- + +HTTP errors +~~~~~~~~~~~ + +Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More +specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a +``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the +BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException: + +.. code-block:: php + + try { + $response = $client->get('/not_found.xml')->send(); + } catch (Guzzle\Http\Exception\BadResponseException $e) { + echo 'Uh oh! ' . $e->getMessage(); + echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n"; + echo 'HTTP request: ' . $e->getRequest() . "\n"; + echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n"; + echo 'HTTP response: ' . $e->getResponse() . "\n"; + } + +Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This +behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation. +You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received. + +You can change the response that will be associated with the request by calling ``setResponse()`` on the +``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the +``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a +request by modifying the event allows you to retry failed requests without complicating the code that uses the client. +This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that +your token has expired, you can get a new token, retry the request with the new token, and return the successful +response to the user. + +Here's an example of retrying a request using updated authorization credentials when a 401 response is received, +overriding the response of the original request with the new response, and still allowing the default exception +behavior to be called when other non-200 response status codes are encountered: + +.. code-block:: php + + // Add custom error handling to any request created by this client + $client->getEventDispatcher()->addListener('request.error', function(Event $event) { + + if ($event['response']->getStatusCode() == 401) { + + $newRequest = $event['request']->clone(); + $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken()); + $newResponse = $newRequest->send(); + + // Set the response object of the request without firing more events + $event['response'] = $newResponse; + + // You can also change the response and fire the normal chain of + // events by calling $event['request']->setResponse($newResponse); + + // Stop other events from firing when you override 401 responses + $event->stopPropagation(); + } + + }); + +cURL errors +~~~~~~~~~~~ + +Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle +encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is +thrown with an informative error message and access to the cURL error message. + +A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while +transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the +transfer. + +Plugins and events +------------------ + +Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request +directly by just calling ``$request->addSubscriber($mySubscriber);``. + +.. _request-events: + +Events emitted from a request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| request.before_send | About to send request | * request: Request to be sent | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.sent | Sent the request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.complete | Completed a full HTTP transaction | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.success | Completed a successful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.error | Completed an unsuccessful request | * request: Request that was sent | +| | | * response: Received response | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.exception | An unsuccessful response was | * request: Request | +| | received. | * response: Received response | +| | | * exception: BadResponseException | ++------------------------------+--------------------------------------------+------------------------------------------+ +| request.receive.status_line | Received the start of a response | * line: Full response start line | +| | | * status_code: Status code | +| | | * reason_phrase: Reason phrase | +| | | * previous_response: (e.g. redirect) | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle | +| | ``emit_io`` is set on a request's curl | * download_size: Total download size | +| | options) | * downloaded: Bytes downloaded | +| | | * upload_size: Total upload bytes | +| | | * uploaded: Bytes uploaded | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.write | cURL event called when data is written to | * request: Request | +| | an outgoing stream | * write: Data being written | ++------------------------------+--------------------------------------------+------------------------------------------+ +| curl.callback.read | cURL event called when data is written to | * request: Request | +| | an incoming stream | * read: Data being read | ++------------------------------+--------------------------------------------+------------------------------------------+ + +Creating a request event listener +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here's an example that listens to the ``request.complete`` event of a request and prints the request and response. + +.. code-block:: php + + use Guzzle\Common\Event; + + $request = $client->get('http://www.google.com'); + + // Echo out the response that was received + $request->getEventDispatcher()->addListener('request.complete', function (Event $e) { + echo $e['request'] . "\n\n"; + echo $e['response']; + }); diff --git a/source/vendor/guzzle/guzzle/docs/http-client/response.rst b/source/vendor/guzzle/guzzle/docs/http-client/response.rst new file mode 100644 index 0000000..ba48731 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/response.rst @@ -0,0 +1,141 @@ +====================== +Using Response objects +====================== + +Sending a request will return a ``Guzzle\Http\Message\Response`` object. You can view the raw HTTP response message by +casting the Response object to a string. Casting the response to a string will return the entity body of the response +as a string too, so this might be an expensive operation if the entity body is stored in a file or network stream. If +you only want to see the response headers, you can call ``getRawHeaders()``. + +Response status line +-------------------- + +The different parts of a response's `status line `_ +(the first line of the response HTTP message) are easily retrievable. + +.. code-block:: php + + $response = $client->get('http://www.amazon.com')->send(); + + echo $response->getStatusCode(); // >>> 200 + echo $response->getReasonPhrase(); // >>> OK + echo $response->getProtocol(); // >>> HTTP + echo $response->getProtocolVersion(); // >>> 1.1 + +You can determine the type of the response using several helper methods: + +.. code-block:: php + + $response->isSuccessful(); // true + $response->isInformational(); + $response->isRedirect(); + $response->isClientError(); + $response->isServerError(); + +Response headers +---------------- + +The Response object contains helper methods for retrieving common response headers. These helper methods normalize the +variations of HTTP response headers. + +.. code-block:: php + + $response->getCacheControl(); + $response->getContentType(); + $response->getContentLength(); + $response->getContentEncoding(); + $response->getContentMd5(); + $response->getEtag(); + // etc... There are methods for every known response header + +You can interact with the Response headers using the same exact methods used to interact with Request headers. See +:ref:`http-message-headers` for more information. + +.. code-block:: php + + echo $response->getHeader('Content-Type'); + echo $response->getHeader('Content-Length'); + echo $response->getHeaders()['Content-Type']; // PHP 5.4 + +Response body +------------- + +The entity body object of a response can be retrieved by calling ``$response->getBody()``. The response EntityBody can +be cast to a string, or you can pass ``true`` to this method to retrieve the body as a string. + +.. code-block:: php + + $request = $client->get('http://www.amazon.com'); + $response = $request->send(); + echo $response->getBody(); + +See :doc:`/http-client/entity-bodies` for more information on entity bodies. + +JSON Responses +~~~~~~~~~~~~~~ + +You can easily parse and use a JSON response as an array using the ``json()`` method of a response. This method will +always return an array if the response is valid JSON or if the response body is empty. You will get an exception if you +call this method and the response is not valid JSON. + +.. code-block:: php + + $data = $response->json(); + echo gettype($data); + // >>> array + +XML Responses +~~~~~~~~~~~~~ + +You can easily parse and use a XML response as SimpleXMLElement object using the ``xml()`` method of a response. This +method will always return a SimpleXMLElement object if the response is valid XML or if the response body is empty. You +will get an exception if you call this method and the response is not valid XML. + +.. code-block:: php + + $xml = $response->xml(); + echo $xml->foo; + // >>> Bar! + +Streaming responses +------------------- + +Some web services provide streaming APIs that allow a client to keep a HTTP request open for an extended period of +time while polling and reading. Guzzle provides a simple way to convert HTTP request messages into +``Guzzle\Stream\Stream`` objects so that you can send the initial headers of a request, read the response headers, and +pull in the response body manually as needed. + +Here's an example using the Twitter Streaming API to track the keyword "bieber": + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Stream\PhpStreamRequestFactory; + + $client = new Client('https://stream.twitter.com/1'); + + $request = $client->post('statuses/filter.json', null, array( + 'track' => 'bieber' + )); + + $request->setAuth('myusername', 'mypassword'); + + $factory = new PhpStreamRequestFactory(); + $stream = $factory->fromRequest($request); + + // Read until the stream is closed + while (!$stream->feof()) { + // Read a line from the stream + $line = $stream->readLine(); + // JSON decode the line of data + $data = json_decode($line, true); + } + +You can use the ``stream`` request option when using a static client to more easily create a streaming response. + +.. code-block:: php + + $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); + while (!$stream->feof()) { + echo $stream->readLine(); + } diff --git a/source/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst b/source/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst new file mode 100644 index 0000000..c18ac3e --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/http-client/uri-templates.rst @@ -0,0 +1,52 @@ +============= +URI templates +============= + +The ``$uri`` passed to one of the client's request creational methods or the base URL of a client can utilize URI +templates. Guzzle supports the entire `URI templates RFC `_. URI templates add a +special syntax to URIs that replace template place holders with user defined variables. + +Every request created by a Guzzle HTTP client passes through a URI template so that URI template expressions are +automatically expanded: + +.. code-block:: php + + $client = new Guzzle\Http\Client('https://example.com/', array('a' => 'hi')); + $request = $client->get('/{a}'); + +Because of URI template expansion, the URL of the above request will become ``https://example.com/hi``. Notice that +the template was expanded using configuration variables of the client. You can pass in custom URI template variables +by passing the URI of your request as an array where the first index of the array is the URI template and the second +index of the array are template variables that are merged into the client's configuration variables. + +.. code-block:: php + + $request = $client->get(array('/test{?a,b}', array('b' => 'there'))); + +The URL for this request will become ``https://test.com?a=hi&b=there``. URI templates aren't limited to just simple +variable replacements; URI templates can provide an enormous amount of flexibility when creating request URIs. + +.. code-block:: php + + $request = $client->get(array('http://example.com{+path}{/segments*}{?query,data*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => 'value' + ) + ))); + +The resulting URL would become ``http://example.com/foo/bar/one/two?query=test&more=value``. + +By default, URI template expressions are enclosed in an opening and closing brace (e.g. ``{var}``). If you are working +with a web service that actually uses braces (e.g. Solr), then you can specify a custom regular expression to use to +match URI template expressions. + +.. code-block:: php + + $client->getUriTemplate()->setRegex('/\<\$(.+)\>/'); + $client->get('/<$a>'); + +You can learn about all of the different features of URI templates by reading the +`URI templates RFC `_. diff --git a/source/vendor/guzzle/guzzle/docs/index.rst b/source/vendor/guzzle/guzzle/docs/index.rst new file mode 100644 index 0000000..f76f3bb --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/index.rst @@ -0,0 +1,5 @@ +.. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services +.. toctree:: + :hidden: + + docs.rst diff --git a/source/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst b/source/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst new file mode 100644 index 0000000..a5c7fd3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/iterators/guzzle-iterators.rst @@ -0,0 +1,97 @@ +================ +Guzzle iterators +================ + +Guzzle provides several SPL iterators that can be used with other SPL iterators, including Guzzle resource iterators. +Guzzle's ``guzzle/iterator`` component can also be used independently of the rest of Guzzle through Packagist and +Composer: https://packagist.org/packages/guzzle/iterator + +ChunkedIterator +--------------- + +Pulls out multiple values from an inner iterator and yields and array of values for each outer iteration -- essentially +pulling out chunks of values from the inner iterator. + +.. code-block:: php + + use Guzzle\Iterator\ChunkedIterator; + + $inner = new ArrayIterator(range(0, 8)); + $chunkedIterator = new ChunkedIterator($inner, 2); + + foreach ($chunkedIterator as $chunk) { + echo implode(', ', $chunk) . "\n"; + } + + // >>> 0, 1 + // >>> 2, 3 + // >>> 4, 5 + // >>> 6, 7 + // >>> 8 + +FilterIterator +-------------- + +This iterator is used to filter values out of the inner iterator. This iterator can be used when PHP 5.4's +CallbackFilterIterator is not available. + +.. code-block:: php + + use Guzzle\Iterator\FilterIterator; + + $inner = new ArrayIterator(range(1, 10)); + $filterIterator = new FilterIterator($inner, function ($value) { + return $value % 2; + }); + + foreach ($filterIterator as $value) { + echo $value . "\n"; + } + + // >>> 2 + // >>> 4 + // >>> 6 + // >>> 8 + // >>> 10 + +MapIterator +----------- + +This iterator modifies the values of the inner iterator before yielding. + +.. code-block:: php + + use Guzzle\Iterator\MapIterator; + + $inner = new ArrayIterator(range(0, 3)); + + $mapIterator = new MapIterator($inner, function ($value) { + return $value * 10; + }); + + foreach ($mapIterator as $value) { + echo $value . "\n"; + } + + // >>> 0 + // >>> 10 + // >>> 20 + // >>> 30 + +MethodProxyIterator +------------------- + +This decorator is useful when you need to expose a specific method from an inner iterator that might be wrapper +by one or more iterator decorators. This decorator proxies missing method calls to each inner iterator until one +of the inner iterators can fulfill the call. + +.. code-block:: php + + use Guzzle\Iterator\MethodProxyIterator; + + $inner = new \ArrayIterator(); + $proxy = new MethodProxyIterator($inner); + + // Proxy method calls to the ArrayIterator + $proxy->append('a'); + $proxy->append('b'); diff --git a/source/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst b/source/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst new file mode 100644 index 0000000..ce0bee5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/iterators/resource-iterators.rst @@ -0,0 +1,149 @@ +================== +Resource iterators +================== + +Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive +requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible +for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator +foundation to make it easier on web service client developers to offer a useful abstraction layer. + +Getting an iterator from a client +--------------------------------- + + ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ]) + +The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for +instantiating a resource iterator for a specific command. This method implicitly uses a +``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an +instantiated command object or the name of a command in the first argument. When passing the name of a command, the +command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument +may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object. + +.. code-block:: php + + $iterator = $client->getIterator('get_users'); + + foreach ($iterator as $user) { + echo $user['name'] . ' age ' . $user['age'] . PHP_EOL; + } + +The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't +care. I just want to iterate over all of the users. + +Iterator options +~~~~~~~~~~~~~~~~ + +The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option +tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in +mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size`` +option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit`` +option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option. + +.. note:: + + The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and + ``setPageSize($pageSize)`` methods. + +Resolving iterator class names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default resource iterator factory of a client object expects that your iterators are stored under the ``Model`` +folder of your client and that an iterator is names after the CamelCase name of a command followed by the word +"Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class +would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``. + +Creating an iterator +-------------------- + +While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface`` +object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend, +accepts a command object and array of iterator options in its constructor. The command object passed to the resource +iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of +this command and clones the original command each time a subsequent request needs to be made to fetch more data. + +Implement the sendRequest method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement +the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin +iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a +subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the +original command object passed into the constructor of the iterator. Use this command object to issue your subsequent +requests. + +The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call. +Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return +an empty result set but still requires further iteration, then you must implement a sort of loop in your +``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated +result set or until additional resources are retrieved from the web service. + +Update the nextToken property +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken`` +property of the iterator. Setting this property to anything other than null tells the iterator that issuing a +subsequent request using the nextToken value will probably return more results. You must continually update this +value in your ``sendRequest()`` method as each response is received from the web service. + +Example iterator +---------------- + +Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The +``get_users`` command receives a response that contains a list of users, and if there are more pages of results to +retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to +issue subsequent requests. + +Assume the response to a ``get_users`` command returns JSON data that looks like this: + +.. code-block:: javascript + + { + "users": [ + { "name": "Craig Johnson", "age": 10 }, + { "name": "Tom Barker", "age": 20 }, + { "name": "Bob Mitchell", "age": 74 } + ], + "next_user": "Michael Dowling" + } + +Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the +``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource +iterator for this command. + +.. code-block:: php + + namespace MyService\Model; + + use Guzzle\Service\Resource\ResourceIterator; + + /** + * Iterate over a get_users command + */ + class GetUsersIterator extends ResourceIterator + { + protected function sendRequest() + { + // If a next token is set, then add it to the command + if ($this->nextToken) { + $this->command->set('next_user', $this->nextToken); + } + + // Execute the command and parse the result + $result = $this->command->execute(); + + // Parse the next token + $this->nextToken = isset($result['next_user']) ? $result['next_user'] : false; + + return $result['users']; + } + } + +As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this +example: + +1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the + original command passed into the constructor of the iterator before the ``sendRequest()`` method is called. + Remember that the resource iterator expects a command that has not been executed. +2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check + before setting it on a command. Notice that the next token is being updated each time a request is sent. +3. After fetching more resources from the service, always return an array of resources. diff --git a/source/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst new file mode 100644 index 0000000..9bd8f42 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/async-plugin.rst @@ -0,0 +1,18 @@ +============ +Async plugin +============ + +The AsyncPlugin allows you to send requests that do not wait on a response. This is handled through cURL by utilizing +the progress event. When a request has sent all of its data to the remote server, Guzzle adds a 1ms timeout on the +request and instructs cURL to not download the body of the response. The async plugin then catches the exception and +adds a mock response to the request, along with an X-Guzzle-Async header to let you know that the response was not +fully downloaded. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Async\AsyncPlugin; + + $client = new Client('http://www.example.com'); + $client->addSubscriber(new AsyncPlugin()); + $response = $client->get()->send(); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst new file mode 100644 index 0000000..5a76941 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/backoff-plugin.rst @@ -0,0 +1,22 @@ +==================== +Backoff retry plugin +==================== + +The ``Guzzle\Plugin\Backoff\BackoffPlugin`` automatically retries failed HTTP requests using custom backoff strategies: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client = new Client('http://www.test.com/'); + // Use a static factory method to get a backoff plugin using the exponential backoff strategy + $backoffPlugin = BackoffPlugin::getExponentialBackoff(); + + // Add the backoff plugin to the client object + $client->addSubscriber($backoffPlugin); + +The BackoffPlugin's constructor accepts a ``Guzzle\Plugin\Backoff\BackoffStrategyInterface`` object that is used to +determine when a retry should be issued and how long to delay between retries. The above code example shows how to +attach a BackoffPlugin to a client that is pre-configured to retry failed 500 and 503 responses using truncated +exponential backoff (emulating the behavior of Guzzle 2's ExponentialBackoffPlugin). diff --git a/source/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst new file mode 100644 index 0000000..d2fd5df --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/cache-plugin.rst @@ -0,0 +1,169 @@ +================= +HTTP Cache plugin +================= + +Guzzle can leverage HTTP's caching specifications using the ``Guzzle\Plugin\Cache\CachePlugin``. The CachePlugin +provides a private transparent proxy cache that caches HTTP responses. The caching logic, based on +`RFC 2616 `_, uses HTTP headers to control caching behavior, +cache lifetime, and supports Vary, ETag, and Last-Modified based revalidation: + +.. code-block:: php + + use Guzzle\Http\Client; + use Doctrine\Common\Cache\FilesystemCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + use Guzzle\Plugin\Cache\DefaultCacheStorage; + + $client = new Client('http://www.test.com/'); + + $cachePlugin = new CachePlugin(array( + 'storage' => new DefaultCacheStorage( + new DoctrineCacheAdapter( + new FilesystemCache('/path/to/cache/files') + ) + ) + )); + + // Add the cache plugin to the client object + $client->addSubscriber($cachePlugin); + $client->get('http://www.wikipedia.org/')->send(); + + // The next request will revalidate against the origin server to see if it + // has been modified. If a 304 response is received the response will be + // served from cache + $client->get('http://www.wikipedia.org/')->send(); + +The cache plugin intercepts GET and HEAD requests before they are actually transferred to the origin server. The cache +plugin then generates a hash key based on the request method and URL, and checks to see if a response exists in the cache. If +a response exists in the cache, the cache adapter then checks to make sure that the caching rules associated with the response +satisfy the request, and ensures that response still fresh. If the response is acceptable for the request any required +revalidation, then the cached response is served instead of contacting the origin server. + +Vary +---- + +Cache keys are derived from a request method and a request URL. Multiple responses can map to the same cache key and +stored in Guzzle's underlying cache storage object. You should use the ``Vary`` HTTP header to tell the cache storage +object that the cache response must have been cached for a request that matches the headers specified in the Vary header +of the request. This allows you to have specific cache entries for the same request URL but variations in a request's +headers determine which cache entry is served. Please see the http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44 +for more information. + +Cache options +------------- + +There are several options you can add to requests or clients to modify the behavior of the cache plugin. + +Override cache TTL +~~~~~~~~~~~~~~~~~~ + +You can override the number of seconds a cacheable response is stored in the cache by setting the +``cache.override_ttl`` parameter on the params object of a request: + +.. code-block:: php + + // If the response to the request is cacheable, then the response will be cached for 100 seconds + $request->getParams()->set('cache.override_ttl', 100); + +If a response doesn't specify any freshness policy, it will be kept in cache for 3600 seconds by default. + +Custom caching decision +~~~~~~~~~~~~~~~~~~~~~~~ + +If the service you are interacting with does not return caching headers or returns responses that are normally +something that would not be cached, you can set a custom ``can_cache`` object on the constructor of the CachePlugin +and provide a ``Guzzle\Plugin\Cache\CanCacheInterface`` object. You can use the +``Guzzle\Plugin\Cache\CallbackCanCacheStrategy`` to easily make a caching decision based on an HTTP request and +response. + +Revalidation options +~~~~~~~~~~~~~~~~~~~~ + +You can change the revalidation behavior of a request using the ``cache.revalidate`` parameter. Setting this +parameter to ``never`` will ensure that a revalidation request is never sent, and the response is always served from +the origin server. Setting this parameter to ``skip`` will never revalidate and uses the response stored in the cache. + +Normalizing requests for caching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the ``cache.key_filter`` parameter if you wish to strip certain query string parameters from your +request before creating a unique hash for the request. This parameter can be useful if your requests have query +string values that cause each request URL to be unique (thus preventing a cache hit). The ``cache.key_filter`` +format is simply a comma separated list of query string values to remove from the URL when creating a cache key. +For example, here we are saying that the ``a`` and ``q`` query string variables should be ignored when generating a +cache key for the request: + +.. code-block:: php + + $request->getParams()->set('cache.key_filter', 'a, q'); + +Other options +~~~~~~~~~~~~~ + +There are many other options available to the CachePlugin that can meet almost any caching requirement, including +custom revalidation implementations, custom cache key generators, custom caching decision strategies, and custom +cache storage objects. Take a look the constructor of ``Guzzle\Plugin\Cache\CachePlugin`` for more information. + +Setting Client-wide cache settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can specify cache settings for every request created by a client by adding cache settings to the configuration +options of a client. + +.. code-block:: php + + $client = new Guzzle\Http\Client('http://www.test.com', array( + 'request.params' => array( + 'cache.override_ttl' => 3600, + 'params.cache.revalidate' => 'never' + ) + )); + + echo $client->get('/')->getParams()->get('cache.override_ttl'); + // >>> 3600 + + echo $client->get('/')->getParams()->get('cache.revalidate'); + // >>> never + +Cache revalidation +------------------ + +If the cache plugin determines that a response to a GET request needs revalidation, a conditional GET is transferred +to the origin server. If the origin server returns a 304 response, then a response containing the merged headers of +the cached response with the new response and the entity body of the cached response is returned. Custom revalidation +strategies can be injected into a CachePlugin if needed. + +Cache adapters +-------------- + +Guzzle doesn't try to reinvent the wheel when it comes to caching or logging. Plenty of other frameworks have +excellent solutions in place that you are probably already using in your applications. Guzzle uses adapters for +caching and logging. The cache plugin requires a cache adapter so that is can store responses in a cache. Guzzle +currently supports cache adapters for `Doctrine 2.0 `_ and the +`Zend Framework `_. + +Doctrine cache adapter +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Doctrine\Common\Cache\ArrayCache; + use Guzzle\Cache\DoctrineCacheAdapter; + use Guzzle\Plugin\Cache\CachePlugin; + + $backend = new ArrayCache(); + $adapter = new DoctrineCacheAdapter($backend); + $cache = new CachePlugin($adapter); + +Zend Framework cache adapter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Guzzle\Cache\ZendCacheAdapter; + use Zend\Cache\Backend\TestBackend; + + $backend = new TestBackend(); + $adapter = new ZendCacheAdapter($backend); + $cache = new CachePlugin($adapter); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst new file mode 100644 index 0000000..a6cc7d9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/cookie-plugin.rst @@ -0,0 +1,33 @@ +============= +Cookie plugin +============= + +Some web services require a Cookie in order to maintain a session. The ``Guzzle\Plugin\Cookie\CookiePlugin`` will add +cookies to requests and parse cookies from responses using a CookieJar object: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Cookie\CookiePlugin; + use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; + + $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); + + // Add the cookie plugin to a client + $client = new Client('http://www.test.com/'); + $client->addSubscriber($cookiePlugin); + + // Send the request with no cookies and parse the returned cookies + $client->get('http://www.yahoo.com/')->send(); + + // Send the request again, noticing that cookies are being sent + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + + echo $request; + +You can disable cookies per-request by setting the ``cookies.disable`` value to true on a request's params object. + +.. code-block:: php + + $request->getParams()->set('cookies.disable', true); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst b/source/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst new file mode 100644 index 0000000..0870155 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/creating-plugins.rst @@ -0,0 +1,93 @@ +================ +Creating plugins +================ + +.. highlight:: php + +Guzzle is extremely extensible because of the behavioral modifications that can be added to requests, clients, and +commands using an event system. Before and after the majority of actions are taken in the library, an event is emitted +with the name of the event and context surrounding the event. Observers can subscribe to a subject and modify the +subject based on the events received. Guzzle's event system utilizes the Symfony2 EventDispatcher and is the backbone +of its plugin architecture. + +Overview +-------- + +Plugins must implement the ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` interface. The +``EventSubscriberInterface`` requires that your class implements a static method, ``getSubscribedEvents()``, that +returns an associative array mapping events to methods on the object. See the +`Symfony2 documentation `_ for more information. + +Plugins can be attached to any subject, or object in Guzzle that implements that +``Guzzle\Common\HasDispatcherInterface``. + +Subscribing to a subject +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can subscribe an instantiated observer to an event by calling ``addSubscriber`` on a subject. + +.. code-block:: php + + $testPlugin = new TestPlugin(); + $client->addSubscriber($testPlugin); + +You can also subscribe to only specific events using a closure:: + + $client->getEventDispatcher()->addListener('request.create', function(Event $event) { + echo $event->getName(); + echo $event['request']; + }); + +``Guzzle\Common\Event`` objects are passed to notified functions. The Event object has a ``getName()`` method which +return the name of the emitted event and may contain contextual information that can be accessed like an array. + +Knowing what events to listen to +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Any class that implements the ``Guzzle\Common\HasDispatcherInterface`` must implement a static method, +``getAllEvents()``, that returns an array of the events that are emitted from the object. You can browse the source +to see each event, or you can call the static method directly in your code to get a list of available events. + +Event hooks +----------- + +* :ref:`client-events` +* :ref:`service-client-events` +* :ref:`request-events` +* ``Guzzle\Http\Curl\CurlMulti``: +* :ref:`service-builder-events` + +Examples of the event system +---------------------------- + +Simple Echo plugin +~~~~~~~~~~~~~~~~~~ + +This simple plugin prints a string containing the request that is about to be sent by listening to the +``request.before_send`` event:: + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EchoPlugin implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array('request.before_send' => 'onBeforeSend'); + } + + public function onBeforeSend(Guzzle\Common\Event $event) + { + echo 'About to send a request: ' . $event['request'] . "\n"; + } + } + + $client = new Guzzle\Service\Client('http://www.test.com/'); + + // Create the plugin and add it as an event subscriber + $plugin = new EchoPlugin(); + $client->addSubscriber($plugin); + + // Send a request and notice that the request is printed to the screen + $client->get('/')->send(); + +Running the above code will print a string containing the HTTP request that is about to be sent. diff --git a/source/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst new file mode 100644 index 0000000..66d4a01 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/curl-auth-plugin.rst @@ -0,0 +1,32 @@ +========================== +cURL authentication plugin +========================== + +.. warning:: + + The CurlAuthPlugin is deprecated. You should use the `auth` parameter of a client to add authorization headers to + every request created by a client. + + .. code-block:: php + + $client->setDefaultOption('auth', array('username', 'password', 'Basic|Digest|NTLM|Any')); + +If your web service client requires basic authorization, then you can use the CurlAuthPlugin to easily add an +Authorization header to each request sent by the client. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\CurlAuth\CurlAuthPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the auth plugin to the client object + $authPlugin = new CurlAuthPlugin('username', 'password'); + $client->addSubscriber($authPlugin); + + $response = $client->get('projects/1/people')->send(); + $xml = new SimpleXMLElement($response->getBody(true)); + foreach ($xml->person as $person) { + echo $person->email . "\n"; + } diff --git a/source/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst new file mode 100644 index 0000000..b96befe --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/history-plugin.rst @@ -0,0 +1,24 @@ +============== +History plugin +============== + +The history plugin tracks all of the requests and responses sent through a request or client. This plugin can be +useful for crawling or unit testing. By default, the history plugin stores up to 10 requests and responses. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\History\HistoryPlugin; + + $client = new Client('http://www.test.com/'); + + // Add the history plugin to the client object + $history = new HistoryPlugin(); + $history->setLimit(5); + $client->addSubscriber($history); + + $client->get('http://www.yahoo.com/')->send(); + + echo $history->getLastRequest(); + echo $history->getLastResponse(); + echo count($history); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst new file mode 100644 index 0000000..3e2b229 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/log-plugin.rst @@ -0,0 +1,69 @@ +========== +Log plugin +========== + +Use the ``Guzzle\Plugin\Log\LogPlugin`` to view all data sent over the wire, including entity bodies and redirects. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Log\Zf1LogAdapter; + use Guzzle\Plugin\Log\LogPlugin; + use Guzzle\Log\MessageFormatter; + + $client = new Client('http://www.test.com/'); + + $adapter = new Zf1LogAdapter( + new \Zend_Log(new \Zend_Log_Writer_Stream('php://output')) + ); + $logPlugin = new LogPlugin($adapter, MessageFormatter::DEBUG_FORMAT); + + // Attach the plugin to the client, which will in turn be attached to all + // requests generated by the client + $client->addSubscriber($logPlugin); + + $response = $client->get('http://google.com')->send(); + +The code sample above wraps a ``Zend_Log`` object using a ``Guzzle\Log\Zf1LogAdapter``. After attaching the plugin to +the client, all data sent over the wire will be logged to stdout. + +The first argument of the LogPlugin's constructor accepts a ``Guzzle\Log\LogAdapterInterface`` object. This object is +an adapter that allows you to use the logging capabilities of your favorite log implementation. The second argument of +the constructor accepts a ``Guzzle\Log\MessageFormatter`` or a log messaged format string. The format string uses +variable substitution and allows you to define the log data that is important to your application. The different +variables that can be injected are as follows: + +================== ==================================================================================== +Variable Substitution +================== ==================================================================================== +{request} Full HTTP request message +{response} Full HTTP response message +{ts} Timestamp +{host} Host of the request +{method} Method of the request +{url} URL of the request +{host} Host of the request +{protocol} Request protocol +{version} Protocol version +{resource} Resource of the request (path + query + fragment) +{port} Port of the request +{hostname} Hostname of the machine that sent the request +{code} Status code of the response (if available) +{phrase} Reason phrase of the response (if available) +{curl_error} Curl error message (if available) +{curl_code} Curl error code (if available) +{curl_stderr} Curl standard error (if available) +{connect_time} Time in seconds it took to establish the connection (if available) +{total_time} Total transaction time in seconds for last transfer (if available) +{req_header_*} Replace `*` with the lowercased name of a request header to add to the message +{res_header_*} Replace `*` with the lowercased name of a response header to add to the message +{req_body} Request body +{res_body} Response body +================== ==================================================================================== + +The LogPlugin has a helper method that can be used when debugging that will output the full HTTP request and +response of a transaction: + +.. code-block:: php + + $client->addSubscriber(LogPlugin::getDebugPlugin()); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst new file mode 100644 index 0000000..1b1cfa8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/md5-validator-plugin.rst @@ -0,0 +1,29 @@ +==================== +MD5 validator plugin +==================== + +Entity bodies can sometimes be modified over the wire due to a faulty TCP transport or misbehaving proxy. If an HTTP +response contains a Content-MD5 header, then a MD5 hash of the entity body of a response can be compared against the +Content-MD5 header of the response to determine if the response was delivered intact. The +``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` will throw an ``UnexpectedValueException`` if the calculated MD5 hash does +not match the Content-MD5 header value: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Md5\Md5ValidatorPlugin; + + $client = new Client('http://www.test.com/'); + + $md5Plugin = new Md5ValidatorPlugin(); + + // Add the md5 plugin to the client object + $client->addSubscriber($md5Plugin); + + $request = $client->get('http://www.yahoo.com/'); + $request->send(); + +Calculating the MD5 hash of a large entity body or an entity body that was transferred using a Content-Encoding is an +expensive operation. When working in high performance applications, you might consider skipping the MD5 hash +validation for entity bodies bigger than a certain size or Content-Encoded entity bodies +(see ``Guzzle\Plugin\Md5\Md5ValidatorPlugin`` for more information). diff --git a/source/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst new file mode 100644 index 0000000..4900cb5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/mock-plugin.rst @@ -0,0 +1,27 @@ +=========== +Mock plugin +=========== + +The mock plugin is useful for testing Guzzle clients. The mock plugin allows you to queue an array of responses that +will satisfy requests sent from a client by consuming the request queue in FIFO order. + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Mock\MockPlugin; + use Guzzle\Http\Message\Response; + + $client = new Client('http://www.test.com/'); + + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)) + ->addResponse(new Response(404)); + + // Add the mock plugin to the client object + $client->addSubscriber($mock); + + // The following request will receive a 200 response from the plugin + $client->get('http://www.example.com/')->send(); + + // The following request will receive a 404 response from the plugin + $client->get('http://www.test.com/')->send(); diff --git a/source/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst b/source/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst new file mode 100644 index 0000000..e67eaba --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/oauth-plugin.rst @@ -0,0 +1,30 @@ +============ +OAuth plugin +============ + +Guzzle ships with an OAuth 1.0 plugin that can sign requests using a consumer key, consumer secret, OAuth token, +and OAuth secret. Here's an example showing how to send an authenticated request to the Twitter REST API: + +.. code-block:: php + + use Guzzle\Http\Client; + use Guzzle\Plugin\Oauth\OauthPlugin; + + $client = new Client('http://api.twitter.com/1'); + $oauth = new OauthPlugin(array( + 'consumer_key' => 'my_key', + 'consumer_secret' => 'my_secret', + 'token' => 'my_token', + 'token_secret' => 'my_token_secret' + )); + $client->addSubscriber($oauth); + + $response = $client->get('statuses/public_timeline.json')->send(); + +If you need to use a custom signing method, you can pass a ``signature_method`` configuration option in the +constructor of the OAuth plugin. The ``signature_method`` option must be a callable variable that accepts a string to +sign and signing key and returns a signed string. + +.. note:: + + You can omit the ``token`` and ``token_secret`` options to use two-legged OAuth. diff --git a/source/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc b/source/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc new file mode 100644 index 0000000..8d6d09b --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/plugins-list.rst.inc @@ -0,0 +1,9 @@ +* :doc:`/plugins/async-plugin` +* :doc:`/plugins/backoff-plugin` +* :doc:`/plugins/cache-plugin` +* :doc:`/plugins/cookie-plugin` +* :doc:`/plugins/history-plugin` +* :doc:`/plugins/log-plugin` +* :doc:`/plugins/md5-validator-plugin` +* :doc:`/plugins/mock-plugin` +* :doc:`/plugins/oauth-plugin` diff --git a/source/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst b/source/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst new file mode 100644 index 0000000..19ae57e --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/plugins/plugins-overview.rst @@ -0,0 +1,59 @@ +====================== +Plugin system overview +====================== + +The workflow of sending a request and parsing a response is driven by Guzzle's event system, which is powered by the +`Symfony2 Event Dispatcher component `_. + +Any object in Guzzle that emits events will implement the ``Guzzle\Common\HasEventDispatcher`` interface. You can add +event subscribers directly to these objects using the ``addSubscriber()`` method, or you can grab the +``Symfony\Component\EventDispatcher\EventDispatcher`` object owned by the object using ``getEventDispatcher()`` and +add a listener or event subscriber. + +Adding event subscribers to clients +----------------------------------- + +Any event subscriber or event listener attached to the EventDispatcher of a ``Guzzle\Http\Client`` or +``Guzzle\Service\Client`` object will automatically be attached to all request objects created by the client. This +allows you to attach, for example, a HistoryPlugin to a client object, and from that point on, every request sent +through that client will utilize the HistoryPlugin. + +.. code-block:: php + + use Guzzle\Plugin\History\HistoryPlugin; + use Guzzle\Service\Client; + + $client = new Client(); + + // Create a history plugin and attach it to the client + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + // Create and send a request. This request will also utilize the HistoryPlugin + $client->get('http://httpbin.org')->send(); + + // Echo out the last sent request by the client + echo $history->getLastRequest(); + +.. tip:: + + :doc:`Create event subscribers `, or *plugins*, to implement reusable logic that can be + shared across clients. Event subscribers are also easier to test than anonymous functions. + +Pre-Built plugins +----------------- + +Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications +powered by the Symfony2 Event Dispatcher component. + +* :doc:`async-plugin` +* :doc:`backoff-plugin` +* :doc:`cache-plugin` +* :doc:`cookie-plugin` +* :doc:`curl-auth-plugin` +* :doc:`history-plugin` +* :doc:`log-plugin` +* :doc:`md5-validator-plugin` +* :doc:`mock-plugin` +* :doc:`oauth-plugin` + diff --git a/source/vendor/guzzle/guzzle/docs/requirements.txt b/source/vendor/guzzle/guzzle/docs/requirements.txt new file mode 100644 index 0000000..f62e318 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/requirements.txt @@ -0,0 +1,2 @@ +Sphinx>=1.2b1 +guzzle_sphinx_theme>=0.5.0 diff --git a/source/vendor/guzzle/guzzle/docs/testing/unit-testing.rst b/source/vendor/guzzle/guzzle/docs/testing/unit-testing.rst new file mode 100644 index 0000000..f4297af --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/testing/unit-testing.rst @@ -0,0 +1,201 @@ +=========================== +Unit Testing Guzzle clients +=========================== + +Guzzle provides several tools that will enable you to easily unit test your web service clients. + +* PHPUnit integration +* Mock responses +* node.js web server for integration testing + +PHPUnit integration +------------------- + +Guzzle is unit tested using `PHPUnit `_. Your web service client's unit tests should extend +``Guzzle\Tests\GuzzleTestCase`` so that you can take advantage of some of the built in helpers. + +In order to unit test your client, a developer would need to copy phpunit.xml.dist to phpunit.xml and make any needed +modifications. As a best practice and security measure for you and your contributors, it is recommended to add an +ignore statement to your SCM so that phpunit.xml is ignored. + +Bootstrapping +~~~~~~~~~~~~~ + +Your web service client should have a tests/ folder that contains a bootstrap.php file. The bootstrap.php file +responsible for autoloading and configuring a ``Guzzle\Service\Builder\ServiceBuilder`` that is used throughout your +unit tests for loading a configured client. You can add custom parameters to your phpunit.xml file that expects users +to provide the path to their configuration data. + +.. code-block:: php + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Aws\Common\Aws::factory($_SERVER['CONFIG'])); + + Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory(array( + 'test.unfuddle' => array( + 'class' => 'Guzzle.Unfuddle.UnfuddleClient', + 'params' => array( + 'username' => 'test_user', + 'password' => '****', + 'subdomain' => 'test' + ) + ) + ))); + +The above code registers a service builder that can be used throughout your unit tests. You would then be able to +retrieve an instantiated and configured Unfuddle client by calling ``$this->getServiceBuilder()->get('test.unfuddle)``. +The above code assumes that ``$_SERVER['CONFIG']`` contains the path to a file that stores service description +configuration. + +Unit testing remote APIs +------------------------ + +Mock responses +~~~~~~~~~~~~~~ + +One of the benefits of unit testing is the ability to quickly determine if there are errors in your code. If your +unit tests run slowly, then they become tedious and will likely be run less frequently. Guzzle's philosophy on unit +testing web service clients is that no network access should be required to run the unit tests. This means that +responses are served from mock responses or local servers. By adhering to this principle, tests will run much faster +and will not require an external resource to be available. The problem with this approach is that your mock responses +must first be gathered and then subsequently updated each time the remote API changes. + +Integration testing over the internet +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can perform integration testing with a web service over the internet by making calls directly to the service. If +the web service you are requesting uses a complex signing algorithm or some other specific implementation, then you +may want to include at least one actual network test that can be run specifically through the command line using +`PHPUnit group annotations `_. + +@group internet annotation +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When creating tests that require an internet connection, it is recommended that you add ``@group internet`` annotations +to your unit tests to specify which tests require network connectivity. + +You can then `run PHPUnit tests `_ that exclude the @internet +group by running ``phpunit --exclude-group internet``. + +API credentials +^^^^^^^^^^^^^^^ + +If API credentials are required to run your integration tests, you must add ```` parameters to your +phpunit.xml.dist file and extract these parameters in your bootstrap.php file. + +.. code-block:: xml + + + + + + + + + + + + + ./Tests + + + + +You can then extract the ``server`` variables in your bootstrap.php file by grabbing them from the ``$_SERVER`` +superglobal: ``$apiUser = $_SERVER['API_USER'];`` + +Further reading +^^^^^^^^^^^^^^^ + +A good discussion on the topic of testing remote APIs can be found in Sebastian Bergmann's +`Real-World Solutions for Developing High-Quality PHP Frameworks and Applications `_. + +Queueing Mock responses +----------------------- + +Mock responses can be used to test if requests are being generated correctly and responses and handled correctly by +your client. Mock responses can be queued up for a client using the ``$this->setMockResponse($client, $path)`` method +of your test class. Pass the client you are adding mock responses to and a single path or array of paths to mock +response files relative to the ``/tests/mock/ folder``. This will queue one or more mock responses for your client by +creating a simple observer on the client. Mock response files must contain a full HTTP response message: + +.. code-block:: none + + HTTP/1.1 200 OK + Date: Wed, 25 Nov 2009 12:00:00 GMT + Connection: close + Server: AmazonS3 + Content-Type: application/xml + + + EU + +After queuing mock responses for a client, you can get an array of the requests that were sent by the client that +were issued a mock response by calling ``$this->getMockedRequests()``. + +You can also use the ``Guzzle\Plugin\Mock\MockPlugin`` object directly with your clients. + +.. code-block:: php + + $plugin = new Guzzle\Plugin\Mock\MockPlugin(); + $plugin->addResponse(new Guzzle\Http\Message\Response(200)); + $client = new Guzzle\Http\Client(); + $client->addSubscriber($plugin); + + // The following request will get the mock response from the plugin in FIFO order + $request = $client->get('http://www.test.com/'); + $request->send(); + + // The MockPlugin maintains a list of requests that were mocked + $this->assertContainsOnly($request, $plugin->getReceivedRequests()); + +node.js web server for integration testing +------------------------------------------ + +Using mock responses is usually enough when testing a web service client. If your client needs to add custom cURL +options to requests, then you should use the node.js test web server to ensure that your HTTP request message is +being created correctly. + +Guzzle is based around PHP's libcurl bindings. cURL sometimes modifies an HTTP request message based on +``CURLOPT_*`` options. Headers that are added to your request by cURL will not be accounted for if you inject mock +responses into your tests. Additionally, some request entity bodies cannot be loaded by the client before transmitting +it to the sever (for example, when using a client as a sort of proxy and streaming content from a remote server). You +might also need to inspect the entity body of a ``multipart/form-data`` POST request. + +.. note:: + + You can skip all of the tests that require the node.js test web server by excluding the ``server`` group: + ``phpunit --exclude-group server`` + +Using the test server +~~~~~~~~~~~~~~~~~~~~~ + +The node.js test server receives requests and returns queued responses. The test server exposes a simple API that is +used to enqueue responses and inspect the requests that it has received. + +Retrieve the server object by calling ``$this->getServer()``. If the node.js server is not running, it will be +started as a forked process and an object that interfaces with the server will be returned. (note: stopping the +server is handled internally by Guzzle.) + +You can queue an HTTP response or an array of responses by calling ``$this->getServer()->enqueue()``: + +.. code-block:: php + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + +The above code queues a single 200 response with an empty body. Responses are queued using a FIFO order; this +response will be returned by the server when it receives the first request and then removed from the queue. If a +request is received by a server with no queued responses, an exception will be thrown in your unit test. + +You can inspect the requests that the server has retrieved by calling ``$this->getServer()->getReceivedRequests()``. +This method accepts an optional ``$hydrate`` parameter that specifies if you are retrieving an array of string HTTP +requests or an array of ``Guzzle\Http\RequestInterface`` subclassed objects. "Hydrating" the requests will allow +greater flexibility in your unit tests so that you can easily assert the state of the various parts of a request. + +You will need to modify the base_url of your web service client in order to use it against the test server. + +.. code-block:: php + + $client = $this->getServiceBuilder()->get('my_client'); + $client->setBaseUrl($this->getServer()->getUrl()); + +After running the above code, all calls made from the ``$client`` object will be sent to the test web server. diff --git a/source/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst b/source/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst new file mode 100644 index 0000000..ad6070b --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/webservice-client/guzzle-service-descriptions.rst @@ -0,0 +1,619 @@ +=========================== +Guzzle service descriptions +=========================== + +Guzzle allows you to serialize HTTP requests and parse HTTP responses using a DSL called a service descriptions. +Service descriptions define web service APIs by documenting each operation, the operation's parameters, validation +options for each parameter, an operation's response, how the response is parsed, and any errors that can be raised for +an operation. Writing a service description for a web service allows you to more quickly consume a web service than +writing concrete commands for each web service operation. + +Guzzle service descriptions can be representing using a PHP array or JSON document. Guzzle's service descriptions are +heavily inspired by `Swagger `_. + +Service description schema +========================== + +A Guzzle Service description must match the following JSON schema document. This document can also serve as a guide when +implementing a Guzzle service description. + +Download the schema here: :download:`Guzzle JSON schema document ` + +.. class:: overflow-height-500px + + .. literalinclude:: ../_downloads/guzzle-schema-1.0.json + :language: json + +Top-level attributes +-------------------- + +Service descriptions are comprised of the following top-level attributes: + +.. code-block:: json + + { + "name": "string", + "apiVersion": "string|number", + "baseUrl": "string", + "description": "string", + "operations": {}, + "models": {}, + "includes": ["string.php", "string.json"] + } + ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| Property Name | Value | Description | ++=========================================+=========================+=======================================================================================================================+ +| name | string | Name of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| apiVersion | string|number | Version identifier that the service description is compatible with | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| baseUrl or basePath | string | Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the | +| | | process defined in RFC 2396. Some clients require custom logic to determine the baseUrl. In those cases, it is best | +| | | to not include a baseUrl in the service description, but rather allow the factory method of the client to configure | +| | | the client’s baseUrl. | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| description | string | Short summary of the web service | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| operations | object containing | Operations of the service. The key is the name of the operation and value is the attributes of the operation. | +| | :ref:`operation-schema` | | +| | | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| models | object containing | Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP | +| | :ref:`model-schema` | response is parsed into a ``Guzzle\Service\Resource\Model`` object when an operation uses a ``model`` ``responseType``| ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| includes | array of .js, | Service description files to include and extend from (can be a .json, .js, or .php file) | +| | .json, or .php | | +| | files. | | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ +| (any additional properties) | mixed | Any additional properties specified as top-level attributes are allowed and will be treated as arbitrary data | ++-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ + +.. _operation-schema: + +Operations +---------- + +Operations are the actions that can be taken on a service. Each operation is given a unique name and has a distinct +endpoint and HTTP method. If an API has a ``DELETE /users/:id`` operation, a satisfactory operation name might be +``DeleteUser`` with a parameter of ``id`` that is inserted into the URI. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "operations": { + "operationName": { + "extends": "string", + "httpMethod": "GET|POST|PUT|DELETE|PATCH|string", + "uri": "string", + "summary": "string", + "class": "string", + "responseClass": "string", + "responseNotes": "string", + "type": "string", + "description": "string", + "responseType": "primitive|class|(model by name)|documentation|(string)", + "deprecated": false, + "errorResponses": [ + { + "code": 500, + "reason": "Unexpected Error", + "class": "string" + } + ], + "data": { + "foo": "bar", + "baz": "bam" + }, + "parameters": {} + } + } + } + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "extends", "string", "Extend from another operation by name. The parent operation must be defined before the child." + "httpMethod", "string", "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" + "uri", "string", "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" + "summary", "string", "Short summary of what the operation does" + "class", "string", "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand. Using this attribute allows you to define an operation using a service description, but allows more customized logic to be implemented in user-land code." + "responseClass", "string", "Defined what is returned from the method. Can be a primitive, class name, or model name. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``." + "responseNotes", "string", "A description of the response returned by the operation" + "responseType", "string", "The type of response that the operation creates: one of primitive, class, model, or documentation. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default." + "deprecated", "boolean", "Whether or not the operation is deprecated" + "errorResponses", "array", "Errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered)" + "data", "object", "Any arbitrary data to associate with the operation" + "parameters", "object containing :ref:`parameter-schema` objects", "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." + "additionalParameters", "A single :ref:`parameter-schema` object", "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." + +additionalParameters +~~~~~~~~~~~~~~~~~~~~ + +When a webservice offers a large number of parameters that all are set in the same location (for example the query +string or a JSON document), defining each parameter individually can require a lot of time and repetition. Furthermore, +some web services allow for completely arbitrary parameters to be supplied for an operation. The +``additionalParameters`` attribute can be used to solve both of these issues. + +As an example, we can define a Twitter API operation quite easily using ``additionalParameters``. The +GetMentions operation accepts a large number of query string parameters. Defining each of these parameters +is ideal because it provide much more introspection for the client and opens the possibility to use the description with +other tools (e.g. a documentation generator). However, you can very quickly provide a "catch-all" serialization rule +that will place any custom parameters supplied to an operation the generated request's query string parameters. + +.. class:: overflow-height-250px + + .. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +responseClass +~~~~~~~~~~~~~ + +The ``responseClass`` attribute is used to define the return value of an operation (what is returned by calling the +``getResult()`` method of a command object). The value set in the responseClass attribute can be one of "primitive" +(meaning the result with be primitive type like a string), a class name meaning the result will be an instance of a +specific user-land class, or a model name meaning the result will be a ``Guzzle\Service\Resource\Model`` object that +uses a :ref:`model schema ` to define how the HTTP response is parsed. + +.. note:: + + Using a class name with a ``responseClass`` will only work if it is supported by the ``class`` that is instantiated + for the operation. Keep this in mind when specifying a custom ``class`` attribute that points to a custom + ``Guzzle\Service\Command\CommandInterface`` class. The default ``class``, + ``Guzzle\Service\Command\OperationCommand``, does support setting custom ``class`` attributes. + +You can specify the name of a class to return a more customized result from the operation (for example, a domain model +object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``. +Here's a very simple example of implementing a custom responseClass object. + +.. code-block:: json + + { + "operations": { + "test": { + "responseClass": "MyApplication\\User" + } + } + } + +.. code-block:: php + + namespace MyApplication; + + use Guzzle\Service\Command\ResponseClassInterface; + use Guzzle\Service\Command\OperationCommand; + + class User implements ResponseClassInterface + { + protected $name; + + public static function fromCommand(OperationCommand $command) + { + $response = $command->getResponse(); + $xml = $response->xml(); + + return new self((string) $xml->name); + } + + public function __construct($name) + { + $this->name = $name; + } + } + +errorResponses +~~~~~~~~~~~~~~ + +``errorResponses`` is an array containing objects that define the errors that could occur while executing the +operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), +'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this +error is encountered). + +ErrorResponsePlugin +^^^^^^^^^^^^^^^^^^^ + +Error responses are by default only used for documentation. If you don't need very complex exception logic for your web +service errors, then you can use the ``Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin`` to automatically throw defined +exceptions when one of the ``errorResponse`` rules are matched. The error response plugin will listen for the +``request.complete`` event of a request created by a command object. Every response (including a successful response) is +checked against the list of error responses for an exact match using the following order of checks: + +1. Does the errorResponse have a defined ``class``? +2. Is the errorResponse ``code`` equal to the status code of the response? +3. Is the errorResponse ``reason`` equal to the reason phrase of the response? +4. Throw the exception stored in the ``class`` attribute of the errorResponse. + +The ``class`` attribute must point to a class that implements +``Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface``. This interface requires that an error response class +implements ``public static function fromCommand(CommandInterface $command, Response $response)``. This method must +return an object that extends from ``\Exception``. After an exception is returned, it is thrown by the plugin. + +.. _parameter-schema: + +Parameter schema +---------------- + +Parameters in both operations and models are represented using the +`JSON schema `_ syntax. + +.. csv-table:: + :header: "Property Name", "Value", "Description" + :widths: 20, 15, 65 + + "name", "string", "Unique name of the parameter" + "type", "string|array", "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." + "instanceOf", "string", "When the type is an object, you can specify the class that the object must implement" + "required", "boolean", "Whether or not the parameter is required" + "default", "mixed", "Default value to use if no value is supplied" + "static", "boolean", "Set to true to specify that the parameter value cannot be changed from the default setting" + "description", "string", "Documentation of the parameter" + "location", "string", "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" + "sentAs", "string", "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." + "filters", "array", "Array of functions to to run a parameter value through." + +filters +~~~~~~~ + +Each value in the array must be a string containing the full class path to a static method or an array of complex +filter information. You can specify static methods of classes using the full namespace class name followed by +"::" (e.g. ``FooBar::baz()``). Some filters require arguments in order to properly filter a value. For complex filters, +use an object containing a ``method`` attribute pointing to a function, and an ``args`` attribute containing an +array of positional arguments to pass to the function. Arguments can contain keywords that are replaced when filtering +a value: ``@value`` is replaced with the value being filtered, and ``@api`` is replaced with the actual Parameter +object. + +.. code-block:: json + + { + "filters": [ + "strtolower", + { + "method": "MyClass::convertString", + "args": [ "test", "@value", "@api" ] + } + ] + } + +The above example will filter a parameter using ``strtolower``. It will then call the ``convertString`` static method +of ``MyClass``, passing in "test", the actual value of the parameter, and a ``Guzzle\Service\Description\Parameter`` +object. + +Operation parameter location attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The location field of top-level parameters control how a parameter is serialized when generating a request. + +uri location +^^^^^^^^^^^^ + +Parameters are injected into the ``uri`` attribute of the operation using +`URI-template expansion `_. + +.. code-block:: json + + { + "operations": { + "uriTest": { + "uri": "/test/{testValue}", + "parameters": { + "testValue": { + "location": "uri" + } + } + } + } + } + +query location +^^^^^^^^^^^^^^ + +Parameters are injected into the query string of a request. Query values can be nested, which would result in a PHP +style nested query string. The name of a parameter is the default name of the query string parameter added to the +request. You can override this behavior by specifying the ``sentAs`` attribute on the parameter. + +.. code-block:: json + + { + "operations": { + "queryTest": { + "parameters": { + "testValue": { + "location": "query", + "sentAs": "test_value" + } + } + } + } + } + +header location +^^^^^^^^^^^^^^^ + +Parameters are injected as headers on an HTTP request. The name of the parameter is used as the name of the header by +default. You can change the name of the header created by the parameter using the ``sentAs`` attribute. + +Headers that are of type ``object`` will be added as multiple headers to a request using the key of the input array as +the header key. Setting a ``sentAs`` attribute along with a type ``object`` will use the value of ``sentAs`` as a +prefix for each header key. + +body location +^^^^^^^^^^^^^ + +Parameters are injected as the body of a request. The input of these parameters may be anything that can be cast to a +string or a ``Guzzle\Http\EntityBodyInterface`` object. + +postField location +^^^^^^^^^^^^^^^^^^ + +Parameters are inserted as POST fields in a request. Nested values may be supplied and will be represented using +PHP style nested query strings. The POST field name is the same as the parameter name by default. You can use the +``sentAs`` parameter to override the POST field name. + +postFile location +^^^^^^^^^^^^^^^^^ + +Parameters are added as POST files. A postFile value may be a string pointing to a local filename or a +``Guzzle\Http\Message\PostFileInterface`` object. The name of the POST file will be the name of the parameter by +default. You can use a custom POST file name by using the ``sentAs`` attribute. + +Supports "string" and "array" types. + +json location +^^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level keys of a JSON document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When JSON parameters are specified, the +``Content-Type`` of the request will change to ``application/json`` if a ``Content-Type`` has not already been specified +on the request. + +xml location +^^^^^^^^^^^^ + +Parameters are added to the body of a request as top level nodes of an XML document. Nested values may be specified, +with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When XML parameters are specified, the +``Content-Type`` of the request will change to ``application/xml`` if a ``Content-Type`` has not already been specified +on the request. + +responseBody location +^^^^^^^^^^^^^^^^^^^^^ + +Specifies the EntityBody of a response. This can be used to download the response body to a file or a custom Guzzle +EntityBody object. + +No location +^^^^^^^^^^^ + +If a parameter has no location attribute, then the parameter is simply used as a data value. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +.. _model-schema: + +Model Schema +------------ + +Models are used in service descriptions to provide generic JSON schema definitions that can be extended from or used in +``$ref`` attributes. Models can also be referenced in a ``responseClass`` attribute to provide valuable output to an +operation. Models are JSON schema documents and use the exact syntax and attributes used in parameters. + +Response Models +~~~~~~~~~~~~~~~ + +Response models describe how a response is parsed into a ``Guzzle\Service\Resource\Model`` object. Response models are +always modeled as JSON schema objects. When an HTTP response is parsed using a response model, the rules specified on +each property of a response model will translate 1:1 as keys in a PHP associative array. When a ``sentAs`` attribute is +found in response model parameters, the value retrieved from the HTTP response is retrieved using the ``sentAs`` +parameter but stored in the response model using the name of the parameter. + +The location field of top-level parameters in a response model tell response parsers how data is retrieved from a +response. + +statusCode location +^^^^^^^^^^^^^^^^^^^ + +Retrieves the status code of the response. + +reasonPhrase location +^^^^^^^^^^^^^^^^^^^^^ + +Retrieves the reason phrase of the response. + +header location +^^^^^^^^^^^^^^^ + +Retrieves a header from the HTTP response. + +body location +^^^^^^^^^^^^^ + +Retrieves the body of an HTTP response. + +json location +^^^^^^^^^^^^^ + +Retrieves a top-level parameter from a JSON document contained in an HTTP response. + +You can use ``additionalProperties`` if the JSON document is wrapped in an outer array. This allows you to parse the +contents of each item in the array using the parsing rules defined in the ``additionalProperties`` schema. + +xml location +^^^^^^^^^^^^ + +Retrieves a top-level node value from an XML document contained in an HTTP response. + +Other locations +^^^^^^^^^^^^^^^ + +Custom locations can be registered as new locations or override default locations if needed. + +Example service description +--------------------------- + +Let's say you're interacting with a web service called 'Foo' that allows for the following routes and methods:: + + GET/POST /users + GET/DELETE /users/:id + +The following JSON service description implements this simple web service: + +.. class:: overflow-height-500px + + .. code-block:: json + + { + "name": "Foo", + "apiVersion": "2012-10-14", + "baseUrl": "http://api.foo.com", + "description": "Foo is an API that allows you to Baz Bar", + "operations": { + "GetUsers": { + "httpMethod": "GET", + "uri": "/users", + "summary": "Gets a list of users", + "responseClass": "GetUsersOutput" + }, + "CreateUser": { + "httpMethod": "POST", + "uri": "/users", + "summary": "Creates a new user", + "responseClass": "CreateUserOutput", + "parameters": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "GetUser": { + "httpMethod": "GET", + "uri": "/users/{id}", + "summary": "Retrieves a single user", + "responseClass": "GetUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to retrieve by ID", + "required": true + } + } + }, + "DeleteUser": { + "httpMethod": "DELETE", + "uri": "/users/{id}", + "summary": "Deletes a user", + "responseClass": "DeleteUserOutput", + "parameters": { + "id": { + "location": "uri", + "description": "User to delete by ID", + "required": true + } + } + } + }, + "models": { + "GetUsersOutput": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + } + }, + "CreateUserOutput": { + "type": "object", + "properties": { + "id": { + "location": "json", + "type": "string" + }, + "location": { + "location": "header", + "sentAs": "Location", + "type": "string" + } + } + }, + "GetUserOutput": { + "type": "object", + "properties": { + "name": { + "location": "json", + "type": "string" + }, + "age": { + "location": "json", + "type": "integer" + } + } + }, + "DeleteUserOutput": { + "type": "object", + "properties": { + "status": { + "location": "statusCode", + "type": "integer" + } + } + } + } + } + +If you attach this service description to a client, you would completely configure the client to interact with the +Foo web service and provide valuable response models for each operation. + +.. code-block:: php + + use Guzzle\Service\Description\ServiceDescription; + + $description = ServiceDescription::factory('/path/to/client.json'); + $client->setDescription($description); + + $command = $client->getCommand('DeleteUser', array('id' => 123)); + $responseModel = $client->execute($command); + echo $responseModel['status']; + +.. note:: + + You can add the service description to your client's factory method or constructor. diff --git a/source/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst b/source/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst new file mode 100644 index 0000000..b7113d6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/webservice-client/using-the-service-builder.rst @@ -0,0 +1,316 @@ +======================= +Using a service builder +======================= + +The best way to instantiate Guzzle web service clients is to let Guzzle handle building the clients for you using a +ServiceBuilder. A ServiceBuilder is responsible for creating concrete client objects based on configuration settings +and helps to manage credentials for different environments. + +You don't have to use a service builder, but they help to decouple your application from concrete classes and help to +share configuration data across multiple clients. Consider the following example. Here we are creating two clients that +require the same API public key and secret key. The clients are created using their ``factory()`` methods. + +.. code-block:: php + + use MyService\FooClient; + use MyService\BarClient; + + $foo = FooClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'and above all' + )); + + $bar = BarClient::factory(array( + 'key' => 'abc', + 'secret' => '123', + 'custom' => 'listen to me' + )); + +The redundant specification of the API keys can be removed using a service builder. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory(array( + 'services' => array( + 'abstract_client' => array( + 'params' => array( + 'key' => 'abc', + 'secret' => '123' + ) + ), + 'foo' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'and above all' + ) + ), + 'bar' => array( + 'extends' => 'abstract_client', + 'class' => 'MyService\FooClient', + 'params' => array( + 'custom' => 'listen to me' + ) + ) + ) + )); + + $foo = $builder->get('foo'); + $bar = $builder->get('bar'); + +You can make managing your API keys even easier by saving the service builder configuration in a JSON format in a +.json file. + +Creating a service builder +-------------------------- + +A ServiceBuilder can source information from an array, an PHP include file that returns an array, or a JSON file. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Source service definitions from a JSON file + $builder = ServiceBuilder::factory('services.json'); + +Sourcing data from an array +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Data can be source from a PHP array. The array must contain an associative ``services`` array that maps the name of a +client to the configuration information used by the service builder to create the client. Clients are given names +which are used to identify how a client is retrieved from a service builder. This can be useful for using multiple +accounts for the same service or creating development clients vs. production clients. + +.. code-block:: php + + $services = array( + 'includes' => array( + '/path/to/other/services.json', + '/path/to/other/php_services.php' + ), + 'services' => array( + 'abstract.foo' => array( + 'params' => array( + 'username' => 'foo', + 'password' => 'bar' + ) + ), + 'bar' => array( + 'extends' => 'abstract.foo', + 'class' => 'MyClientClass', + 'params' => array( + 'other' => 'abc' + ) + ) + ) + ); + +A service builder configuration array contains two top-level array keys: + ++------------+---------------------------------------------------------------------------------------------------------+ +| Key | Description | ++============+=========================================================================================================+ +| includes | Array of paths to JSON or PHP include files to include in the configuration. | ++------------+---------------------------------------------------------------------------------------------------------+ +| services | Associative array of defined services that can be created by the service builder. Each service can | +| | contain the following keys: | +| | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | Key | Description | | +| | +============+========================================================================================+ | +| | | class | The concrete class to instantiate that implements the | | +| | | | ``Guzzle\Common\FromConfigInterface``. | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | extends | The name of a previously defined service to extend from | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | params | Associative array of parameters to pass to the factory method of the service it is | | +| | | | instantiated | | +| | +------------+----------------------------------------------------------------------------------------+ | +| | | alias | An alias that can be used in addition to the array key for retrieving a client from | | +| | | | the service builder. | | +| | +------------+----------------------------------------------------------------------------------------+ | ++------------+---------------------------------------------------------------------------------------------------------+ + +The first client defined, ``abstract.foo``, is used as a placeholder of shared configuration values. Any service +extending abstract.foo will inherit its params. As an example, this can be useful when clients share the same username +and password. + +The next client, ``bar``, extends from ``abstract.foo`` using the ``extends`` attribute referencing the client from +which to extend. Additional parameters can be merged into the original service definition when extending a parent +service. + +.. important:: + + Each client that you intend to instantiate must specify a ``class`` attribute that references the full class name + of the client being created. The class referenced in the ``class`` parameter must implement a static ``factory()`` + method that accepts an array or ``Guzzle\Common\Collection`` object and returns an instantiated object. + +Sourcing from a PHP include +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create service builder configurations using a PHP include file. This can be useful if you wish to take +advantage of an opcode cache like APC to speed up the process of loading and processing the configuration. The PHP +include file is the same format as an array, but you simply create a PHP script that returns an array and save the +file with the .php file extension. + +.. code-block:: php + + '...'); + // Saved as config.php + +This configuration file can then be used with a service builder. + +.. code-block:: php + + $builder = ServiceBuilder::factory('/path/to/config.php'); + +Sourcing from a JSON document +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use JSON documents to serialize your service descriptions. The JSON format uses the exact same structure as +the PHP array syntax, but it's just serialized using JSON. + +.. code-block:: javascript + + { + "includes": ["/path/to/other/services.json", "/path/to/other/php_services.php"], + "services": { + "abstract.foo": { + "params": { + "username": "foo", + "password": "bar" + } + }, + "bar": { + "extends": "abstract.foo", + "class": "MyClientClass", + "params": { + "other": "abc" + } + } + } + } + +Referencing other clients in parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If one of your clients depends on another client as one of its parameters, you can reference that client by name by +enclosing the client's reference key in ``{}``. + +.. code-block:: javascript + + { + "services": { + "token": { + "class": "My\Token\TokenFactory", + "params": { + "access_key": "xyz" + } + }, + "client": { + "class": "My\Client", + "params": { + "token_client": "{token}", + "version": "1.0" + } + } + } + } + +When ``client`` is constructed by the service builder, the service builder will first create the ``token`` service +and then inject the token service into ``client``'s factory method in the ``token_client`` parameter. + +Retrieving clients from a service builder +----------------------------------------- + +Clients are referenced using a customizable name you provide in your service definition. The ServiceBuilder is a sort +of multiton object-- it will only instantiate a client once and return that client for subsequent retrievals. Clients +are retrieved by name (the array key used in the configuration) or by the ``alias`` setting of a service. + +Here's an example of retrieving a client from your ServiceBuilder: + +.. code-block:: php + + $client = $builder->get('foo'); + + // You can also use the ServiceBuilder object as an array + $client = $builder['foo']; + +Creating throwaway clients +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get a "throwaway" client (a client that is not persisted by the ServiceBuilder) by passing ``true`` in the +second argument of ``ServiceBuilder::get()``. This allows you to create a client that will not be returned by other +parts of your code that use the service builder. Instead of passing ``true``, you can pass an array of configuration +settings that will override the configuration settings specified in the service builder. + +.. code-block:: php + + // Get a throwaway client and overwrite the "custom" setting of the client + $foo = $builder->get('foo', array( + 'custom' => 'in this world there are rules' + )); + +Getting raw configuration settings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can get the raw configuration settings provided to the service builder for a specific service using the +``getData($name)`` method of a service builder. This method will null if the service was not found in the service +builder or an array of configuration settings if the service was found. + +.. code-block:: php + + $data = $builder->getData('foo'); + echo $data['key'] . "\n"; + echo $data['secret'] . "\n"; + echo $data['custom'] . "\n"; + +Adding a plugin to all clients +------------------------------ + +You can add a plugin to all clients created by a service builder using the ``addGlobalPlugin($plugin)`` method of a +service builder and passing a ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` object. The service builder +will then attach each global plugin to every client as it is created. This allows you to, for example, add a LogPlugin +to every request created by a service builder for easy debugging. + +.. code-block:: php + + use Guzzle\Plugin\Log\LogPlugin; + + // Add a debug log plugin to every client as it is created + $builder->addGlobalPlugin(LogPlugin::getDebugPlugin()); + + $foo = $builder->get('foo'); + $foo->get('/')->send(); + // Should output all of the data sent over the wire + +.. _service-builder-events: + +Events emitted from a service builder +------------------------------------- + +A ``Guzzle\Service\Builder\ServiceBuilder`` object emits the following events: + ++-------------------------------+--------------------------------------------+-----------------------------------------+ +| Event name | Description | Event data | ++===============================+============================================+=========================================+ +| service_builder.create_client | Called when a client is created | * client: The created client object | ++-------------------------------+--------------------------------------------+-----------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Builder\ServiceBuilder; + + $builder = ServiceBuilder::factory('/path/to/config.json'); + + // Add an event listener to print out each client client as it is created + $builder->getEventDispatcher()->addListener('service_builder.create_client', function (Event $e) { + echo 'Client created: ' . get_class($e['client']) . "\n"; + }); + + $foo = $builder->get('foo'); + // Should output the class used for the "foo" client diff --git a/source/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst b/source/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst new file mode 100644 index 0000000..7ec771e --- /dev/null +++ b/source/vendor/guzzle/guzzle/docs/webservice-client/webservice-client.rst @@ -0,0 +1,659 @@ +====================== +The web service client +====================== + +The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web +service API, including commands, service descriptions, and resource iterators. + +In this chapter, we'll build a simple `Twitter API client `_. + +Creating a client +================= + +A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a +``factory()`` method in order to be used with a :doc:`service builder `. + +Factory method +-------------- + +You can use the ``factory()`` method of a client directly if you do not need a service builder. + +.. code-block:: php + + use mtdowling\TwitterClient; + + // Create a client and pass an array of configuration data + $twitter = TwitterClient::factory(array( + 'consumer_key' => '****', + 'consumer_secret' => '****', + 'token' => '****', + 'token_secret' => '****' + )); + +.. note:: + + If you'd like to follow along, here's how to get your Twitter API credentials: + + 1. Visit https://dev.twitter.com/apps + 2. Click on an application that you've created + 3. Click on the "OAuth tool" tab + 4. Copy all of the settings under "OAuth Settings" + +Implementing a factory method +----------------------------- + +Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface`` +or extend from ``Guzzle\Service\Client``. + +.. code-block:: php + + namespace mtdowling; + + use Guzzle\Common\Collection; + use Guzzle\Plugin\Oauth\OauthPlugin; + use Guzzle\Service\Client; + use Guzzle\Service\Description\ServiceDescription; + + /** + * A simple Twitter API client + */ + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // Provide a hash of default client configuration options + $default = array('base_url' => 'https://api.twitter.com/1.1'); + + // The following values are required when creating the client + $required = array( + 'base_url', + 'consumer_key', + 'consumer_secret', + 'token', + 'token_secret' + ); + + // Merge in default settings and validate the config + $config = Collection::fromConfig($config, $default, $required); + + // Create a new Twitter client + $client = new self($config->get('base_url'), $config); + + // Ensure that the OauthPlugin is attached to the client + $client->addSubscriber(new OauthPlugin($config->toArray())); + + return $client; + } + } + +Service Builder +--------------- + +A service builder is used to easily create web service clients, provides a simple configuration driven approach to +creating clients, and allows you to share configuration settings across multiple clients. You can find out more about +Guzzle's service builder in :doc:`using-the-service-builder`. + +.. code-block:: php + + use Guzzle\Service\Builder\ServiceBuilder; + + // Create a service builder and provide client configuration data + $builder = ServiceBuilder::factory('/path/to/client_config.json'); + + // Get the client from the service builder by name + $twitter = $builder->get('twitter'); + +The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json": + +.. code-block:: json + + { + "services": { + "twitter": { + "class": "mtdowling\\TwitterClient", + "params": { + "consumer_key": "****", + "consumer_secret": "****", + "token": "****", + "token_secret": "****" + } + } + } + } + +.. note:: + + A service builder becomes much more valuable when using multiple web service clients in a single application or + if you need to utilize the same client with varying configuration settings (e.g. multiple accounts). + +Commands +======== + +Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use +parameter driven object for each action of an API. A command is responsible for accepting an array of configuration +parameters, serializing an HTTP request, and parsing an HTTP response. Following the +`command pattern `_, commands in Guzzle offer a greater level of +flexibility when implementing and utilizing a web service client. + +Executing commands +------------------ + +You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an +``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the +command object. Calling either of these execute methods will return the result value of the command. The result value is +the result of parsing the HTTP response with the ``process()`` method. + +.. code-block:: php + + // Get a command from the client and pass an array of parameters + $command = $twitter->getCommand('getMentions', array( + 'count' => 5 + )); + + // Other parameters can be set on the command after it is created + $command['trim_user'] = false; + + // Execute the command using the command object. + // The result value contains an array of JSON data from the response + $result = $command->execute(); + + // You can retrieve the result of the command later too + $result = $command->getResult(). + +Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with +the command. + +.. code-block:: php + + $request = $command->getRequest(); + $response = $command->getResponse(); + +.. note:: + + The format and notation used to retrieve commands from a client can be customized by injecting a custom command + factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``. + +Executing with magic methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using method missing magic methods with a command, the command will be executed right away and the result of the +command is returned. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Creating commands +----------------- + +Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using +the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the +command or the request serialized by the command. + +When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``. +By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command +(concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about +concrete commands and operation commands later in this chapter. + +.. code-block:: php + + // Get a command from the twitter client. + $command = $twitter->getCommand('getMentions'); + $result = $command->execute(); + +Unless you've skipped ahead, running the above code will throw an exception. + + PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message + 'Command was not found matching getMentions' + +This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now. + +Concrete commands +~~~~~~~~~~~~~~~~~ + +Commands can be created in one of two ways: create a concrete command class that extends +``Guzzle\Service\Command\AbstractCommand`` or +:doc:`create an OperationCommand based on a service description `. The recommended +approach is to use a service description to define your web service, but you can use concrete commands when custom +logic must be implemented for marshaling or unmarshaling a HTTP message. + +Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take +action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command +object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible +for executing commands. Commands created for your web service must implement +``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand`` +class, implement the ``build()`` method, and optionally implement the ``process()`` method. + +Serializing requests +^^^^^^^^^^^^^^^^^^^^ + +The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a +HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of +for you when using a service description driven command that uses the default +``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't +using a service description or need to implement more complex request serialization. + +.. important:::: + + When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an + instantiated and ready to send request. + +The following example shows how to implement the ``getMentions`` +`Twitter API `_ method using a concrete command. + +.. code-block:: php + + namespace mtdowling\Twitter\Command; + + use Guzzle\Service\Command\AbstractCommand; + + class GetMentions extends AbstractCommand + { + protected function build() + { + // Create the request property of the command + $this->request = $this->client->get('statuses/mentions_timeline.json'); + + // Grab the query object of the request because we will use it for + // serializing command parameters on the request + $query = $this->request->getQuery(); + + if ($this['count']) { + $query->set('count', $this['count']); + } + + if ($this['since_id']) { + $query->set('since_id', $this['since_id']); + } + + if ($this['max_id']) { + $query->set('max_id', $this['max_id']); + } + + if ($this['trim_user'] !== null) { + $query->set('trim_user', $this['trim_user'] ? 'true' : 'false'); + } + + if ($this['contributor_details'] !== null) { + $query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false'); + } + + if ($this['include_entities'] !== null) { + $query->set('include_entities', $this['include_entities'] ? 'true' : 'false'); + } + } + } + +By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First +the client will attempt to find an exact match for the name of the command to the name of the command class. If an +exact match is not found, the client will calculate a class name using inflection. This is calculated based on the +folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on +how the command names are calculated: + +#. ``Foo\Command\JarJar`` **->** jar_jar +#. ``Foo\Command\Test`` **->** test +#. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person + +Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted +to lowercased snake_casing (e.g. JarJar == jar_jar). + +Parsing responses +^^^^^^^^^^^^^^^^^ + +The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For +example, a service description operation that has specified a model object in the ``responseClass`` attribute of the +operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be +completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom +``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the +default Guzzle response parsing functionality or get inspiration from existing code by using +``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes. + +If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt +to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API +sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response +parsing. + +Operation commands +~~~~~~~~~~~~~~~~~~ + +Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are +driven by a Guzzle service description. Because request serialization, validation, and response parsing are +described using a DSL, creating operation commands is a much faster process than writing concrete commands. + +Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete +command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to +create the Twitter API client: + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "parameters": { + "count": { + "description": "Specifies the number of tweets to try and retrieve", + "type": "integer", + "location": "query" + }, + "since_id": { + "description": "Returns results with an ID greater than the specified ID", + "type": "integer", + "location": "query" + }, + "max_id": { + "description": "Returns results with an ID less than or equal to the specified ID.", + "type": "integer", + "location": "query" + }, + "trim_user": { + "description": "Limits the amount of data returned for each user", + "type": "boolean", + "location": "query" + }, + "contributor_details": { + "description": "Adds more data to contributor elements", + "type": "boolean", + "location": "query" + }, + "include_entities": { + "description": "The entities node will be disincluded when set to false.", + "type": "boolean", + "location": "query" + } + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``. +``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined +in a service description. + +.. code-block:: json + + { + "name": "Twitter", + "apiVersion": "1.1", + "baseUrl": "https://api.twitter.com/1.1", + "description": "Twitter REST API client", + "operations": { + "GetMentions": { + "httpMethod": "GET", + "uri": "statuses/mentions_timeline.json", + "summary": "Returns the 20 most recent mentions for the authenticating user.", + "responseClass": "GetMentionsOutput", + "additionalParameters": { + "location": "query" + } + } + }, + "models": { + "GetMentionsOutput": { + "type": "object", + "additionalProperties": { + "location": "json" + } + } + } + } + +You should attach the service description to the client at the end of the client's factory method: + +.. code-block:: php + + // ... + class TwitterClient extends Client + { + public static function factory($config = array()) + { + // ... same code as before ... + + // Set the service description + $client->setDescription(ServiceDescription::factory('path/to/twitter.json')); + + return $client; + } + } + +The client can now use operations defined in the service description instead of requiring you to create concrete +command classes. Feel free to delete the concrete command class we created earlier. + +.. code-block:: php + + $jsonData = $twitter->getMentions(array( + 'count' => 5, + 'trim_user' => true + )); + +Executing commands in parallel +------------------------------ + +Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by +passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and +send them all in parallel. If an error is encountered during the transfer, then a +``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that +succeeded and a list of commands that failed. + +.. code-block:: php + + use Guzzle\Service\Exception\CommandTransferException; + + $commands = array(); + $commands[] = $twitter->getCommand('getMentions'); + $commands[] = $twitter->getCommand('otherCommandName'); + // etc... + + try { + $result = $client->execute($commands); + foreach ($result as $command) { + echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n"; + } + } catch (CommandTransferException $e) { + // Get an array of the commands that succeeded + foreach ($e->getSuccessfulCommands() as $command) { + echo $command->getName() . " succeeded\n"; + } + // Get an array of the commands that failed + foreach ($e->getFailedCommands() as $command) { + echo $command->getName() . " failed\n"; + } + } + +.. note:: + + All commands executed from a client using an array must originate from the same client. + +Special command options +----------------------- + +Guzzle exposes several options that help to control how commands are validated, serialized, and parsed. +Command options can be specified when creating a command or in the ``command.params`` parameter in the +``Guzzle\Service\Client``. + +=========================== ============================================================================================ +command.request_options Option used to add :ref:`Request options ` to the request created by a + command +command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema +command.disable_validation Set to true to disable JSON schema validation of the command's input parameters +command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing, + "model" (the default method used to parse commands using response models defined in service + descriptions) +command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead +command.on_complete (deprecated) Option used to add an onComplete method to a command. Use + ``command.after_send`` event instead +command.response_body (deprecated) Option used to change the entity body used to store a response. + Use ``command.request_options`` instead +=========================== ============================================================================================ + +Advanced client configuration +============================= + +Default command parameters +-------------------------- + +When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair +present in the ``command.params`` settings of a client will be added as default parameters to any command created +by the client. + +.. code-block:: php + + $client = new Guzzle\Service\Client(array( + 'command.params' => array( + 'default_1' => 'foo', + 'another' => 'bar' + ) + )); + +Magic methods +------------- + +Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client. +This powerful concept applies to both concrete commands and operation commands powered by a service description. This +makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are +invoked using PHP's magic ``__call`` method. + +The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal +``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate +operations defined in a client's service description. The method in which a client determines which command to +execute is defined as follows: + +1. The client will first try to find a literal match for an operation in the service description. +2. If the literal match is not found, the client will try to uppercase the first character of the operation and find + the match again. +3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and + attempt to find a matching command. +4. If a command still does not match, an exception is thrown. + +.. code-block:: php + + // Use the magic method + $result = $twitter->getMentions(); + + // This is exactly the same as: + $result = $twitter->getCommand('getMentions')->execute(); + +You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method. + +Custom command factory +---------------------- + +A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command +factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory`` +and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom +command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of +a client. + +Custom resource Iterator factory +-------------------------------- + +Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses +a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a +``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource +iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client +followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to +specify a custom resource iterator factory. + +Plugins and events +================== + +``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a +``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling +``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or +event subscribers (classes that listen to specific events of a dispatcher). + +.. _service-client-events: + +Events emitted from a Service Client +------------------------------------ + +A ``Guzzle\Service\Client`` object emits the following events: + ++------------------------------+--------------------------------------------+------------------------------------------+ +| Event name | Description | Event data | ++==============================+============================================+==========================================+ +| client.command.create | The client created a command object | * client: Client object | +| | | * command: Command object | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_prepare | Before a command is validated and built. | * command: Command being prepared | +| | This is also before a request is created. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_prepare | After a command instantiates and | * command: Command that was prepared | +| | configures its request object. | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.before_send | The client is about to execute a prepared | * command: Command to execute | +| | command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.after_send | The client successfully completed | * command: The command that was executed | +| | executing a command | | ++------------------------------+--------------------------------------------+------------------------------------------+ +| command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response | +| | and the response is about to be parsed. | about to be parsed. | ++------------------------------+--------------------------------------------+------------------------------------------+ + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Service\Client; + + $client = new Client(); + + // create an event listener that operates on request objects + $client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) { + $command = $event['command']; + $request = $command->getRequest(); + + // do something with request + }); + +.. code-block:: php + + use Guzzle\Common\Event; + use Guzzle\Common\Client; + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + + class EventSubscriber implements EventSubscriberInterface + { + public static function getSubscribedEvents() + { + return array( + 'client.command.create' => 'onCommandCreate', + 'command.parse_response' => 'onParseResponse' + ); + } + + public function onCommandCreate(Event $event) + { + $client = $event['client']; + $command = $event['command']; + // operate on client and command + } + + public function onParseResponse(Event $event) + { + $command = $event['command']; + // operate on the command + } + } + + $client = new Client(); + + $client->addSubscriber(new EventSubscriber()); diff --git a/source/vendor/guzzle/guzzle/phar-stub.php b/source/vendor/guzzle/guzzle/phar-stub.php new file mode 100644 index 0000000..cc2b53f --- /dev/null +++ b/source/vendor/guzzle/guzzle/phar-stub.php @@ -0,0 +1,16 @@ +registerNamespaces(array( + 'Guzzle' => 'phar://guzzle.phar/src', + 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', + 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', + 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' +)); +$classLoader->register(); + +__HALT_COMPILER(); diff --git a/source/vendor/guzzle/guzzle/phing/build.properties.dist b/source/vendor/guzzle/guzzle/phing/build.properties.dist new file mode 100644 index 0000000..c60d3d9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/build.properties.dist @@ -0,0 +1,16 @@ +# you may need to update this if you're working on a fork. +guzzle.remote=git@github.com:guzzle/guzzle.git + +# github credentials -- only used by GitHub API calls to create subtree repos +github.basicauth=username:password +# for the subtree split and testing +github.org=guzzle + +# your git path +cmd.git=git + +# your composer command +cmd.composer=composer + +# test server start +cmd.testserver=node diff --git a/source/vendor/guzzle/guzzle/phing/imports/dependencies.xml b/source/vendor/guzzle/guzzle/phing/imports/dependencies.xml new file mode 100644 index 0000000..e40e037 --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/imports/dependencies.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + using git at ${cmd.git} + + + + found git at ${cmd.git} + + + + + + + + + + diff --git a/source/vendor/guzzle/guzzle/phing/imports/deploy.xml b/source/vendor/guzzle/guzzle/phing/imports/deploy.xml new file mode 100644 index 0000000..109e5ec --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/imports/deploy.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + On branch ${head} + + + + + + + + + + working directory clean + + + ${git.status} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChangeLog Match: ${version.changelog} + Guzzle\Common\Version Match: ${version.version} + + + + releasing: phing -Dnew.version=3.0.x -Dhead=master release + -- + + + + + + + + + + + + + + + BEGINNING RELEASE FOR ${new.version} + + + + + + + + + + + + + + + + + + + + + + + + Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php b/source/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php new file mode 100644 index 0000000..3b70409 --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/tasks/ComposerLintTask.php @@ -0,0 +1,152 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; + +class ComposerLintTask extends Task +{ + protected $dir = null; + protected $file = null; + protected $passthru = false; + protected $composer = null; + + /** + * The setter for the dir + * + * @param string $str Directory to crawl recursively for composer files + */ + public function setDir($str) + { + $this->dir = $str; + } + + /** + * The setter for the file + * + * @param string $str Individual file to validate + */ + public function setFile($str) + { + $this->file = $str; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Composer to execute. If unset, will attempt composer.phar in project + * basedir, and if that fails, will attempt global composer + * installation. + * + * @param string $str Individual file to validate + */ + public function setComposer($str) + { + $this->file = $str; + } + + /** + * The init method: do init steps + */ + public function init() + { + // nothing needed here + } + + /** + * The main entry point + */ + public function main() + { + if ($this->composer === null) { + $this->findComposer(); + } + + $files = array(); + if (!empty($this->file) && file_exists($this->file)) { + $files[] = $this->file; + } + + if (!empty($this->dir)) { + $found = $this->findFiles(); + foreach ($found as $file) { + $files[] = $this->dir . DIRECTORY_SEPARATOR . $file; + } + } + + foreach ($files as $file) { + + $cmd = $this->composer . ' validate ' . $file; + $cmd = escapeshellcmd($cmd); + + if ($this->passthru) { + $retval = null; + passthru($cmd, $retval); + if ($retval == 1) { + throw new BuildException('invalid composer.json'); + } + } else { + $out = array(); + $retval = null; + exec($cmd, $out, $retval); + if ($retval == 1) { + $err = join("\n", $out); + throw new BuildException($err); + } else { + $this->log($out[0]); + } + } + + } + + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findFiles() + { + $ds = new DirectoryScanner(); + $ds->setBasedir($this->dir); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + return $ds->getIncludedFiles(); + } + + /** + * Find composer installation + * + */ + protected function findComposer() + { + $basedir = $this->project->getBasedir(); + $php = $this->project->getProperty('php.interpreter'); + + if (file_exists($basedir . '/composer.phar')) { + $this->composer = "$php $basedir/composer.phar"; + } else { + $out = array(); + exec('which composer', $out); + if (empty($out)) { + throw new BuildException( + 'Could not determine composer location.' + ); + } + $this->composer = $out[0]; + } + } +} diff --git a/source/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php b/source/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php new file mode 100644 index 0000000..f72a6b5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php @@ -0,0 +1,338 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; +require_once 'PEAR/PackageFileManager2.php'; +require_once 'PEAR/PackageFileManager/File.php'; +require_once 'PEAR/Packager.php'; + +class GuzzlePearPharPackageTask extends Task +{ + private $version; + private $deploy = true; + private $makephar = true; + + private $subpackages = array(); + + public function setVersion($str) + { + $this->version = $str; + } + + public function getVersion() + { + return $this->version; + } + + public function setDeploy($deploy) + { + $this->deploy = (bool) $deploy; + } + + public function getDeploy() + { + return $this->deploy; + } + + public function setMakephar($makephar) + { + $this->makephar = (bool) $makephar; + } + + public function getMakephar() + { + return $this->makephar; + } + + private $basedir; + private $guzzleinfo; + private $changelog_release_date; + private $changelog_notes = '-'; + + public function main() + { + $this->basedir = $this->getProject()->getBasedir(); + + if (!is_dir((string) $this->basedir.'/.subsplit')) { + throw new BuildException('PEAR packaging requires .subsplit directory'); + } + + // main composer file + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json'); + $this->guzzleinfo = json_decode($composer_file, true); + + // make sure we have a target + $pearwork = (string) $this->basedir . '/build/pearwork'; + if (!is_dir($pearwork)) { + mkdir($pearwork, 0777, true); + } + $pearlogs = (string) $this->basedir . '/build/artifacts/logs'; + if (!is_dir($pearlogs)) { + mkdir($pearlogs, 0777, true); + } + + $version = $this->getVersion(); + $this->grabChangelog(); + if ($version[0] == '2') { + $this->log('building single PEAR package'); + $this->buildSinglePackage(); + } else { + // $this->log("building PEAR subpackages"); + // $this->createSubPackages(); + // $this->log("building PEAR bundle package"); + $this->buildSinglePackage(); + } + + if ($this->getMakephar()) { + $this->log("building PHAR"); + $this->getProject()->executeTarget('package-phar'); + } + + if ($this->getDeploy()) { + $this->doDeployment(); + } + } + + public function doDeployment() + { + $basedir = (string) $this->basedir; + $this->log('beginning PEAR/PHAR deployment'); + + chdir($basedir . '/build/pearwork'); + if (!is_dir('./channel')) { + mkdir('./channel'); + } + + // Pull the PEAR channel down locally + passthru('aws s3 sync s3://pear.guzzlephp.org ./channel'); + + // add PEAR packages + foreach (scandir('./') as $file) { + if (substr($file, -4) == '.tgz') { + passthru('pirum add ./channel ' . $file); + } + } + + // if we have a new phar, add it + if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) { + rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar'); + } + + // Sync up with the S3 bucket + chdir($basedir . '/build/pearwork/channel'); + passthru('aws s3 sync . s3://pear.guzzlephp.org'); + } + + public function buildSinglePackage() + { + $v = $this->getVersion(); + $apiversion = $v[0] . '.0.0'; + + $opts = array( + 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/', + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json'), + 'baseinstalldir' => '/', + 'packagefile' => 'package.xml' + //'outputdirectory' => (string) $this->basedir . '/build/pearwork/' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->addRole('md', 'doc'); + $pfm->addRole('pem', 'php'); + $pfm->setPackage('Guzzle'); + $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+"); + $pfm->setDescription($this->guzzleinfo['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion($apiversion); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->addExtensionDep('required', 'curl'); + $pfm->setPearinstallerDep('1.4.6'); + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + if (!empty($this->subpackages)) { + foreach ($this->subpackages as $package) { + $pkg = dirname($package); + $pkg = str_replace('/', '_', $pkg); + $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion); + } + } + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log); + chdir($startdir); + } + + public function createSubPackages() + { + $this->findComponents(); + + foreach ($this->subpackages as $package) { + $baseinstalldir = dirname($package); + $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir; + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package); + $package_info = json_decode($composer_file, true); + $this->log('building ' . $package_info['target-dir'] . ' subpackage'); + $this->buildSubPackage($dir, $baseinstalldir, $package_info); + } + } + + public function buildSubPackage($dir, $baseinstalldir, $info) + { + $package = str_replace('/', '_', $baseinstalldir); + $opts = array( + 'packagedirectory' => $dir, + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json', '*package.xml'), + 'baseinstalldir' => '/' . $info['target-dir'], + 'packagefile' => 'package.xml' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->setPackage($package); + $pfm->setSummary($info['description']); + $pfm->setDescription($info['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion('3.0.0'); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->setPearinstallerDep('1.4.6'); + + foreach ($info['require'] as $type => $version) { + if ($type == 'php') { + continue; + } + if ($type == 'symfony/event-dispatcher') { + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + } + if ($type == 'ext-curl') { + $pfm->addExtensionDep('required', 'curl'); + } + if (substr($type, 0, 6) == 'guzzle') { + $gdep = str_replace('/', ' ', $type); + $gdep = ucwords($gdep); + $gdep = str_replace(' ', '_', $gdep); + $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion()); + } + } + + // can't have main Guzzle package AND sub-packages + $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion); + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'/package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'/package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log); + chdir($startdir); + } + + public function findComponents() + { + $ds = new DirectoryScanner(); + $ds->setBasedir((string) $this->basedir.'/.subsplit/src'); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + $files = $ds->getIncludedFiles(); + $this->subpackages = $files; + } + + public function grabChangelog() + { + $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md'); + $notes = ''; + $in_version = false; + $release_date = null; + + foreach ($cl as $line) { + $line = trim($line); + if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) { + $release_date = $matches[1]; + $in_version = true; + continue; + } + if ($in_version && empty($line) && empty($notes)) { + continue; + } + if ($in_version && ! empty($line)) { + $notes .= $line."\n"; + } + if ($in_version && empty($line) && !empty($notes)) { + $in_version = false; + } + } + $this->changelog_release_date = $release_date; + + if (! empty($notes)) { + $this->changelog_notes = $notes; + } + } +} diff --git a/source/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php b/source/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php new file mode 100644 index 0000000..5d56a5b --- /dev/null +++ b/source/vendor/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php @@ -0,0 +1,385 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +// base - base of tree to split out +// subIndicatorFile - composer.json, package.xml? +class GuzzleSubSplitTask extends GitBaseTask +{ + /** + * What git repository to pull from and publish to + */ + protected $remote = null; + + /** + * Publish for comma-separated heads instead of all heads + */ + protected $heads = null; + + /** + * Publish for comma-separated tags instead of all tags + */ + protected $tags = null; + + /** + * Base of the tree RELATIVE TO .subsplit working dir + */ + protected $base = null; + + /** + * The presence of this file will indicate that the directory it resides + * in is at the top level of a split. + */ + protected $subIndicatorFile = 'composer.json'; + + /** + * Do everything except actually send the update. + */ + protected $dryRun = null; + + /** + * Do not sync any heads. + */ + protected $noHeads = false; + + /** + * Do not sync any tags. + */ + protected $noTags = false; + + /** + * The splits we found in the heads + */ + protected $splits; + + public function setRemote($str) + { + $this->remote = $str; + } + + public function getRemote() + { + return $this->remote; + } + + public function setHeads($str) + { + $this->heads = explode(',', $str); + } + + public function getHeads() + { + return $this->heads; + } + + public function setTags($str) + { + $this->tags = explode(',', $str); + } + + public function getTags() + { + return $this->tags; + } + + public function setBase($str) + { + $this->base = $str; + } + + public function getBase() + { + return $this->base; + } + + public function setSubIndicatorFile($str) + { + $this->subIndicatorFile = $str; + } + + public function getSubIndicatorFile() + { + return $this->subIndicatorFile; + } + + public function setDryRun($bool) + { + $this->dryRun = (bool) $bool; + } + + public function getDryRun() + { + return $this->dryRun; + } + + public function setNoHeads($bool) + { + $this->noHeads = (bool) $bool; + } + + public function getNoHeads() + { + return $this->noHeads; + } + + public function setNoTags($bool) + { + $this->noTags = (bool) $bool; + } + + public function getNoTags() + { + return $this->noTags; + } + + /** + * GitClient from VersionControl_Git + */ + protected $client = null; + + /** + * The main entry point + */ + public function main() + { + $repo = $this->getRepository(); + if (empty($repo)) { + throw new BuildException('"repository" is a required parameter'); + } + + $remote = $this->getRemote(); + if (empty($remote)) { + throw new BuildException('"remote" is a required parameter'); + } + + chdir($repo); + $this->client = $this->getGitClient(false, $repo); + + // initalized yet? + if (!is_dir('.subsplit')) { + $this->subsplitInit(); + } else { + // update + $this->subsplitUpdate(); + } + + // find all splits based on heads requested + $this->findSplits(); + + // check that GitHub has the repos + $this->verifyRepos(); + + // execute the subsplits + $this->publish(); + } + + public function publish() + { + $this->log('DRY RUN ONLY FOR NOW'); + $base = $this->getBase(); + $base = rtrim($base, '/') . '/'; + $org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + + $splits = array(); + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo']; + } + + $cmd = 'git subsplit publish '; + $cmd .= escapeshellarg(implode(' ', $splits)); + + if ($this->getNoHeads()) { + $cmd .= ' --no-heads'; + } else { + $cmd .= ' --heads='.$head; + } + + if ($this->getNoTags()) { + $cmd .= ' --no-tags'; + } else { + if ($this->getTags()) { + $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags())); + } + } + + passthru($cmd); + } + } + + /** + * Runs `git subsplit update` + */ + public function subsplitUpdate() + { + $repo = $this->getRepository(); + $this->log('git-subsplit update...'); + $cmd = $this->client->getCommand('subsplit'); + $cmd->addArgument('update'); + try { + $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit update failed'. $e); + } + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar update --dev'); + chdir($repo); + } + + /** + * Runs `git subsplit init` based on the remote repository. + */ + public function subsplitInit() + { + $remote = $this->getRemote(); + $cmd = $this->client->getCommand('subsplit'); + $this->log('running git-subsplit init ' . $remote); + + $cmd->setArguments(array( + 'init', + $remote + )); + + try { + $output = $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit init failed'. $e); + } + $this->log(trim($output), Project::MSG_INFO); + $repo = $this->getRepository(); + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar install --dev'); + chdir($repo); + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findSplits() + { + $this->log("checking heads for subsplits"); + $repo = $this->getRepository(); + $base = $this->getBase(); + + $splits = array(); + $heads = $this->getHeads(); + + if (!empty($base)) { + $base = '/' . ltrim($base, '/'); + } else { + $base = '/'; + } + + chdir($repo . '/.subsplit'); + foreach ($heads as $head) { + $splits[$head] = array(); + + // check each head requested *BEFORE* the actual subtree split command gets it + passthru("git checkout '$head'"); + $ds = new DirectoryScanner(); + $ds->setBasedir($repo . '/.subsplit' . $base); + $ds->setIncludes(array('**/'.$this->subIndicatorFile)); + $ds->scan(); + $files = $ds->getIncludedFiles(); + + // Process the files we found + foreach ($files as $file) { + $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file); + $pkg_json = json_decode($pkg, true); + $name = $pkg_json['name']; + $component = str_replace('/composer.json', '', $file); + // keep this for split cmd + $tmpreponame = explode('/', $name); + $reponame = $tmpreponame[1]; + $splits[$head][$component]['repo'] = $reponame; + $nscomponent = str_replace('/', '\\', $component); + $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description']; + } + } + + // go back to how we found it + passthru("git checkout master"); + chdir($repo); + $this->splits = $splits; + } + + /** + * Based on list of repositories we determined we *should* have, talk + * to GitHub and make sure they're all there. + * + */ + protected function verifyRepos() + { + $this->log('verifying GitHub target repos'); + $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth'); + + if ($github_creds == 'username:password') { + $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1); + return; + } + + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + curl_close($ch); + $repos = json_decode($result, true); + $existing_repos = array(); + + // parse out the repos we found on GitHub + foreach ($repos as $repo) { + $tmpreponame = explode('/', $repo['full_name']); + $reponame = $tmpreponame[1]; + $existing_repos[$reponame] = $repo['description']; + } + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + + $reponame = $meta['repo']; + + if (!isset($existing_repos[$reponame])) { + $this->log("Creating missing repo $reponame"); + $payload = array( + 'name' => $reponame, + 'description' => $meta['desc'], + 'homepage' => 'http://www.guzzlephp.org/', + 'private' => true, + 'has_issues' => false, + 'has_wiki' => false, + 'has_downloads' => true, + 'auto_init' => false + ); + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n"; + curl_close($ch); + } else { + $this->log("Repo $reponame exists", 2); + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/phpunit.xml.dist b/source/vendor/guzzle/guzzle/phpunit.xml.dist new file mode 100644 index 0000000..208fdc0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/phpunit.xml.dist @@ -0,0 +1,48 @@ + + + + + + ./tests/Guzzle/Tests + + + + + + + + + + ./src/Guzzle + + ./src/Guzzle + ./src/Guzzle/Common/Exception/GuzzleException.php + ./src/Guzzle/Http/Exception/HttpException.php + ./src/Guzzle/Http/Exception/ServerErrorResponseException.php + ./src/Guzzle/Http/Exception/ClientErrorResponseException.php + ./src/Guzzle/Http/Exception/TooManyRedirectsException.php + ./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php + ./src/Guzzle/Common/Exception/BadMethodCallException.php + ./src/Guzzle/Common/Exception/InvalidArgumentException.php + ./src/Guzzle/Common/Exception/RuntimeException.php + ./src/Guzzle/Common/Exception/UnexpectedValueException.php + ./src/Guzzle/Service/Exception/ClientNotFoundException.php + ./src/Guzzle/Service/Exception/CommandException.php + ./src/Guzzle/Service/Exception/DescriptionBuilderException.php + ./src/Guzzle/Service/Exception/ServiceBuilderException.php + ./src/Guzzle/Service/Exception/ServiceNotFoundException.php + ./src/Guzzle/Service/Exception/ValidationException.php + ./src/Guzzle/Service/Exception/JsonException.php + + + + + diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php new file mode 100644 index 0000000..0625d71 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php @@ -0,0 +1,66 @@ +decoratedBatch = $decoratedBatch; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + * @codeCoverageIgnore + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->decoratedBatch, $method), $args); + } + + public function add($item) + { + $this->decoratedBatch->add($item); + + return $this; + } + + public function flush() + { + return $this->decoratedBatch->flush(); + } + + public function isEmpty() + { + return $this->decoratedBatch->isEmpty(); + } + + /** + * Trace the decorators associated with the batch + * + * @return array + */ + public function getDecorators() + { + $found = array($this); + if (method_exists($this->decoratedBatch, 'getDecorators')) { + $found = array_merge($found, $this->decoratedBatch->getDecorators()); + } + + return $found; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php new file mode 100644 index 0000000..4d41c54 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/Batch.php @@ -0,0 +1,92 @@ +transferStrategy = $transferStrategy; + $this->divisionStrategy = $divisionStrategy; + $this->queue = new \SplQueue(); + $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); + $this->dividedBatches = array(); + } + + public function add($item) + { + $this->queue->enqueue($item); + + return $this; + } + + public function flush() + { + $this->createBatches(); + + $items = array(); + foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { + while ($dividedBatch->valid()) { + $batch = $dividedBatch->current(); + $dividedBatch->next(); + try { + $this->transferStrategy->transfer($batch); + $items = array_merge($items, $batch); + } catch (\Exception $e) { + throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); + } + } + // Keep the divided batch down to a minimum in case of a later exception + unset($this->dividedBatches[$batchIndex]); + } + + return $items; + } + + public function isEmpty() + { + return count($this->queue) == 0 && count($this->dividedBatches) == 0; + } + + /** + * Create batches for any queued items + */ + protected function createBatches() + { + if (count($this->queue)) { + if ($batches = $this->divisionStrategy->createBatches($this->queue)) { + // Convert arrays into iterators + if (is_array($batches)) { + $batches = new \ArrayIterator($batches); + } + $this->dividedBatches[] = $batches; + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php new file mode 100644 index 0000000..ea99b4d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php @@ -0,0 +1,199 @@ + 'Guzzle\Batch\BatchRequestTransfer', + 'command' => 'Guzzle\Batch\BatchCommandTransfer' + ); + + /** + * Create a new instance of the BatchBuilder + * + * @return BatchBuilder + */ + public static function factory() + { + return new self(); + } + + /** + * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}. + * + * @param $threshold Number of items to allow in the queue before a flush + * + * @return BatchBuilder + */ + public function autoFlushAt($threshold) + { + $this->autoFlush = $threshold; + + return $this; + } + + /** + * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}. + * + * @return BatchBuilder + */ + public function keepHistory() + { + $this->history = true; + + return $this; + } + + /** + * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer + * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator. + * + * @return BatchBuilder + */ + public function bufferExceptions() + { + $this->exceptionBuffering = true; + + return $this; + } + + /** + * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator. + * + * @param mixed $callable Callable function to notify + * + * @return BatchBuilder + * @throws InvalidArgumentException if the argument is not callable + */ + public function notify($callable) + { + $this->afterFlush = $callable; + + return $this; + } + + /** + * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer} + * object as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of requests + * + * @return BatchBuilder + */ + public function transferRequests($batchSize = 50) + { + $className = self::$mapping['request']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Configures the batch to transfer batches commands. Associates as + * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of commands + * + * @return BatchBuilder + */ + public function transferCommands($batchSize = 50) + { + $className = self::$mapping['command']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Specify the strategy used to divide the queue into an array of batches + * + * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches + * + * @return BatchBuilder + */ + public function createBatchesWith(BatchDivisorInterface $divisorStrategy) + { + $this->divisorStrategy = $divisorStrategy; + + return $this; + } + + /** + * Specify the strategy used to transport the items when flush is called + * + * @param BatchTransferInterface $transferStrategy How items are transferred + * + * @return BatchBuilder + */ + public function transferWith(BatchTransferInterface $transferStrategy) + { + $this->transferStrategy = $transferStrategy; + + return $this; + } + + /** + * Create and return the instantiated batch + * + * @return BatchInterface + * @throws RuntimeException if no transfer strategy has been specified + */ + public function build() + { + if (!$this->transferStrategy) { + throw new RuntimeException('No transfer strategy has been specified'); + } + + if (!$this->divisorStrategy) { + throw new RuntimeException('No divisor strategy has been specified'); + } + + $batch = new Batch($this->transferStrategy, $this->divisorStrategy); + + if ($this->exceptionBuffering) { + $batch = new ExceptionBufferingBatch($batch); + } + + if ($this->afterFlush) { + $batch = new NotifyingBatch($batch, $this->afterFlush); + } + + if ($this->autoFlush) { + $batch = new FlushingBatch($batch, $this->autoFlush); + } + + if ($this->history) { + $batch = new HistoryBatch($batch); + } + + return $batch; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php new file mode 100644 index 0000000..e0a2d95 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php @@ -0,0 +1,39 @@ +callable = $callable; + $this->context = $context; + } + + public function createBatches(\SplQueue $queue) + { + return call_user_func($this->callable, $queue, $this->context); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php new file mode 100644 index 0000000..9cbf1ab --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php @@ -0,0 +1,40 @@ +callable = $callable; + $this->context = $context; + } + + public function transfer(array $batch) + { + return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php new file mode 100644 index 0000000..d55ac7d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php @@ -0,0 +1,75 @@ +batchSize = $batchSize; + } + + /** + * Creates batches by grouping commands by their associated client + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof CommandInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, new \ArrayObject(array($item))); + } else { + $groups[$client]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + // Get the client of the first found command + $client = reset($batch)->getClient(); + + // Keep a list of all commands with invalid clients + $invalid = array_filter($batch, function ($command) use ($client) { + return $command->getClient() !== $client; + }); + + if (!empty($invalid)) { + throw new InconsistentClientTransferException($invalid); + } + + $client->execute($batch); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php new file mode 100644 index 0000000..0214f05 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php @@ -0,0 +1,18 @@ +batchSize = $batchSize; + } + + /** + * Creates batches of requests by grouping requests by their associated curl multi object. + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + // Create batches by client objects + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof RequestInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, array($item)); + } else { + $current = $groups[$client]; + $current[] = $item; + $groups[$client] = $current; + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if ($batch) { + reset($batch)->getClient()->send($batch); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php new file mode 100644 index 0000000..67f90a5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php @@ -0,0 +1,47 @@ +size = $size; + } + + /** + * Set the size of each batch + * + * @param int $size Size of each batch + * + * @return BatchSizeDivisor + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Get the size of each batch + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + public function createBatches(\SplQueue $queue) + { + return array_chunk(iterator_to_array($queue, false), $this->size); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php new file mode 100644 index 0000000..2e0b60d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php @@ -0,0 +1,16 @@ +batch = $batch; + $this->transferredItems = $transferredItems; + $this->transferStrategy = $transferStrategy; + $this->divisorStrategy = $divisorStrategy; + parent::__construct( + 'Exception encountered while transferring batch: ' . $exception->getMessage(), + $exception->getCode(), + $exception + ); + } + + /** + * Get the batch that we being sent when the exception occurred + * + * @return array + */ + public function getBatch() + { + return $this->batch; + } + + /** + * Get the items transferred at the point in which the exception was encountered + * + * @return array + */ + public function getTransferredItems() + { + return $this->transferredItems; + } + + /** + * Get the transfer strategy + * + * @return TransferStrategy + */ + public function getTransferStrategy() + { + return $this->transferStrategy; + } + + /** + * Get the divisor strategy + * + * @return DivisorStrategy + */ + public function getDivisorStrategy() + { + return $this->divisorStrategy; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php new file mode 100644 index 0000000..d7a8928 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php @@ -0,0 +1,50 @@ +decoratedBatch->isEmpty()) { + try { + $transferredItems = $this->decoratedBatch->flush(); + } catch (BatchTransferException $e) { + $this->exceptions[] = $e; + $transferredItems = $e->getTransferredItems(); + } + $items = array_merge($items, $transferredItems); + } + + return $items; + } + + /** + * Get the buffered exceptions + * + * @return array Array of BatchTransferException objects + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Clear the buffered exceptions + */ + public function clearExceptions() + { + $this->exceptions = array(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php new file mode 100644 index 0000000..367b684 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php @@ -0,0 +1,60 @@ +threshold = $threshold; + parent::__construct($decoratedBatch); + } + + /** + * Set the auto-flush threshold + * + * @param int $threshold The auto-flush threshold + * + * @return FlushingBatch + */ + public function setThreshold($threshold) + { + $this->threshold = $threshold; + + return $this; + } + + /** + * Get the auto-flush threshold + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + public function add($item) + { + $this->decoratedBatch->add($item); + if (++$this->currentTotal >= $this->threshold) { + $this->currentTotal = 0; + $this->decoratedBatch->flush(); + } + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php new file mode 100644 index 0000000..e345fdc --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php @@ -0,0 +1,39 @@ +history[] = $item; + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * Get the batch history + * + * @return array + */ + public function getHistory() + { + return $this->history; + } + + /** + * Clear the batch history + */ + public function clearHistory() + { + $this->history = array(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php new file mode 100644 index 0000000..96d04da --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php @@ -0,0 +1,38 @@ +callable = $callable; + parent::__construct($decoratedBatch); + } + + public function flush() + { + $items = $this->decoratedBatch->flush(); + call_user_func($this->callable, $items); + + return $items; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json new file mode 100644 index 0000000..12404d3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Batch/composer.json @@ -0,0 +1,31 @@ +{ + "name": "guzzle/batch", + "description": "Guzzle batch component for batching requests, commands, or custom transfers", + "homepage": "http://guzzlephp.org/", + "keywords": ["batch", "HTTP", "REST", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Batch": "" } + }, + "suggest": { + "guzzle/http": "self.version", + "guzzle/service": "self.version" + }, + "target-dir": "Guzzle/Batch", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php new file mode 100644 index 0000000..a5c5271 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php @@ -0,0 +1,21 @@ +cache; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php new file mode 100644 index 0000000..94e6234 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php @@ -0,0 +1,117 @@ +newInstanceArgs($args); + } + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php new file mode 100644 index 0000000..970c9e2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php @@ -0,0 +1,55 @@ +callables = $callables; + } + + public function contains($id, array $options = null) + { + return call_user_func($this->callables['contains'], $id, $options); + } + + public function delete($id, array $options = null) + { + return call_user_func($this->callables['delete'], $id, $options); + } + + public function fetch($id, array $options = null) + { + return call_user_func($this->callables['fetch'], $id, $options); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php new file mode 100644 index 0000000..e1aaf9f --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->contains($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->delete($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->fetch($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($id, $data, $lifeTime !== false ? $lifeTime : 0); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php new file mode 100644 index 0000000..68bd4af --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php @@ -0,0 +1,31 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->test($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->remove($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->load($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($data, $id, array(), $lifeTime); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php new file mode 100644 index 0000000..1fc18a5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->hasItem($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->removeItem($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->getItem($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->setItem($id, $data); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json new file mode 100644 index 0000000..a5d999b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Cache/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/cache", + "description": "Guzzle cache adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Cache": "" } + }, + "target-dir": "Guzzle/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/source/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php new file mode 100644 index 0000000..d1e842b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php @@ -0,0 +1,49 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php new file mode 100644 index 0000000..5cb1535 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php @@ -0,0 +1,403 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws InvalidArgumentException if a parameter is missing + */ + public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) + { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); + } + + return new self($data); + } + + public function count() + { + return count($this->data); + } + + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + public function toArray() + { + return $this->data; + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = array(); + + return $this; + } + + /** + * Get all or a subset of matching key value pairs + * + * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs + * + * @return array Returns an array of all matching key value pairs + */ + public function getAll(array $keys = null) + { + return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an + * array and the new value will be pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + + return $this; + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Case insensitive search the keys in the collection + * + * @param string $key Key to search for + * + * @return bool|string Returns false if not found, otherwise returns the key + */ + public function keySearch($key) + { + foreach (array_keys($this->data) as $k) { + if (!strcasecmp($k, $key)) { + return $k; + } + } + + return false; + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + + return $this; + } + + /** + * Over write key value pairs in this collection with all of the data from an array or collection. + * + * @param array|\Traversable $data Values to override over this config + * + * @return self + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + + return $this; + } + + /** + * Returns a Collection containing all the elements of the collection after applying the callback function to each + * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a + * modified value + * + * @param \Closure $closure Closure to apply + * @param array $context Context to pass to the closure + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function map(\Closure $closure, array $context = array(), $static = true) + { + $collection = $static ? new static() : new self(); + foreach ($this as $key => $value) { + $collection->add($key, $closure($key, $value, $context)); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns + * true, the current value from input is returned into the result Collection. The Closure must accept three + * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. + * + * @param \Closure $closure Closure evaluation function + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function filter(\Closure $closure, $static = true) + { + $collection = ($static) ? new static() : new self(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection->add($key, $value); + } + } + + return $collection; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @return self + * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value + */ + public function setPath($path, $value) + { + $current =& $this->data; + $queue = explode('/', $path); + while (null !== ($key = array_shift($queue))) { + if (!is_array($current)) { + throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); + } elseif (!$queue) { + $current[$key] = $value; + } elseif (isset($current[$key])) { + $current =& $current[$key]; + } else { + $current[$key] = array(); + $current =& $current[$key]; + } + } + + return $this; + } + + /** + * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) + * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This + * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. + * + * @param string $path Path to traverse and retrieve a value from + * @param string $separator Character used to add depth to the search + * @param mixed $data Optional data to descend into (used when wildcards are encountered) + * + * @return mixed|null + */ + public function getPath($path, $separator = '/', $data = null) + { + if ($data === null) { + $data =& $this->data; + } + + $path = is_array($path) ? $path : explode($separator, $path); + while (null !== ($part = array_shift($path))) { + if (!is_array($data)) { + return null; + } elseif (isset($data[$part])) { + $data =& $data[$part]; + } elseif ($part != '*') { + return null; + } else { + // Perform a wildcard search by diverging and merging paths + $result = array(); + foreach ($data as $value) { + if (!$path) { + $result = array_merge_recursive($result, (array) $value); + } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { + $result = array_merge_recursive($result, (array) $test); + } + } + return $result; + } + } + + return $data; + } + + /** + * Inject configuration settings into an input string + * + * @param string $input Input to inject + * + * @return string + * @deprecated + */ + public function inject($input) + { + Version::warn(__METHOD__ . ' is deprecated'); + $replace = array(); + foreach ($this->data as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + return strtr($input, $replace); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php new file mode 100644 index 0000000..fad76a9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php @@ -0,0 +1,52 @@ +context = $context; + } + + public function getIterator() + { + return new \ArrayIterator($this->context); + } + + public function offsetGet($offset) + { + return isset($this->context[$offset]) ? $this->context[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->context[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->context[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->context[$offset]); + } + + public function toArray() + { + return $this->context; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php new file mode 100644 index 0000000..08d1c72 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php @@ -0,0 +1,5 @@ +shortMessage = $message; + } + + /** + * Set all of the exceptions + * + * @param array $exceptions Array of exceptions + * + * @return self + */ + public function setExceptions(array $exceptions) + { + $this->exceptions = array(); + foreach ($exceptions as $exception) { + $this->add($exception); + } + + return $this; + } + + /** + * Add exceptions to the collection + * + * @param ExceptionCollection|\Exception $e Exception to add + * + * @return ExceptionCollection; + */ + public function add($e) + { + $this->exceptions[] = $e; + if ($this->message) { + $this->message .= "\n"; + } + + $this->message .= $this->getExceptionMessage($e, 0); + + return $this; + } + + /** + * Get the total number of request exceptions + * + * @return int + */ + public function count() + { + return count($this->exceptions); + } + + /** + * Allows array-like iteration over the request exceptions + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->exceptions); + } + + /** + * Get the first exception in the collection + * + * @return \Exception + */ + public function getFirst() + { + return $this->exceptions ? $this->exceptions[0] : null; + } + + private function getExceptionMessage(\Exception $e, $depth = 0) + { + static $sp = ' '; + $prefix = $depth ? str_repeat($sp, $depth) : ''; + $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; + + if ($e instanceof self) { + if ($e->shortMessage) { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; + } + foreach ($e as $ee) { + $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); + } + } else { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; + } + + return str_replace(getcwd(), '.', $message); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php new file mode 100644 index 0000000..458e6f2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php @@ -0,0 +1,8 @@ +=5.3.2", + "symfony/event-dispatcher": ">=2.1" + }, + "autoload": { + "psr-0": { "Guzzle\\Common": "" } + }, + "target-dir": "Guzzle/Common", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php new file mode 100644 index 0000000..5005a88 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php @@ -0,0 +1,221 @@ +body = $body; + } + + public function __toString() + { + return (string) $this->body; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->body, $method), $args); + } + + public function close() + { + return $this->body->close(); + } + + public function setRewindFunction($callable) + { + $this->body->setRewindFunction($callable); + + return $this; + } + + public function rewind() + { + return $this->body->rewind(); + } + + public function compress($filter = 'zlib.deflate') + { + return $this->body->compress($filter); + } + + public function uncompress($filter = 'zlib.inflate') + { + return $this->body->uncompress($filter); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->body->getContentType(); + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + $hash = Stream::getHash($this, 'md5', $rawOutput); + + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } + + public function getContentEncoding() + { + return $this->body->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->body->getMetaData($key); + } + + public function getStream() + { + return $this->body->getStream(); + } + + public function setStream($stream, $size = 0) + { + $this->body->setStream($stream, $size); + + return $this; + } + + public function detachStream() + { + $this->body->detachStream(); + + return $this; + } + + public function getWrapper() + { + return $this->body->getWrapper(); + } + + public function getWrapperData() + { + return $this->body->getWrapperData(); + } + + public function getStreamType() + { + return $this->body->getStreamType(); + } + + public function getUri() + { + return $this->body->getUri(); + } + + public function getSize() + { + return $this->body->getSize(); + } + + public function isReadable() + { + return $this->body->isReadable(); + } + + public function isRepeatable() + { + return $this->isSeekable() && $this->isReadable(); + } + + public function isWritable() + { + return $this->body->isWritable(); + } + + public function isConsumed() + { + return $this->body->isConsumed(); + } + + /** + * Alias of isConsumed() + * {@inheritdoc} + */ + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->body->isLocal(); + } + + public function isSeekable() + { + return $this->body->isSeekable(); + } + + public function setSize($size) + { + $this->body->setSize($size); + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->body->seek($offset, $whence); + } + + public function read($length) + { + return $this->body->read($length); + } + + public function write($string) + { + return $this->body->write($string); + } + + public function readLine($maxLength = null) + { + return $this->body->readLine($maxLength); + } + + public function ftell() + { + return $this->body->ftell(); + } + + public function getCustomData($key) + { + return $this->body->getCustomData($key); + } + + public function setCustomData($key, $value) + { + $this->body->setCustomData($key, $value); + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php new file mode 100644 index 0000000..c65c136 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php @@ -0,0 +1,229 @@ +remoteStream = $body; + $this->body = new EntityBody(fopen('php://temp', 'r+')); + } + + /** + * Will give the contents of the buffer followed by the exhausted remote stream. + * + * Warning: Loads the entire stream into memory + * + * @return string + */ + public function __toString() + { + $pos = $this->ftell(); + $this->rewind(); + + $str = ''; + while (!$this->isConsumed()) { + $str .= $this->read(16384); + } + + $this->seek($pos); + + return $str; + } + + public function getSize() + { + return max($this->body->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->ftell(); + } else { + throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->body->getSize()) { + throw new RuntimeException( + "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" + ); + } + + return $this->body->seek($byte); + } + + public function rewind() + { + return $this->seek(0); + } + + /** + * Does not support custom rewind functions + * + * @throws RuntimeException + */ + public function setRewindFunction($callable) + { + throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->body->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have been filled from the remote stream, + // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This + // mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->body->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want to skip bytes from being read from + // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. + $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->body->write($string); + } + + /** + * {@inheritdoc} + * @link http://php.net/manual/en/function.fgets.php + */ + public function readLine($maxLength = null) + { + $buffer = ''; + $size = 0; + while (!$this->isConsumed()) { + $byte = $this->read(1); + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; + } + + public function isConsumed() + { + return $this->body->isConsumed() && $this->remoteStream->isConsumed(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + return $this->remoteStream->close() && $this->body->close(); + } + + public function setStream($stream, $size = 0) + { + $this->remoteStream->setStream($stream, $size); + } + + public function getContentType() + { + return $this->remoteStream->getContentType(); + } + + public function getContentEncoding() + { + return $this->remoteStream->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->remoteStream->getMetaData($key); + } + + public function getStream() + { + return $this->remoteStream->getStream(); + } + + public function getWrapper() + { + return $this->remoteStream->getWrapper(); + } + + public function getWrapperData() + { + return $this->remoteStream->getWrapperData(); + } + + public function getStreamType() + { + return $this->remoteStream->getStreamType(); + } + + public function getUri() + { + return $this->remoteStream->getUri(); + } + + /** + * Always retrieve custom data from the remote stream + * {@inheritdoc} + */ + public function getCustomData($key) + { + return $this->remoteStream->getCustomData($key); + } + + /** + * Always set custom data on the remote stream + * {@inheritdoc} + */ + public function setCustomData($key, $value) + { + $this->remoteStream->setCustomData($key, $value); + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php new file mode 100644 index 0000000..3d7298d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php @@ -0,0 +1,524 @@ +setConfig($config ?: new Collection()); + $this->initSsl(); + $this->setBaseUrl($baseUrl); + $this->defaultHeaders = new Collection(); + $this->setRequestFactory(RequestFactory::getInstance()); + $this->userAgent = $this->getDefaultUserAgent(); + if (!$this->config[self::DISABLE_REDIRECTS]) { + $this->addSubscriber(new RedirectPlugin()); + } + } + + final public function setConfig($config) + { + if ($config instanceof Collection) { + $this->config = $config; + } elseif (is_array($config)) { + $this->config = new Collection($config); + } else { + throw new InvalidArgumentException('Config must be an array or Collection'); + } + + return $this; + } + + final public function getConfig($key = false) + { + return $key ? $this->config[$key] : $this->config; + } + + /** + * Set a default request option on the client that will be used as a default for each request + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * @param mixed $value Value to set + * + * @return $this + */ + public function setDefaultOption($keyOrPath, $value) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + $this->config->setPath($keyOrPath, $value); + + return $this; + } + + /** + * Retrieve a default request option from the client + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * + * @return mixed|null + */ + public function getDefaultOption($keyOrPath) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + + return $this->config->getPath($keyOrPath); + } + + final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) + { + $opts = $this->config[self::CURL_OPTIONS] ?: array(); + + if ($certificateAuthority === true) { + // use bundled CA bundle, set secure defaults + $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; + $opts[CURLOPT_SSL_VERIFYPEER] = true; + $opts[CURLOPT_SSL_VERIFYHOST] = 2; + } elseif ($certificateAuthority === false) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_SSL_VERIFYPEER] = false; + $opts[CURLOPT_SSL_VERIFYHOST] = 0; + } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { + throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); + } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { + throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); + } else { + $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; + $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; + if (is_file($certificateAuthority)) { + unset($opts[CURLOPT_CAPATH]); + $opts[CURLOPT_CAINFO] = $certificateAuthority; + } elseif (is_dir($certificateAuthority)) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_CAPATH] = $certificateAuthority; + } else { + throw new RuntimeException( + 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority + ); + } + } + + $this->config->set(self::CURL_OPTIONS, $opts); + + return $this; + } + + public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) + { + if (!$uri) { + $url = $this->getBaseUrl(); + } else { + if (!is_array($uri)) { + $templateVars = null; + } else { + list($uri, $templateVars) = $uri; + } + if (strpos($uri, '://')) { + // Use absolute URLs as-is + $url = $this->expandTemplate($uri, $templateVars); + } else { + $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); + } + } + + // If default headers are provided, then merge them under any explicitly provided headers for the request + if (count($this->defaultHeaders)) { + if (!$headers) { + $headers = $this->defaultHeaders->toArray(); + } elseif (is_array($headers)) { + $headers += $this->defaultHeaders->toArray(); + } elseif ($headers instanceof Collection) { + $headers = $headers->toArray() + $this->defaultHeaders->toArray(); + } + } + + return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); + } + + public function getBaseUrl($expand = true) + { + return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; + } + + public function setBaseUrl($url) + { + $this->baseUrl = $url; + + return $this; + } + + public function setUserAgent($userAgent, $includeDefault = false) + { + if ($includeDefault) { + $userAgent .= ' ' . $this->getDefaultUserAgent(); + } + $this->userAgent = $userAgent; + + return $this; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public function getDefaultUserAgent() + { + return 'Guzzle/' . Version::VERSION + . ' curl/' . CurlVersion::getInstance()->get('version') + . ' PHP/' . PHP_VERSION; + } + + public function get($uri = null, $headers = null, $options = array()) + { + // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded + return is_array($options) + ? $this->createRequest('GET', $uri, $headers, null, $options) + : $this->createRequest('GET', $uri, $headers, $options); + } + + public function head($uri = null, $headers = null, array $options = array()) + { + return $this->createRequest('HEAD', $uri, $headers, null, $options); + } + + public function delete($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('DELETE', $uri, $headers, $body, $options); + } + + public function put($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PUT', $uri, $headers, $body, $options); + } + + public function patch($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PATCH', $uri, $headers, $body, $options); + } + + public function post($uri = null, $headers = null, $postBody = null, array $options = array()) + { + return $this->createRequest('POST', $uri, $headers, $postBody, $options); + } + + public function options($uri = null, array $options = array()) + { + return $this->createRequest('OPTIONS', $uri, $options); + } + + public function send($requests) + { + if (!($requests instanceof RequestInterface)) { + return $this->sendMultiple($requests); + } + + try { + /** @var $requests RequestInterface */ + $this->getCurlMulti()->add($requests)->send(); + return $requests->getResponse(); + } catch (ExceptionCollection $e) { + throw $e->getFirst(); + } + } + + /** + * Set a curl multi object to be used internally by the client for transferring requests. + * + * @param CurlMultiInterface $curlMulti Multi object + * + * @return self + */ + public function setCurlMulti(CurlMultiInterface $curlMulti) + { + $this->curlMulti = $curlMulti; + + return $this; + } + + /** + * @return CurlMultiInterface|CurlMultiProxy + */ + public function getCurlMulti() + { + if (!$this->curlMulti) { + $this->curlMulti = new CurlMultiProxy( + self::MAX_HANDLES, + $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT + ); + } + + return $this->curlMulti; + } + + public function setRequestFactory(RequestFactoryInterface $factory) + { + $this->requestFactory = $factory; + + return $this; + } + + /** + * Set the URI template expander to use with the client + * + * @param UriTemplateInterface $uriTemplate URI template expander + * + * @return self + */ + public function setUriTemplate(UriTemplateInterface $uriTemplate) + { + $this->uriTemplate = $uriTemplate; + + return $this; + } + + /** + * Expand a URI template while merging client config settings into the template variables + * + * @param string $template Template to expand + * @param array $variables Variables to inject + * + * @return string + */ + protected function expandTemplate($template, array $variables = null) + { + $expansionVars = $this->getConfig()->toArray(); + if ($variables) { + $expansionVars = $variables + $expansionVars; + } + + return $this->getUriTemplate()->expand($template, $expansionVars); + } + + /** + * Get the URI template expander used by the client + * + * @return UriTemplateInterface + */ + protected function getUriTemplate() + { + if (!$this->uriTemplate) { + $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); + } + + return $this->uriTemplate; + } + + /** + * Send multiple requests in parallel + * + * @param array $requests Array of RequestInterface objects + * + * @return array Returns an array of Response objects + */ + protected function sendMultiple(array $requests) + { + $curlMulti = $this->getCurlMulti(); + foreach ($requests as $request) { + $curlMulti->add($request); + } + $curlMulti->send(); + + /** @var $request RequestInterface */ + $result = array(); + foreach ($requests as $request) { + $result[] = $request->getResponse(); + } + + return $result; + } + + /** + * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. + * + * @param RequestInterface $request Request to prepare for the client + * @param array $options Options to apply to the request + * + * @return RequestInterface + */ + protected function prepareRequest(RequestInterface $request, array $options = array()) + { + $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); + + if ($curl = $this->config[self::CURL_OPTIONS]) { + $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); + } + + if ($params = $this->config[self::REQUEST_PARAMS]) { + Version::warn('request.params is deprecated. Use request.options to add default request options.'); + $request->getParams()->overwriteWith($params); + } + + if ($this->userAgent && !$request->hasHeader('User-Agent')) { + $request->setHeader('User-Agent', $this->userAgent); + } + + if ($defaults = $this->config[self::REQUEST_OPTIONS]) { + $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); + } + + if ($options) { + $this->requestFactory->applyOptions($request, $options); + } + + $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); + + return $request; + } + + /** + * Initializes SSL settings + */ + protected function initSsl() + { + $authority = $this->config[self::SSL_CERT_AUTHORITY]; + + if ($authority === 'system') { + return; + } + + if ($authority === null) { + $authority = true; + } + + if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { + $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem'); + } + + $this->setSslVerification($authority); + } + + /** + * @deprecated + */ + public function getDefaultHeaders() + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); + return $this->defaultHeaders; + } + + /** + * @deprecated + */ + public function setDefaultHeaders($headers) + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); + if ($headers instanceof Collection) { + $this->defaultHeaders = $headers; + } elseif (is_array($headers)) { + $this->defaultHeaders = new Collection($headers); + } else { + throw new InvalidArgumentException('Headers must be an array or Collection'); + } + + return $this; + } + + /** + * @deprecated + */ + public function preparePharCacert($md5Check = true) + { + return sys_get_temp_dir() . '/guzzle-cacert.pem'; + } + + /** + * Copies the phar cacert from a phar into the temp directory. + * + * @param string $pharCacertPath Path to the phar cacert. For example: + * 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem' + * + * @return string Returns the path to the extracted cacert file. + * @throws \RuntimeException Throws if the phar cacert cannot be found or + * the file cannot be copied to the temp dir. + */ + public static function extractPharCacert($pharCacertPath) + { + // Copy the cacert.pem file from the phar if it is not in the temp + // folder. + $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; + + if (!file_exists($pharCacertPath)) { + throw new \RuntimeException("Could not find $pharCacertPath"); + } + + if (!file_exists($certFile) || + filesize($certFile) != filesize($pharCacertPath) + ) { + if (!copy($pharCacertPath, $certFile)) { + throw new \RuntimeException( + "Could not copy {$pharCacertPath} to {$certFile}: " + . var_export(error_get_last(), true) + ); + } + } + + return $certFile; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php new file mode 100644 index 0000000..10e4de2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php @@ -0,0 +1,223 @@ +getCurlOptions(); + $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); + $tempContentLength = null; + $method = $request->getMethod(); + $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); + + // Prepare url + $url = (string)$request->getUrl(); + if(($pos = strpos($url, '#')) !== false ){ + // strip fragment from url + $url = substr($url, 0, $pos); + } + + // Array of default cURL options. + $curlOptions = array( + CURLOPT_URL => $url, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_PORT => $request->getPort(), + CURLOPT_HTTPHEADER => array(), + CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' + ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + // Verifies the authenticity of the peer's certificate + CURLOPT_SSL_VERIFYPEER => 1, + // Certificate must indicate that the server is the server to which you meant to connect + CURLOPT_SSL_VERIFYHOST => 2 + ); + + if (defined('CURLOPT_PROTOCOLS')) { + // Allow only HTTP and HTTPS protocols + $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + // Add CURLOPT_ENCODING if Accept-Encoding header is provided + if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { + $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; + // Let cURL set the Accept-Encoding header, prevents duplicate values + $request->removeHeader('Accept-Encoding'); + } + + // Enable curl debug information if the 'debug' param was set + if ($requestCurlOptions->get('debug')) { + $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); + // @codeCoverageIgnoreStart + if (false === $curlOptions[CURLOPT_STDERR]) { + throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); + } + // @codeCoverageIgnoreEnd + $curlOptions[CURLOPT_VERBOSE] = true; + } + + // Specify settings according to the HTTP method + if ($method == 'GET') { + $curlOptions[CURLOPT_HTTPGET] = true; + } elseif ($method == 'HEAD') { + $curlOptions[CURLOPT_NOBODY] = true; + // HEAD requests do not use a write function + unset($curlOptions[CURLOPT_WRITEFUNCTION]); + } elseif (!($request instanceof EntityEnclosingRequest)) { + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + } else { + + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + + // Handle sending raw bodies in a request + if ($request->getBody()) { + // You can send the body as a string using curl's CURLOPT_POSTFIELDS + if ($bodyAsString) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Allow curl to add the Content-Length for us to account for the times when + // POST redirects are followed by GET requests + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + } + // Remove the curl generated Content-Type header if none was set manually + if (!$request->hasHeader('Content-Type')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } else { + $curlOptions[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; + } + // Add a callback for curl to read data to send with the request only if a body was specified + $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); + // Attempt to seek to the start of the stream + $request->getBody()->seek(0); + } + + } else { + + // Special handling for POST specific fields and files + $postFields = false; + if (count($request->getPostFiles())) { + $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); + foreach ($request->getPostFiles() as $key => $data) { + $prefixKeys = count($data) > 1; + foreach ($data as $index => $file) { + // Allow multiple files in the same key + $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; + $postFields[$fieldKey] = $file->getCurlValue(); + } + } + } elseif (count($request->getPostFields())) { + $postFields = (string) $request->getPostFields()->useUrlEncoding(true); + } + + if ($postFields !== false) { + if ($method == 'POST') { + unset($curlOptions[CURLOPT_CUSTOMREQUEST]); + $curlOptions[CURLOPT_POST] = true; + } + $curlOptions[CURLOPT_POSTFIELDS] = $postFields; + $request->removeHeader('Content-Length'); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + // If a Content-Length header was specified but we want to allow curl to set one for us + if (null !== $tempContentLength) { + $request->removeHeader('Content-Length'); + } + + // Set custom cURL options + foreach ($requestCurlOptions->toArray() as $key => $value) { + if (is_numeric($key)) { + $curlOptions[$key] = $value; + } + } + + // Do not set an Accept header by default + if (!isset($curlOptions[CURLOPT_ENCODING])) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + + // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. + foreach ($request->getHeaderLines() as $line) { + $curlOptions[CURLOPT_HTTPHEADER][] = $line; + } + + // Add the content-length header back if it was temporarily removed + if (null !== $tempContentLength) { + $request->setHeader('Content-Length', $tempContentLength); + } + + // Apply the options to a new cURL handle. + $handle = curl_init(); + + // Enable the progress function if the 'progress' param was set + if ($requestCurlOptions->get('progress')) { + // Wrap the function in a function that provides the curl handle to the mediator's progress function + // Using this rather than injecting the handle into the mediator prevents a circular reference + $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { + $args = func_get_args(); + $args[] = $handle; + + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + + call_user_func_array(array($mediator, 'progress'), $args); + }; + $curlOptions[CURLOPT_NOPROGRESS] = false; + } + + curl_setopt_array($handle, $curlOptions); + + return new static($handle, $curlOptions); + } + + /** + * Construct a new CurlHandle object that wraps a cURL handle + * + * @param resource $handle Configured cURL handle resource + * @param Collection|array $options Curl options to use with the handle + * + * @throws InvalidArgumentException + */ + public function __construct($handle, $options) + { + if (!is_resource($handle)) { + throw new InvalidArgumentException('Invalid handle provided'); + } + if (is_array($options)) { + $this->options = new Collection($options); + } elseif ($options instanceof Collection) { + $this->options = $options; + } else { + throw new InvalidArgumentException('Expected array or Collection'); + } + $this->handle = $handle; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->close(); + } + + /** + * Close the curl handle + */ + public function close() + { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + $this->handle = null; + } + + /** + * Check if the handle is available and still OK + * + * @return bool + */ + public function isAvailable() + { + return is_resource($this->handle); + } + + /** + * Get the last error that occurred on the cURL handle + * + * @return string + */ + public function getError() + { + return $this->isAvailable() ? curl_error($this->handle) : ''; + } + + /** + * Get the last error number that occurred on the cURL handle + * + * @return int + */ + public function getErrorNo() + { + if ($this->errorNo) { + return $this->errorNo; + } + + return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; + } + + /** + * Set the curl error number + * + * @param int $error Error number to set + * + * @return CurlHandle + */ + public function setErrorNo($error) + { + $this->errorNo = $error; + + return $this; + } + + /** + * Get cURL curl_getinfo data + * + * @param int $option Option to retrieve. Pass null to retrieve all data as an array. + * + * @return array|mixed + */ + public function getInfo($option = null) + { + if (!is_resource($this->handle)) { + return null; + } + + if (null !== $option) { + return curl_getinfo($this->handle, $option) ?: null; + } + + return curl_getinfo($this->handle) ?: array(); + } + + /** + * Get the stderr output + * + * @param bool $asResource Set to TRUE to get an fopen resource + * + * @return string|resource|null + */ + public function getStderr($asResource = false) + { + $stderr = $this->getOptions()->get(CURLOPT_STDERR); + if (!$stderr) { + return null; + } + + if ($asResource) { + return $stderr; + } + + fseek($stderr, 0); + $e = stream_get_contents($stderr); + fseek($stderr, 0, SEEK_END); + + return $e; + } + + /** + * Get the URL that this handle is connecting to + * + * @return Url + */ + public function getUrl() + { + return Url::factory($this->options->get(CURLOPT_URL)); + } + + /** + * Get the wrapped curl handle + * + * @return resource|null Returns the cURL handle or null if it was closed + */ + public function getHandle() + { + return $this->isAvailable() ? $this->handle : null; + } + + /** + * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl + * handle after it is created. + * + * @return Collection + */ + public function getOptions() + { + return $this->options; + } + + /** + * Update a request based on the log messages of the CurlHandle + * + * @param RequestInterface $request Request to update + */ + public function updateRequestFromTransfer(RequestInterface $request) + { + if (!$request->getResponse()) { + return; + } + + // Update the transfer stats of the response + $request->getResponse()->setInfo($this->getInfo()); + + if (!$log = $this->getStderr(true)) { + return; + } + + // Parse the cURL stderr output for outgoing requests + $headers = ''; + fseek($log, 0); + while (($line = fgets($log)) !== false) { + if ($line && $line[0] == '>') { + $headers = substr(trim($line), 2) . "\r\n"; + while (($line = fgets($log)) !== false) { + if ($line[0] == '*' || $line[0] == '<') { + break; + } else { + $headers .= trim($line) . "\r\n"; + } + } + } + } + + // Add request headers to the request exactly as they were sent + if ($headers) { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); + if (!empty($parsed['headers'])) { + $request->setHeaders(array()); + foreach ($parsed['headers'] as $name => $value) { + $request->setHeader($name, $value); + } + } + if (!empty($parsed['version'])) { + $request->setProtocolVersion($parsed['version']); + } + } + } + + /** + * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere + * + * @param array|Collection $config The configuration we want to parse + * + * @return array + */ + public static function parseCurlConfig($config) + { + $curlOptions = array(); + foreach ($config as $key => $value) { + if (is_string($key) && defined($key)) { + // Convert constants represented as string to constant int values + $key = constant($key); + } + if (is_string($value) && defined($value)) { + $value = constant($value); + } + $curlOptions[$key] = $value; + } + + return $curlOptions; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php new file mode 100644 index 0000000..9e4e637 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php @@ -0,0 +1,423 @@ + array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') + ); + + /** @var float */ + protected $selectTimeout; + + public function __construct($selectTimeout = 1.0) + { + $this->selectTimeout = $selectTimeout; + $this->multiHandle = curl_multi_init(); + // @codeCoverageIgnoreStart + if ($this->multiHandle === false) { + throw new CurlException('Unable to create multi handle'); + } + // @codeCoverageIgnoreEnd + $this->reset(); + } + + public function __destruct() + { + if (is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + } + + public function add(RequestInterface $request) + { + $this->requests[] = $request; + // If requests are currently transferring and this is async, then the + // request must be prepared now as the send() method is not called. + $this->beforeSend($request); + $this->dispatch(self::ADD_REQUEST, array('request' => $request)); + + return $this; + } + + public function all() + { + return $this->requests; + } + + public function remove(RequestInterface $request) + { + $this->removeHandle($request); + if (($index = array_search($request, $this->requests, true)) !== false) { + $request = $this->requests[$index]; + unset($this->requests[$index]); + $this->requests = array_values($this->requests); + $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); + return true; + } + + return false; + } + + public function reset($hard = false) + { + // Remove each request + if ($this->requests) { + foreach ($this->requests as $request) { + $this->remove($request); + } + } + + $this->handles = new \SplObjectStorage(); + $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); + } + + public function send() + { + $this->perform(); + $exceptions = $this->exceptions; + $successful = $this->successful; + $this->reset(); + + if ($exceptions) { + $this->throwMultiException($exceptions, $successful); + } + } + + public function count() + { + return count($this->requests); + } + + /** + * Build and throw a MultiTransferException + * + * @param array $exceptions Exceptions encountered + * @param array $successful Successful requests + * @throws MultiTransferException + */ + protected function throwMultiException(array $exceptions, array $successful) + { + $multiException = new MultiTransferException('Errors during multi transfer'); + + while ($e = array_shift($exceptions)) { + $multiException->addFailedRequestWithException($e['request'], $e['exception']); + } + + // Add successful requests + foreach ($successful as $request) { + if (!$multiException->containsRequest($request)) { + $multiException->addSuccessfulRequest($request); + } + } + + throw $multiException; + } + + /** + * Prepare for sending + * + * @param RequestInterface $request Request to prepare + * @throws \Exception on error preparing the request + */ + protected function beforeSend(RequestInterface $request) + { + try { + $state = $request->setState(RequestInterface::STATE_TRANSFER); + if ($state == RequestInterface::STATE_TRANSFER) { + $this->addHandle($request); + } else { + // Requests might decide they don't need to be sent just before + // transfer (e.g. CachePlugin) + $this->remove($request); + if ($state == RequestInterface::STATE_COMPLETE) { + $this->successful[] = $request; + } + } + } catch (\Exception $e) { + // Queue the exception to be thrown when sent + $this->removeErroredRequest($request, $e); + } + } + + private function addHandle(RequestInterface $request) + { + $handle = $this->createCurlHandle($request)->getHandle(); + $this->checkCurlResult( + curl_multi_add_handle($this->multiHandle, $handle) + ); + } + + /** + * Create a curl handle for a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle + */ + protected function createCurlHandle(RequestInterface $request) + { + $wrapper = CurlHandle::factory($request); + $this->handles[$request] = $wrapper; + $this->resourceHash[(int) $wrapper->getHandle()] = $request; + + return $wrapper; + } + + /** + * Get the data from the multi handle + */ + protected function perform() + { + $event = new Event(array('curl_multi' => $this)); + + while ($this->requests) { + // Notify each request as polling + $blocking = $total = 0; + foreach ($this->requests as $request) { + ++$total; + $event['request'] = $request; + $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); + // The blocking variable just has to be non-falsey to block the loop + if ($request->getParams()->hasKey(self::BLOCKING)) { + ++$blocking; + } + } + if ($blocking == $total) { + // Sleep to prevent eating CPU because no requests are actually pending a select call + usleep(500); + } else { + $this->executeHandles(); + } + } + } + + /** + * Execute and select curl handles + */ + private function executeHandles() + { + // The first curl_multi_select often times out no matter what, but is usually required for fast transfers + $selectTimeout = 0.001; + $active = false; + do { + while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); + $this->checkCurlResult($mrc); + $this->processMessages(); + if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { + // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 + usleep(150); + } + $selectTimeout = $this->selectTimeout; + } while ($active); + } + + /** + * Process any received curl multi messages + */ + private function processMessages() + { + while ($done = curl_multi_info_read($this->multiHandle)) { + $request = $this->resourceHash[(int) $done['handle']]; + try { + $this->processResponse($request, $this->handles[$request], $done); + $this->successful[] = $request; + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + } + } + + /** + * Remove a request that encountered an exception + * + * @param RequestInterface $request Request to remove + * @param \Exception $e Exception encountered + */ + protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) + { + $this->exceptions[] = array('request' => $request, 'exception' => $e); + $this->remove($request); + $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); + } + + /** + * Check for errors and fix headers of a request based on a curl response + * + * @param RequestInterface $request Request to process + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @throws CurlException on Curl error + */ + protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) + { + // Set the transfer stats on the response + $handle->updateRequestFromTransfer($request); + // Check if a cURL exception occurred, and if so, notify things + $curlException = $this->isCurlException($request, $handle, $curl); + + // Always remove completed curl handles. They can be added back again + // via events if needed (e.g. ExponentialBackoffPlugin) + $this->removeHandle($request); + + if (!$curlException) { + if ($this->validateResponseWasSet($request)) { + $state = $request->setState( + RequestInterface::STATE_COMPLETE, + array('handle' => $handle) + ); + // Only remove the request if it wasn't resent as a result of + // the state change + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + } + return; + } + + // Set the state of the request to an error + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); + // Allow things to ignore the error if possible + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + + // The error was not handled, so fail + if ($state == RequestInterface::STATE_ERROR) { + /** @var CurlException $curlException */ + throw $curlException; + } + } + + /** + * Remove a curl handle from the curl multi object + * + * @param RequestInterface $request Request that owns the handle + */ + protected function removeHandle(RequestInterface $request) + { + if (isset($this->handles[$request])) { + $handle = $this->handles[$request]; + curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); + unset($this->handles[$request]); + unset($this->resourceHash[(int) $handle->getHandle()]); + $handle->close(); + } + } + + /** + * Check if a cURL transfer resulted in what should be an exception + * + * @param RequestInterface $request Request to check + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @return CurlException|bool + */ + private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) + { + if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { + return false; + } + + $handle->setErrorNo($curl['result']); + $e = new CurlException(sprintf('[curl] %s: %s [url] %s', + $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); + $e->setCurlHandle($handle) + ->setRequest($request) + ->setCurlInfo($handle->getInfo()) + ->setError($handle->getError(), $handle->getErrorNo()); + + return $e; + } + + /** + * Throw an exception for a cURL multi response if needed + * + * @param int $code Curl response code + * @throws CurlException + */ + private function checkCurlResult($code) + { + if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { + throw new CurlException(isset($this->multiErrors[$code]) + ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" + : 'Unexpected cURL error: ' . $code + ); + } + } + + /** + * @link https://github.com/guzzle/guzzle/issues/710 + */ + private function validateResponseWasSet(RequestInterface $request) + { + if ($request->getResponse()) { + return true; + } + + $body = $request instanceof EntityEnclosingRequestInterface + ? $request->getBody() + : null; + + if (!$body) { + $rex = new RequestException( + 'No response was received for a request with no body. This' + . ' could mean that you are saturating your network.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } elseif (!$body->isSeekable() || !$body->seek(0)) { + // Nothing we can do with this. Sorry! + $rex = new RequestException( + 'The connection was unexpectedly closed. The request would' + . ' have been retried, but attempting to rewind the' + . ' request body failed.' + ); + $rex->setRequest($request); + $this->removeErroredRequest($request, $rex); + } else { + $this->remove($request); + // Add the request back to the batch to retry automatically. + $this->requests[] = $request; + $this->addHandle($request); + } + + return false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php new file mode 100644 index 0000000..0ead757 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php @@ -0,0 +1,58 @@ +maxHandles = $maxHandles; + $this->selectTimeout = $selectTimeout; + // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. + // These two statements autoload classes before a system runs out of file descriptors so that you can get back + // valuable error messages if you run out. + class_exists('Guzzle\Http\Message\Response'); + class_exists('Guzzle\Http\Exception\CurlException'); + } + + public function add(RequestInterface $request) + { + $this->queued[] = $request; + + return $this; + } + + public function all() + { + $requests = $this->queued; + foreach ($this->handles as $handle) { + $requests = array_merge($requests, $handle->all()); + } + + return $requests; + } + + public function remove(RequestInterface $request) + { + foreach ($this->queued as $i => $r) { + if ($request === $r) { + unset($this->queued[$i]); + return true; + } + } + + foreach ($this->handles as $handle) { + if ($handle->remove($request)) { + return true; + } + } + + return false; + } + + public function reset($hard = false) + { + $this->queued = array(); + $this->groups = array(); + foreach ($this->handles as $handle) { + $handle->reset(); + } + if ($hard) { + $this->handles = array(); + } + + return $this; + } + + public function send() + { + if ($this->queued) { + $group = $this->getAvailableHandle(); + // Add this handle to a list of handles than is claimed + $this->groups[] = $group; + while ($request = array_shift($this->queued)) { + $group->add($request); + } + try { + $group->send(); + array_pop($this->groups); + $this->cleanupHandles(); + } catch (\Exception $e) { + // Remove the group and cleanup if an exception was encountered and no more requests in group + if (!$group->count()) { + array_pop($this->groups); + $this->cleanupHandles(); + } + throw $e; + } + } + } + + public function count() + { + return count($this->all()); + } + + /** + * Get an existing available CurlMulti handle or create a new one + * + * @return CurlMulti + */ + protected function getAvailableHandle() + { + // Grab a handle that is not claimed + foreach ($this->handles as $h) { + if (!in_array($h, $this->groups, true)) { + return $h; + } + } + + // All are claimed, so create one + $handle = new CurlMulti($this->selectTimeout); + $handle->setEventDispatcher($this->getEventDispatcher()); + $this->handles[] = $handle; + + return $handle; + } + + /** + * Trims down unused CurlMulti handles to limit the number of open connections + */ + protected function cleanupHandles() + { + if ($diff = max(0, count($this->handles) - $this->maxHandles)) { + for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { + if (!count($this->handles[$i])) { + unset($this->handles[$i]); + $diff--; + } + } + $this->handles = array_values($this->handles); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php new file mode 100644 index 0000000..c3f99dd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php @@ -0,0 +1,66 @@ +version) { + $this->version = curl_version(); + } + + return $this->version; + } + + /** + * Get a specific type of curl information + * + * @param string $type Version information to retrieve. This value is one of: + * - version_number: cURL 24 bit version number + * - version: cURL version number, as a string + * - ssl_version_number: OpenSSL 24 bit version number + * - ssl_version: OpenSSL version number, as a string + * - libz_version: zlib version number, as a string + * - host: Information about the host where cURL was built + * - features: A bitmask of the CURL_VERSION_XXX constants + * - protocols: An array of protocols names supported by cURL + * + * @return string|float|bool if the $type is found, and false if not found + */ + public function get($type) + { + $version = $this->getAll(); + + return isset($version[$type]) ? $version[$type] : false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php new file mode 100644 index 0000000..5d1a0cd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php @@ -0,0 +1,147 @@ +request = $request; + $this->emitIo = $emitIo; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader($curl, $header) + { + static $normalize = array("\r", "\n"); + $length = strlen($header); + $header = str_replace($normalize, '', $header); + + if (strpos($header, 'HTTP/') === 0) { + + $startLine = explode(' ', $header, 3); + $code = $startLine[1]; + $status = isset($startLine[2]) ? $startLine[2] : ''; + + // Only download the body of the response to the specified response + // body when a successful response is received. + if ($code >= 200 && $code < 300) { + $body = $this->request->getResponseBody(); + } else { + $body = EntityBody::factory(); + } + + $response = new Response($code, null, $body); + $response->setStatus($code, $status); + $this->request->startResponse($response); + + $this->request->dispatch('request.receive.status_line', array( + 'request' => $this, + 'line' => $header, + 'status_code' => $code, + 'reason_phrase' => $status + )); + + } elseif ($pos = strpos($header, ':')) { + $this->request->getResponse()->addHeader( + trim(substr($header, 0, $pos)), + trim(substr($header, $pos + 1)) + ); + } + + return $length; + } + + /** + * Received a progress notification + * + * @param int $downloadSize Total download size + * @param int $downloaded Amount of bytes downloaded + * @param int $uploadSize Total upload size + * @param int $uploaded Amount of bytes uploaded + * @param resource $handle CurlHandle object + */ + public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) + { + $this->request->dispatch('curl.callback.progress', array( + 'request' => $this->request, + 'handle' => $handle, + 'download_size' => $downloadSize, + 'downloaded' => $downloaded, + 'upload_size' => $uploadSize, + 'uploaded' => $uploaded + )); + } + + /** + * Write data to the response body of a request + * + * @param resource $curl Curl handle + * @param string $write Data that was received + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if ($this->emitIo) { + $this->request->dispatch('curl.callback.write', array( + 'request' => $this->request, + 'write' => $write + )); + } + + if ($response = $this->request->getResponse()) { + return $response->getBody()->write($write); + } else { + // Unexpected data received before response headers - abort transfer + return 0; + } + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + if (!($body = $this->request->getBody())) { + return ''; + } + + $read = (string) $body->read($length); + if ($this->emitIo) { + $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); + } + + return $read; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php new file mode 100644 index 0000000..b60d170 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php @@ -0,0 +1,201 @@ +rewindFunction = $callable; + + return $this; + } + + public function rewind() + { + return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); + } + + /** + * Create a new EntityBody from a string + * + * @param string $string String of data + * + * @return EntityBody + */ + public static function fromString($string) + { + $stream = fopen('php://temp', 'r+'); + if ($string !== '') { + fwrite($stream, $string); + rewind($stream); + } + + return new static($stream); + } + + public function compress($filter = 'zlib.deflate') + { + $result = $this->handleCompression($filter); + $this->contentEncoding = $result ? $filter : false; + + return $result; + } + + public function uncompress($filter = 'zlib.inflate') + { + $offsetStart = 0; + + // When inflating gzipped data, the first 10 bytes must be stripped + // if a gzip header is present + if ($filter == 'zlib.inflate') { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { + $offsetStart = 10; + } + } + + $this->contentEncoding = false; + + return $this->handleCompression($filter, $offsetStart); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + if ($hash = self::getHash($this, 'md5', $rawOutput)) { + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } else { + return false; + } + } + + /** + * Calculate the MD5 hash of an entity body + * + * @param EntityBodyInterface $body Entity body to calculate the hash for + * @param bool $rawOutput Whether or not to use raw output + * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) + * + * @return bool|string Returns an MD5 string on success or FALSE on failure + * @deprecated This will be deprecated soon + * @codeCoverageIgnore + */ + public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) + { + Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); + return $body->getContentMd5($rawOutput, $base64Encode); + } + + public function setStreamFilterContentEncoding($streamFilterContentEncoding) + { + $this->contentEncoding = $streamFilterContentEncoding; + + return $this; + } + + public function getContentEncoding() + { + return strtr($this->contentEncoding, array( + 'zlib.deflate' => 'gzip', + 'bzip2.compress' => 'compress' + )) ?: false; + } + + protected function handleCompression($filter, $offsetStart = 0) + { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + + $handle = fopen('php://temp', 'r+'); + $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); + if (!$filter) { + return false; + } + + // Seek to the offset start if possible + $this->seek($offsetStart); + while ($data = fread($this->stream, 8096)) { + fwrite($handle, $data); + } + + fclose($this->stream); + $this->stream = $handle; + stream_filter_remove($filter); + $stat = fstat($this->stream); + $this->size = $stat['size']; + $this->rebuildCache(); + $this->seek(0); + + // Remove any existing rewind function as the underlying stream has been replaced + $this->rewindFunction = null; + + return true; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php new file mode 100644 index 0000000..e640f57 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php @@ -0,0 +1,73 @@ +isClientError()) { + $label = 'Client error response'; + $class = __NAMESPACE__ . '\\ClientErrorResponseException'; + } elseif ($response->isServerError()) { + $label = 'Server error response'; + $class = __NAMESPACE__ . '\\ServerErrorResponseException'; + } else { + $label = 'Unsuccessful response'; + $class = __CLASS__; + } + + $message = $label . PHP_EOL . implode(PHP_EOL, array( + '[status code] ' . $response->getStatusCode(), + '[reason phrase] ' . $response->getReasonPhrase(), + '[url] ' . $request->getUrl(), + )); + + $e = new $class($message); + $e->setResponse($response); + $e->setRequest($request); + + return $e; + } + + /** + * Set the response that caused the exception + * + * @param Response $response Response to set + */ + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Get the response that caused the exception + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php new file mode 100644 index 0000000..04d7ddc --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php @@ -0,0 +1,8 @@ +curlError = $error; + $this->curlErrorNo = $number; + + return $this; + } + + /** + * Set the associated curl handle + * + * @param CurlHandle $handle Curl handle + * + * @return self + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->handle = $handle; + + return $this; + } + + /** + * Get the associated cURL handle + * + * @return CurlHandle|null + */ + public function getCurlHandle() + { + return $this->handle; + } + + /** + * Get the associated cURL error message + * + * @return string|null + */ + public function getError() + { + return $this->curlError; + } + + /** + * Get the associated cURL error number + * + * @return int|null + */ + public function getErrorNo() + { + return $this->curlErrorNo; + } + + /** + * Returns curl information about the transfer + * + * @return array + */ + public function getCurlInfo() + { + return $this->curlInfo; + } + + /** + * Set curl transfer information + * + * @param array $info Array of curl transfer information + * + * @return self + * @link http://php.net/manual/en/function.curl-getinfo.php + */ + public function setCurlInfo(array $info) + { + $this->curlInfo = $info; + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php new file mode 100644 index 0000000..ee87295 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php @@ -0,0 +1,10 @@ +successfulRequests, $this->failedRequests); + } + + /** + * Add to the array of successful requests + * + * @param RequestInterface $request Successful request + * + * @return self + */ + public function addSuccessfulRequest(RequestInterface $request) + { + $this->successfulRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests + * + * @param RequestInterface $request Failed request + * + * @return self + */ + public function addFailedRequest(RequestInterface $request) + { + $this->failedRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests and associate with exceptions + * + * @param RequestInterface $request Failed request + * @param \Exception $exception Exception to add and associate with + * + * @return self + */ + public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) + { + $this->add($exception) + ->addFailedRequest($request) + ->exceptionForRequest[spl_object_hash($request)] = $exception; + + return $this; + } + + /** + * Get the Exception that caused the given $request to fail + * + * @param RequestInterface $request Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedRequest(RequestInterface $request) + { + $oid = spl_object_hash($request); + + return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; + } + + /** + * Set all of the successful requests + * + * @param array Array of requests + * + * @return self + */ + public function setSuccessfulRequests(array $requests) + { + $this->successfulRequests = $requests; + + return $this; + } + + /** + * Set all of the failed requests + * + * @param array Array of requests + * + * @return self + */ + public function setFailedRequests(array $requests) + { + $this->failedRequests = $requests; + + return $this; + } + + /** + * Get an array of successful requests sent in the multi transfer + * + * @return array + */ + public function getSuccessfulRequests() + { + return $this->successfulRequests; + } + + /** + * Get an array of failed requests sent in the multi transfer + * + * @return array + */ + public function getFailedRequests() + { + return $this->failedRequests; + } + + /** + * Check if the exception object contains a request + * + * @param RequestInterface $request Request to check + * + * @return bool + */ + public function containsRequest(RequestInterface $request) + { + return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php new file mode 100644 index 0000000..274df2c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php @@ -0,0 +1,39 @@ +request = $request; + + return $this; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php new file mode 100644 index 0000000..f0f7cfe --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php @@ -0,0 +1,8 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + public function read($length) + { + $event = array( + 'body' => $this, + 'length' => $length, + 'read' => $this->body->read($length) + ); + $this->dispatch('body.read', $event); + + return $event['read']; + } + + public function write($string) + { + $event = array( + 'body' => $this, + 'write' => $string, + 'result' => $this->body->write($string) + ); + $this->dispatch('body.write', $event); + + return $event['result']; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php new file mode 100644 index 0000000..0d066ff --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php @@ -0,0 +1,220 @@ +params = new Collection(); + $this->headerFactory = new HeaderFactory(); + $this->headers = new HeaderCollection(); + } + + /** + * Set the header factory to use to create headers + * + * @param HeaderFactoryInterface $factory + * + * @return self + */ + public function setHeaderFactory(HeaderFactoryInterface $factory) + { + $this->headerFactory = $factory; + + return $this; + } + + public function getParams() + { + return $this->params; + } + + public function addHeader($header, $value) + { + if (isset($this->headers[$header])) { + $this->headers[$header]->add($value); + } elseif ($value instanceof HeaderInterface) { + $this->headers[$header] = $value; + } else { + $this->headers[$header] = $this->headerFactory->createHeader($header, $value); + } + + return $this; + } + + public function addHeaders(array $headers) + { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function getHeader($header) + { + return $this->headers[$header]; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getHeaderLines() + { + $headers = array(); + foreach ($this->headers as $value) { + $headers[] = $value->getName() . ': ' . $value; + } + + return $headers; + } + + public function setHeader($header, $value) + { + unset($this->headers[$header]); + $this->addHeader($header, $value); + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers->clear(); + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function hasHeader($header) + { + return isset($this->headers[$header]); + } + + public function removeHeader($header) + { + unset($this->headers[$header]); + + return $this; + } + + /** + * @deprecated Use $message->getHeader()->parseParams() + * @codeCoverageIgnore + */ + public function getTokenizedHeader($header, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); + if ($this->hasHeader($header)) { + $data = new Collection(); + foreach ($this->getHeader($header)->parseParams() as $values) { + foreach ($values as $key => $value) { + if ($value === '') { + $data->set($data->count(), $key); + } else { + $data->add($key, $value); + } + } + } + return $data; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setTokenizedHeader($header, $data, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated.'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + return null; + } + + return $header->getDirective($directive); + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + return $header->hasDirective($directive); + } else { + return false; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function addCacheControlDirective($directive, $value = true) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + $this->addHeader('Cache-Control', ''); + $header = $this->getHeader('Cache-Control'); + } + + $header->addDirective($directive, $value); + + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function removeCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + $header->removeDirective($directive); + } + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php new file mode 100644 index 0000000..212850a --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php @@ -0,0 +1,247 @@ +postFields = new QueryString(); + parent::__construct($method, $url, $headers); + } + + /** + * @return string + */ + public function __toString() + { + // Only attempt to include the POST data if it's only fields + if (count($this->postFields) && empty($this->postFiles)) { + return parent::__toString() . (string) $this->postFields; + } + + return parent::__toString() . $this->body; + } + + public function setState($state, array $context = array()) + { + parent::setState($state, $context); + if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { + $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); + } + + return $this->state; + } + + public function setBody($body, $contentType = null) + { + $this->body = EntityBody::factory($body); + + // Auto detect the Content-Type from the path of the request if possible + if ($contentType === null && !$this->hasHeader('Content-Type')) { + $contentType = $this->body->getContentType(); + } + + if ($contentType) { + $this->setHeader('Content-Type', $contentType); + } + + // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. + if (!$this->body->isSeekable() && $this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + + // Set the Content-Length header if it can be determined + $size = $this->body->getContentLength(); + if ($size !== null && $size !== false) { + $this->setHeader('Content-Length', $size); + if ($size > $this->expectCutoff) { + $this->setHeader('Expect', '100-Continue'); + } + } elseif (!$this->hasHeader('Content-Length')) { + if ('1.1' == $this->protocolVersion) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + throw new RequestException( + 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' + ); + } + } + + return $this; + } + + public function getBody() + { + return $this->body; + } + + /** + * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. + * + * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) + * + * @return self + */ + public function setExpectHeaderCutoff($size) + { + $this->expectCutoff = $size; + if ($size === false || !$this->body) { + $this->removeHeader('Expect'); + } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { + $this->setHeader('Expect', '100-Continue'); + } + + return $this; + } + + public function configureRedirects($strict = false, $maxRedirects = 5) + { + $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); + if ($maxRedirects == 0) { + $this->getParams()->set(RedirectPlugin::DISABLE, true); + } else { + $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); + } + + return $this; + } + + public function getPostField($field) + { + return $this->postFields->get($field); + } + + public function getPostFields() + { + return $this->postFields; + } + + public function setPostField($key, $value) + { + $this->postFields->set($key, $value); + $this->processPostFields(); + + return $this; + } + + public function addPostFields($fields) + { + $this->postFields->merge($fields); + $this->processPostFields(); + + return $this; + } + + public function removePostField($field) + { + $this->postFields->remove($field); + $this->processPostFields(); + + return $this; + } + + public function getPostFiles() + { + return $this->postFiles; + } + + public function getPostFile($fieldName) + { + return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; + } + + public function removePostFile($fieldName) + { + unset($this->postFiles[$fieldName]); + $this->processPostFields(); + + return $this; + } + + public function addPostFile($field, $filename = null, $contentType = null, $postname = null) + { + $data = null; + + if ($field instanceof PostFileInterface) { + $data = $field; + } elseif (is_array($filename)) { + // Allow multiple values to be set in a single key + foreach ($filename as $file) { + $this->addPostFile($field, $file, $contentType); + } + return $this; + } elseif (!is_string($filename)) { + throw new RequestException('The path to a file must be a string'); + } elseif (!empty($filename)) { + // Adding an empty file will cause cURL to error out + $data = new PostFile($field, $filename, $contentType, $postname); + } + + if ($data) { + if (!isset($this->postFiles[$data->getFieldName()])) { + $this->postFiles[$data->getFieldName()] = array($data); + } else { + $this->postFiles[$data->getFieldName()][] = $data; + } + $this->processPostFields(); + } + + return $this; + } + + public function addPostFiles(array $files) + { + foreach ($files as $key => $file) { + if ($file instanceof PostFileInterface) { + $this->addPostFile($file, null, null, false); + } elseif (is_string($file)) { + // Convert non-associative array keys into 'file' + if (is_numeric($key)) { + $key = 'file'; + } + $this->addPostFile($key, $file, null, false); + } else { + throw new RequestException('File must be a string or instance of PostFileInterface'); + } + } + + return $this; + } + + /** + * Determine what type of request should be sent based on post fields + */ + protected function processPostFields() + { + if (!$this->postFiles) { + $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); + } else { + $this->setHeader('Content-Type', self::MULTIPART); + if ($this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php new file mode 100644 index 0000000..49ad459 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php @@ -0,0 +1,137 @@ + filenames where filename can be a string or PostFileInterface + * + * @return self + */ + public function addPostFiles(array $files); + + /** + * Configure how redirects are handled for the request + * + * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most + * browsers with follow a 301-302 redirect for a POST request with a GET request. This is + * the default behavior of Guzzle. Enable strict redirects to redirect these responses + * with a POST rather than a GET request. + * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. + * + * @return self + */ + public function configureRedirects($strict = false, $maxRedirects = 5); +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php new file mode 100644 index 0000000..50597b2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php @@ -0,0 +1,182 @@ +header = trim($header); + $this->glue = $glue; + + foreach ((array) $values as $value) { + foreach ((array) $value as $v) { + $this->values[] = $v; + } + } + } + + public function __toString() + { + return implode($this->glue . ' ', $this->toArray()); + } + + public function add($value) + { + $this->values[] = $value; + + return $this; + } + + public function getName() + { + return $this->header; + } + + public function setName($name) + { + $this->header = $name; + + return $this; + } + + public function setGlue($glue) + { + $this->glue = $glue; + + return $this; + } + + public function getGlue() + { + return $this->glue; + } + + /** + * Normalize the header to be a single header with an array of values. + * + * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into + * multiple entries in the header. + * + * @return self + */ + public function normalize() + { + $values = $this->toArray(); + + for ($i = 0, $total = count($values); $i < $total; $i++) { + if (strpos($values[$i], $this->glue) !== false) { + // Explode on glue when the glue is not inside of a comma + foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { + $values[] = trim($v); + } + unset($values[$i]); + } + } + + $this->values = array_values($values); + + return $this; + } + + public function hasValue($searchValue) + { + return in_array($searchValue, $this->toArray()); + } + + public function removeValue($searchValue) + { + $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { + return $value != $searchValue; + })); + + return $this; + } + + public function toArray() + { + return $this->values; + } + + public function count() + { + return count($this->toArray()); + } + + public function getIterator() + { + return new \ArrayIterator($this->toArray()); + } + + public function parseParams() + { + $params = $matches = array(); + $callback = array($this, 'trimHeader'); + + // Normalize the header into a single array and iterate over all values + foreach ($this->normalize()->toArray() as $val) { + $part = array(); + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + continue; + } + $pieces = array_map($callback, $matches[0]); + $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasExactHeader($header) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this->header == $header; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function raw() + { + Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); + return $this->toArray(); + } + + /** + * Trim a header by removing excess spaces and wrapping quotes + * + * @param $str + * + * @return string + */ + protected function trimHeader($str) + { + static $trimmed = "\"' \n\t"; + + return trim($str, $trimmed); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php new file mode 100644 index 0000000..77789e5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php @@ -0,0 +1,121 @@ +directives = null; + } + + public function removeValue($searchValue) + { + parent::removeValue($searchValue); + $this->directives = null; + } + + /** + * Check if a specific cache control directive exists + * + * @param string $param Directive to retrieve + * + * @return bool + */ + public function hasDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]); + } + + /** + * Get a specific cache control directive + * + * @param string $param Directive to retrieve + * + * @return string|bool|null + */ + public function getDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]) ? $directives[$param] : null; + } + + /** + * Add a cache control directive + * + * @param string $param Directive to add + * @param string $value Value to set + * + * @return self + */ + public function addDirective($param, $value) + { + $directives = $this->getDirectives(); + $directives[$param] = $value; + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Remove a cache control directive by name + * + * @param string $param Directive to remove + * + * @return self + */ + public function removeDirective($param) + { + $directives = $this->getDirectives(); + unset($directives[$param]); + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Get an associative array of cache control directives + * + * @return array + */ + public function getDirectives() + { + if ($this->directives === null) { + $this->directives = array(); + foreach ($this->parseParams() as $collection) { + foreach ($collection as $key => $value) { + $this->directives[$key] = $value === '' ? true : $value; + } + } + } + + return $this->directives; + } + + /** + * Updates the header value based on the parsed directives + * + * @param array $directives Array of cache control directives + */ + protected function updateFromDirectives(array $directives) + { + $this->directives = $directives; + $this->values = array(); + + foreach ($directives as $key => $value) { + $this->values[] = $value === true ? $key : "{$key}={$value}"; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php new file mode 100644 index 0000000..8c7f6ae --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php @@ -0,0 +1,108 @@ +headers = $headers; + } + + public function __clone() + { + foreach ($this->headers as &$header) { + $header = clone $header; + } + } + + /** + * Clears the header collection + */ + public function clear() + { + $this->headers = array(); + } + + /** + * Set a header on the collection + * + * @param HeaderInterface $header Header to add + * + * @return self + */ + public function add(HeaderInterface $header) + { + $this->headers[strtolower($header->getName())] = $header; + + return $this; + } + + /** + * Get an array of header objects + * + * @return array + */ + public function getAll() + { + return $this->headers; + } + + /** + * Alias of offsetGet + */ + public function get($key) + { + return $this->offsetGet($key); + } + + public function count() + { + return count($this->headers); + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetGet($offset) + { + $l = strtolower($offset); + + return isset($this->headers[$l]) ? $this->headers[$l] : null; + } + + public function offsetSet($offset, $value) + { + $this->add($value); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + public function toArray() + { + $result = array(); + foreach ($this->headers as $header) { + $result[$header->getName()] = $header->toArray(); + } + + return $result; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php new file mode 100644 index 0000000..0273be5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php @@ -0,0 +1,26 @@ + 'Guzzle\Http\Message\Header\CacheControl', + 'link' => 'Guzzle\Http\Message\Header\Link', + ); + + public function createHeader($header, $value = null) + { + $lowercase = strtolower($header); + + return isset($this->mapping[$lowercase]) + ? new $this->mapping[$lowercase]($header, $value) + : new Header($header, $value); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php new file mode 100644 index 0000000..9457cf6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php @@ -0,0 +1,19 @@ +", "rel=\"{$rel}\""); + + foreach ($params as $k => $v) { + $values[] = "{$k}=\"{$v}\""; + } + + return $this->add(implode('; ', $values)); + } + + /** + * Check if a specific link exists for a given rel attribute + * + * @param string $rel rel value + * + * @return bool + */ + public function hasLink($rel) + { + return $this->getLink($rel) !== null; + } + + /** + * Get a specific link for a given rel attribute + * + * @param string $rel Rel value + * + * @return array|null + */ + public function getLink($rel) + { + foreach ($this->getLinks() as $link) { + if (isset($link['rel']) && $link['rel'] == $rel) { + return $link; + } + } + + return null; + } + + /** + * Get an associative array of links + * + * For example: + * Link: ; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg" + * + * + * var_export($response->getLinks()); + * array( + * array( + * 'url' => 'http:/.../front.jpeg', + * 'rel' => 'back', + * 'type' => 'image/jpeg', + * ) + * ) + * + * + * @return array + */ + public function getLinks() + { + $links = $this->parseParams(); + + foreach ($links as &$link) { + $key = key($link); + unset($link[$key]); + $link['url'] = trim($key, '<> '); + } + + return $links; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php new file mode 100644 index 0000000..62bcd43 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php @@ -0,0 +1,102 @@ +fieldName = $fieldName; + $this->setFilename($filename); + $this->postname = $postname ? $postname : basename($filename); + $this->contentType = $contentType ?: $this->guessContentType(); + } + + public function setFieldName($name) + { + $this->fieldName = $name; + + return $this; + } + + public function getFieldName() + { + return $this->fieldName; + } + + public function setFilename($filename) + { + // Remove leading @ symbol + if (strpos($filename, '@') === 0) { + $filename = substr($filename, 1); + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $this->filename = $filename; + + return $this; + } + + public function setPostname($postname) + { + $this->postname = $postname; + + return $this; + } + + public function getFilename() + { + return $this->filename; + } + + public function getPostname() + { + return $this->postname; + } + + public function setContentType($type) + { + $this->contentType = $type; + + return $this; + } + + public function getContentType() + { + return $this->contentType; + } + + public function getCurlValue() + { + // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax + // See: https://wiki.php.net/rfc/curl-file-upload + if (function_exists('curl_file_create')) { + return curl_file_create($this->filename, $this->contentType, $this->postname); + } + + // Use the old style if using an older version of PHP + $value = "@{$this->filename};filename=" . $this->postname; + if ($this->contentType) { + $value .= ';type=' . $this->contentType; + } + + return $value; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCurlString() + { + Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); + return $this->getCurlValue(); + } + + /** + * Determine the Content-Type of the file + */ + protected function guessContentType() + { + return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php new file mode 100644 index 0000000..7f0779d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php @@ -0,0 +1,83 @@ +method = strtoupper($method); + $this->curlOptions = new Collection(); + $this->setUrl($url); + + if ($headers) { + // Special handling for multi-value headers + foreach ($headers as $key => $value) { + // Deal with collisions with Host and Authorization + if ($key == 'host' || $key == 'Host') { + $this->setHeader($key, $value); + } elseif ($value instanceof HeaderInterface) { + $this->addHeader($key, $value); + } else { + foreach ((array) $value as $v) { + $this->addHeader($key, $v); + } + } + } + } + + $this->setState(self::STATE_NEW); + } + + public function __clone() + { + if ($this->eventDispatcher) { + $this->eventDispatcher = clone $this->eventDispatcher; + } + $this->curlOptions = clone $this->curlOptions; + $this->params = clone $this->params; + $this->url = clone $this->url; + $this->response = $this->responseBody = null; + $this->headers = clone $this->headers; + + $this->setState(RequestInterface::STATE_NEW); + $this->dispatch('request.clone', array('request' => $this)); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + return $this->getRawHeaders() . "\r\n\r\n"; + } + + /** + * Default method that will throw exceptions if an unsuccessful response is received. + * + * @param Event $event Received + * @throws BadResponseException if the response is not successful + */ + public static function onRequestError(Event $event) + { + $e = BadResponseException::factory($event['request'], $event['response']); + $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); + throw $e; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getClient() + { + return $this->client; + } + + public function getRawHeaders() + { + $protocolVersion = $this->protocolVersion ?: '1.1'; + + return trim($this->method . ' ' . $this->getResource()) . ' ' + . strtoupper(str_replace('https', 'http', $this->url->getScheme())) + . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); + } + + public function setUrl($url) + { + if ($url instanceof Url) { + $this->url = $url; + } else { + $this->url = Url::factory($url); + } + + // Update the port and host header + $this->setPort($this->url->getPort()); + + if ($this->url->getUsername() || $this->url->getPassword()) { + $this->setAuth($this->url->getUsername(), $this->url->getPassword()); + // Remove the auth info from the URL + $this->url->setUsername(null); + $this->url->setPassword(null); + } + + return $this; + } + + public function send() + { + if (!$this->client) { + throw new RuntimeException('A client must be set on the request'); + } + + return $this->client->send($this); + } + + public function getResponse() + { + return $this->response; + } + + public function getQuery($asString = false) + { + return $asString + ? (string) $this->url->getQuery() + : $this->url->getQuery(); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->setPort($this->url->getPort()); + + return $this; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function setProtocolVersion($protocol) + { + $this->protocolVersion = $protocol; + + return $this; + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + + // Include the port in the Host header if it is not the default port for the scheme of the URL + $scheme = $this->url->getScheme(); + if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); + } else { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); + } + + return $this; + } + + public function getUsername() + { + return $this->username; + } + + public function getPassword() + { + return $this->password; + } + + public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) + { + static $authMap = array( + 'basic' => CURLAUTH_BASIC, + 'digest' => CURLAUTH_DIGEST, + 'ntlm' => CURLAUTH_NTLM, + 'any' => CURLAUTH_ANY + ); + + // If we got false or null, disable authentication + if (!$user) { + $this->password = $this->username = null; + $this->removeHeader('Authorization'); + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + return $this; + } + + if (!is_numeric($scheme)) { + $scheme = strtolower($scheme); + if (!isset($authMap[$scheme])) { + throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); + } + $scheme = $authMap[$scheme]; + } + + $this->username = $user; + $this->password = $password; + + // Bypass CURL when using basic auth to promote connection reuse + if ($scheme == CURLAUTH_BASIC) { + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); + } else { + $this->getCurlOptions() + ->set(CURLOPT_HTTPAUTH, $scheme) + ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + return $this; + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getUrl($asObject = false) + { + return $asObject ? clone $this->url : (string) $this->url; + } + + public function getState() + { + return $this->state; + } + + public function setState($state, array $context = array()) + { + $oldState = $this->state; + $this->state = $state; + + switch ($state) { + case self::STATE_NEW: + $this->response = null; + break; + case self::STATE_TRANSFER: + if ($oldState !== $state) { + // Fix Content-Length and Transfer-Encoding collisions + if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { + $this->removeHeader('Transfer-Encoding'); + } + $this->dispatch('request.before_send', array('request' => $this)); + } + break; + case self::STATE_COMPLETE: + if ($oldState !== $state) { + $this->processResponse($context); + $this->responseBody = null; + } + break; + case self::STATE_ERROR: + if (isset($context['exception'])) { + $this->dispatch('request.exception', array( + 'request' => $this, + 'response' => isset($context['response']) ? $context['response'] : $this->response, + 'exception' => isset($context['exception']) ? $context['exception'] : null + )); + } + } + + return $this->state; + } + + public function getCurlOptions() + { + return $this->curlOptions; + } + + public function startResponse(Response $response) + { + $this->state = self::STATE_TRANSFER; + $response->setEffectiveUrl((string) $this->getUrl()); + $this->response = $response; + + return $this; + } + + public function setResponse(Response $response, $queued = false) + { + $response->setEffectiveUrl((string) $this->url); + + if ($queued) { + $ed = $this->getEventDispatcher(); + $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { + $e['request']->setResponse($response); + $ed->removeListener('request.before_send', $f); + }, -9999); + } else { + $this->response = $response; + // If a specific response body is specified, then use it instead of the response's body + if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { + $this->getResponseBody()->write((string) $this->response->getBody()); + } else { + $this->responseBody = $this->response->getBody(); + } + $this->setState(self::STATE_COMPLETE); + } + + return $this; + } + + public function setResponseBody($body) + { + // Attempt to open a file for writing if a string was passed + if (is_string($body)) { + // @codeCoverageIgnoreStart + if (!($body = fopen($body, 'w+'))) { + throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); + } + // @codeCoverageIgnoreEnd + } + + $this->responseBody = EntityBody::factory($body); + + return $this; + } + + public function getResponseBody() + { + if ($this->responseBody === null) { + $this->responseBody = EntityBody::factory()->setCustomData('default', true); + } + + return $this->responseBody; + } + + /** + * Determine if the response body is repeatable (readable + seekable) + * + * @return bool + * @deprecated Use getResponseBody()->isSeekable() + * @codeCoverageIgnore + */ + public function isResponseBodyRepeatable() + { + Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); + return !$this->responseBody ? true : $this->responseBody->isRepeatable(); + } + + public function getCookies() + { + if ($cookie = $this->getHeader('Cookie')) { + $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); + return $data['cookies']; + } + + return array(); + } + + public function getCookie($name) + { + $cookies = $this->getCookies(); + + return isset($cookies[$name]) ? $cookies[$name] : null; + } + + public function addCookie($name, $value) + { + if (!$this->hasHeader('Cookie')) { + $this->setHeader('Cookie', "{$name}={$value}"); + } else { + $this->getHeader('Cookie')->add("{$name}={$value}"); + } + + // Always use semicolons to separate multiple cookie headers + $this->getHeader('Cookie')->setGlue(';'); + + return $this; + } + + public function removeCookie($name) + { + if ($cookie = $this->getHeader('Cookie')) { + foreach ($cookie as $cookieValue) { + if (strpos($cookieValue, $name . '=') === 0) { + $cookie->removeValue($cookieValue); + } + } + } + + return $this; + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->setEventDispatcher(new EventDispatcher()); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + $context['request'] = $this; + + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + /** + * Get an array containing the request and response for event notifications + * + * @return array + */ + protected function getEventArray() + { + return array( + 'request' => $this, + 'response' => $this->response + ); + } + + /** + * Process a received response + * + * @param array $context Contextual information + * @throws RequestException|BadResponseException on unsuccessful responses + */ + protected function processResponse(array $context = array()) + { + if (!$this->response) { + // If no response, then processResponse shouldn't have been called + $e = new RequestException('Error completing request'); + $e->setRequest($this); + throw $e; + } + + $this->state = self::STATE_COMPLETE; + + // A request was sent, but we don't know if we'll send more or if the final response will be successful + $this->dispatch('request.sent', $this->getEventArray() + $context); + + // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) + if ($this->state == RequestInterface::STATE_COMPLETE) { + + // The request completed, so the HTTP transaction is complete + $this->dispatch('request.complete', $this->getEventArray()); + + // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by + // modifying the Event object in your listeners or calling setResponse() on the request + if ($this->response->isError()) { + $event = new Event($this->getEventArray()); + $this->getEventDispatcher()->dispatch('request.error', $event); + // Allow events of request.error to quietly change the response + if ($event['response'] !== $this->response) { + $this->response = $event['response']; + } + } + + // If a successful response was received, dispatch an event + if ($this->response->isSuccessful()) { + $this->dispatch('request.success', $this->getEventArray()); + } + } + } + + /** + * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy + * @codeCoverageIgnore + */ + public function canCache() + { + Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); + if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { + $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); + return $canCache->canCacheRequest($this); + } else { + return false; + } + } + + /** + * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) + * @codeCoverageIgnore + */ + public function setIsRedirect($isRedirect) + { + $this->isRedirect = $isRedirect; + + return $this; + } + + /** + * @deprecated Use the history plugin + * @codeCoverageIgnore + */ + public function isRedirect() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); + return $this->isRedirect; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php new file mode 100644 index 0000000..ba00a76 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php @@ -0,0 +1,359 @@ +methods = array_flip(get_class_methods(__CLASS__)); + } + + public function fromMessage($message) + { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); + + if (!$parsed) { + return false; + } + + $request = $this->fromParts($parsed['method'], $parsed['request_url'], + $parsed['headers'], $parsed['body'], $parsed['protocol'], + $parsed['version']); + + // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST + // requests. This factory method should accurately reflect the message, so here we are removing the Expect + // header if one was not supplied in the message. + if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { + $request->removeHeader('Expect'); + } + + return $request; + } + + public function fromParts( + $method, + array $urlParts, + $headers = null, + $body = null, + $protocol = 'HTTP', + $protocolVersion = '1.1' + ) { + return $this->create($method, Url::buildUrl($urlParts), $headers, $body) + ->setProtocolVersion($protocolVersion); + } + + public function create($method, $url, $headers = null, $body = null, array $options = array()) + { + $method = strtoupper($method); + + if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE') { + // Handle non-entity-enclosing request methods + $request = new $this->requestClass($method, $url, $headers); + if ($body) { + // The body is where the response body will be stored + $type = gettype($body); + if ($type == 'string' || $type == 'resource' || $type == 'object') { + $request->setResponseBody($body); + } + } + } else { + // Create an entity enclosing request by default + $request = new $this->entityEnclosingRequestClass($method, $url, $headers); + if ($body || $body === '0') { + // Add POST fields and files to an entity enclosing request if an array is used + if (is_array($body) || $body instanceof Collection) { + // Normalize PHP style cURL uploads with a leading '@' symbol + foreach ($body as $key => $value) { + if (is_string($value) && substr($value, 0, 1) == '@') { + $request->addPostFile($key, $value); + unset($body[$key]); + } + } + // Add the fields if they are still present and not all files + $request->addPostFields($body); + } else { + // Add a raw entity body body to the request + $request->setBody($body, (string) $request->getHeader('Content-Type')); + if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { + $request->removeHeader('Content-Length'); + } + } + } + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Clone a request while changing the method. Emulates the behavior of + * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. + * + * @param RequestInterface $request Request to clone + * @param string $method Method to set + * + * @return RequestInterface + */ + public function cloneRequestWithMethod(RequestInterface $request, $method) + { + // Create the request with the same client if possible + if ($request->getClient()) { + $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); + } else { + $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); + } + + $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); + $cloned->setEventDispatcher(clone $request->getEventDispatcher()); + // Ensure that that the Content-Length header is not copied if changing to GET or HEAD + if (!($cloned instanceof EntityEnclosingRequestInterface)) { + $cloned->removeHeader('Content-Length'); + } elseif ($request instanceof EntityEnclosingRequestInterface) { + $cloned->setBody($request->getBody()); + } + $cloned->getParams()->replace($request->getParams()->toArray()); + $cloned->dispatch('request.clone', array('request' => $cloned)); + + return $cloned; + } + + public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) + { + // Iterate over each key value pair and attempt to apply a config using function visitors + foreach ($options as $key => $value) { + $method = "visit_{$key}"; + if (isset($this->methods[$method])) { + $this->{$method}($request, $value, $flags); + } + } + } + + protected function visit_headers(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('headers value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge headers in but do not overwrite existing values + foreach ($value as $key => $header) { + if (!$request->hasHeader($key)) { + $request->setHeader($key, $header); + } + } + } else { + $request->addHeaders($value); + } + } + + protected function visit_body(RequestInterface $request, $value, $flags) + { + if ($request instanceof EntityEnclosingRequestInterface) { + $request->setBody($value); + } else { + throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); + } + } + + protected function visit_allow_redirects(RequestInterface $request, $value, $flags) + { + if ($value === false) { + $request->getParams()->set(RedirectPlugin::DISABLE, true); + } + } + + protected function visit_auth(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('auth value must be an array'); + } + + $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); + } + + protected function visit_query(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('query value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge query string values in but do not overwrite existing values + $query = $request->getQuery(); + $query->overwriteWith(array_diff_key($value, $query->toArray())); + } else { + $request->getQuery()->overwriteWith($value); + } + } + + protected function visit_cookies(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('cookies value must be an array'); + } + + foreach ($value as $name => $v) { + $request->addCookie($name, $v); + } + } + + protected function visit_events(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('events value must be an array'); + } + + foreach ($value as $name => $method) { + if (is_array($method)) { + $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); + } else { + $request->getEventDispatcher()->addListener($name, $method); + } + } + } + + protected function visit_plugins(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('plugins value must be an array'); + } + + foreach ($value as $plugin) { + $request->addSubscriber($plugin); + } + } + + protected function visit_exceptions(RequestInterface $request, $value, $flags) + { + if ($value === false || $value === 0) { + $dispatcher = $request->getEventDispatcher(); + foreach ($dispatcher->getListeners('request.error') as $listener) { + if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { + $dispatcher->removeListener('request.error', $listener); + break; + } + } + } + } + + protected function visit_save_to(RequestInterface $request, $value, $flags) + { + $request->setResponseBody($value); + } + + protected function visit_params(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('params value must be an array'); + } + + $request->getParams()->overwriteWith($value); + } + + protected function visit_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_TIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value); + } + } + + protected function visit_connect_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value); + } + } + + protected function visit_debug(RequestInterface $request, $value, $flags) + { + if ($value) { + $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); + } + } + + protected function visit_verify(RequestInterface $request, $value, $flags) + { + $curl = $request->getCurlOptions(); + if ($value === true || is_string($value)) { + $curl[CURLOPT_SSL_VERIFYHOST] = 2; + $curl[CURLOPT_SSL_VERIFYPEER] = true; + if ($value !== true) { + $curl[CURLOPT_CAINFO] = $value; + } + } elseif ($value === false) { + unset($curl[CURLOPT_CAINFO]); + $curl[CURLOPT_SSL_VERIFYHOST] = 0; + $curl[CURLOPT_SSL_VERIFYPEER] = false; + } + } + + protected function visit_proxy(RequestInterface $request, $value, $flags) + { + $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); + } + + protected function visit_cert(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); + } + } + + protected function visit_ssl_key(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php new file mode 100644 index 0000000..6088f10 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php @@ -0,0 +1,105 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + /** @var EntityBodyInterface The response body */ + protected $body; + + /** @var string The reason phrase of the response (human readable code) */ + protected $reasonPhrase; + + /** @var string The status code of the response */ + protected $statusCode; + + /** @var array Information about the request */ + protected $info = array(); + + /** @var string The effective URL that returned this response */ + protected $effectiveUrl; + + /** @var array Cacheable response codes (see RFC 2616:13.4) */ + protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); + + /** + * Create a new Response based on a raw response message + * + * @param string $message Response message + * + * @return self|bool Returns false on error + */ + public static function fromMessage($message) + { + $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); + if (!$data) { + return false; + } + + $response = new static($data['code'], $data['headers'], $data['body']); + $response->setProtocol($data['protocol'], $data['version']) + ->setStatus($data['code'], $data['reason_phrase']); + + // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) + $contentLength = (string) $response->getHeader('Content-Length'); + $actualLength = strlen($data['body']); + if (strlen($data['body']) > 0 && $contentLength != $actualLength) { + $response->setHeader('Content-Length', $actualLength); + } + + return $response; + } + + /** + * Construct the response + * + * @param string $statusCode The response status code (e.g. 200, 404, etc) + * @param ToArrayInterface|array $headers The response headers + * @param string|resource|EntityBodyInterface $body The body of the response + * + * @throws BadResponseException if an invalid response code is given + */ + public function __construct($statusCode, $headers = null, $body = null) + { + parent::__construct(); + $this->setStatus($statusCode); + $this->body = EntityBody::factory($body !== null ? $body : ''); + + if ($headers) { + if (is_array($headers)) { + $this->setHeaders($headers); + } elseif ($headers instanceof ToArrayInterface) { + $this->setHeaders($headers->toArray()); + } else { + throw new BadResponseException('Invalid headers argument received'); + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + public function serialize() + { + return json_encode(array( + 'status' => $this->statusCode, + 'body' => (string) $this->body, + 'headers' => $this->headers->toArray() + )); + } + + public function unserialize($serialize) + { + $data = json_decode($serialize, true); + $this->__construct($data['status'], $data['headers'], $data['body']); + } + + /** + * Get the response entity body + * + * @param bool $asString Set to TRUE to return a string of the body rather than a full body object + * + * @return EntityBodyInterface|string + */ + public function getBody($asString = false) + { + return $asString ? (string) $this->body : $this->body; + } + + /** + * Set the response entity body + * + * @param EntityBodyInterface|string $body Body to set + * + * @return self + */ + public function setBody($body) + { + $this->body = EntityBody::factory($body); + + return $this; + } + + /** + * Set the protocol and protocol version of the response + * + * @param string $protocol Response protocol + * @param string $version Protocol version + * + * @return self + */ + public function setProtocol($protocol, $version) + { + $this->protocol = $protocol; + $this->protocolVersion = $version; + + return $this; + } + + /** + * Get the protocol used for the response (e.g. HTTP) + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * Get the HTTP protocol version + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * Get a cURL transfer information + * + * @param string $key A single statistic to check + * + * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key + * is set and not found + * @link http://www.php.net/manual/en/function.curl-getinfo.php + */ + public function getInfo($key = null) + { + if ($key === null) { + return $this->info; + } elseif (array_key_exists($key, $this->info)) { + return $this->info[$key]; + } else { + return null; + } + } + + /** + * Set the transfer information + * + * @param array $info Array of cURL transfer stats + * + * @return self + */ + public function setInfo(array $info) + { + $this->info = $info; + + return $this; + } + + /** + * Set the response status + * + * @param int $statusCode Response status code to set + * @param string $reasonPhrase Response reason phrase + * + * @return self + * @throws BadResponseException when an invalid response code is received + */ + public function setStatus($statusCode, $reasonPhrase = '') + { + $this->statusCode = (int) $statusCode; + + if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } else { + $this->reasonPhrase = $reasonPhrase; + } + + return $this; + } + + /** + * Get the response status code + * + * @return integer + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Get the entire response as a string + * + * @return string + */ + public function getMessage() + { + $message = $this->getRawHeaders(); + + // Only include the body in the message if the size is < 2MB + $size = $this->body->getSize(); + if ($size < 2097152) { + $message .= (string) $this->body; + } + + return $message; + } + + /** + * Get the the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; + $lines = $this->getHeaderLines(); + if (!empty($lines)) { + $headers .= implode("\r\n", $lines) . "\r\n"; + } + + return $headers . "\r\n"; + } + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Get the Accept-Ranges HTTP header + * + * @return string Returns what partial content range types this server supports. + */ + public function getAcceptRanges() + { + return (string) $this->getHeader('Accept-Ranges'); + } + + /** + * Calculate the age of the response + * + * @return integer + */ + public function calculateAge() + { + $age = $this->getHeader('Age'); + + if ($age === null && $this->getDate()) { + $age = time() - strtotime($this->getDate()); + } + + return $age === null ? null : (int) (string) $age; + } + + /** + * Get the Age HTTP header + * + * @return integer|null Returns the age the object has been in a proxy cache in seconds. + */ + public function getAge() + { + return (string) $this->getHeader('Age'); + } + + /** + * Get the Allow HTTP header + * + * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. + */ + public function getAllow() + { + return (string) $this->getHeader('Allow'); + } + + /** + * Check if an HTTP method is allowed by checking the Allow response header + * + * @param string $method Method to check + * + * @return bool + */ + public function isMethodAllowed($method) + { + $allow = $this->getHeader('Allow'); + if ($allow) { + foreach (explode(',', $allow) as $allowable) { + if (!strcasecmp(trim($allowable), $method)) { + return true; + } + } + } + + return false; + } + + /** + * Get the Cache-Control HTTP header + * + * @return string + */ + public function getCacheControl() + { + return (string) $this->getHeader('Cache-Control'); + } + + /** + * Get the Connection HTTP header + * + * @return string + */ + public function getConnection() + { + return (string) $this->getHeader('Connection'); + } + + /** + * Get the Content-Encoding HTTP header + * + * @return string|null + */ + public function getContentEncoding() + { + return (string) $this->getHeader('Content-Encoding'); + } + + /** + * Get the Content-Language HTTP header + * + * @return string|null Returns the language the content is in. + */ + public function getContentLanguage() + { + return (string) $this->getHeader('Content-Language'); + } + + /** + * Get the Content-Length HTTP header + * + * @return integer Returns the length of the response body in bytes + */ + public function getContentLength() + { + return (int) (string) $this->getHeader('Content-Length'); + } + + /** + * Get the Content-Location HTTP header + * + * @return string|null Returns an alternate location for the returned data (e.g /index.htm) + */ + public function getContentLocation() + { + return (string) $this->getHeader('Content-Location'); + } + + /** + * Get the Content-Disposition HTTP header + * + * @return string|null Returns the Content-Disposition header + */ + public function getContentDisposition() + { + return (string) $this->getHeader('Content-Disposition'); + } + + /** + * Get the Content-MD5 HTTP header + * + * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. + */ + public function getContentMd5() + { + return (string) $this->getHeader('Content-MD5'); + } + + /** + * Get the Content-Range HTTP header + * + * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). + */ + public function getContentRange() + { + return (string) $this->getHeader('Content-Range'); + } + + /** + * Get the Content-Type HTTP header + * + * @return string Returns the mime type of this content. + */ + public function getContentType() + { + return (string) $this->getHeader('Content-Type'); + } + + /** + * Checks if the Content-Type is of a certain type. This is useful if the + * Content-Type header contains charset information and you need to know if + * the Content-Type matches a particular type. + * + * @param string $type Content type to check against + * + * @return bool + */ + public function isContentType($type) + { + return stripos($this->getHeader('Content-Type'), $type) !== false; + } + + /** + * Get the Date HTTP header + * + * @return string|null Returns the date and time that the message was sent. + */ + public function getDate() + { + return (string) $this->getHeader('Date'); + } + + /** + * Get the ETag HTTP header + * + * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. + */ + public function getEtag() + { + return (string) $this->getHeader('ETag'); + } + + /** + * Get the Expires HTTP header + * + * @return string|null Returns the date/time after which the response is considered stale. + */ + public function getExpires() + { + return (string) $this->getHeader('Expires'); + } + + /** + * Get the Last-Modified HTTP header + * + * @return string|null Returns the last modified date for the requested object, in RFC 2822 format + * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) + */ + public function getLastModified() + { + return (string) $this->getHeader('Last-Modified'); + } + + /** + * Get the Location HTTP header + * + * @return string|null Used in redirection, or when a new resource has been created. + */ + public function getLocation() + { + return (string) $this->getHeader('Location'); + } + + /** + * Get the Pragma HTTP header + * + * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along + * the request-response chain. + */ + public function getPragma() + { + return (string) $this->getHeader('Pragma'); + } + + /** + * Get the Proxy-Authenticate HTTP header + * + * @return string|null Authentication to access the proxy (e.g. Basic) + */ + public function getProxyAuthenticate() + { + return (string) $this->getHeader('Proxy-Authenticate'); + } + + /** + * Get the Retry-After HTTP header + * + * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a + * specified period of time. + */ + public function getRetryAfter() + { + return (string) $this->getHeader('Retry-After'); + } + + /** + * Get the Server HTTP header + * + * @return string|null A name for the server + */ + public function getServer() + { + return (string) $this->getHeader('Server'); + } + + /** + * Get the Set-Cookie HTTP header + * + * @return string|null An HTTP cookie. + */ + public function getSetCookie() + { + return (string) $this->getHeader('Set-Cookie'); + } + + /** + * Get the Trailer HTTP header + * + * @return string|null The Trailer general field value indicates that the given set of header fields is present in + * the trailer of a message encoded with chunked transfer-coding. + */ + public function getTrailer() + { + return (string) $this->getHeader('Trailer'); + } + + /** + * Get the Transfer-Encoding HTTP header + * + * @return string|null The form of encoding used to safely transfer the entity to the user + */ + public function getTransferEncoding() + { + return (string) $this->getHeader('Transfer-Encoding'); + } + + /** + * Get the Vary HTTP header + * + * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached + * response can be used rather than requesting a fresh one from the origin server. + */ + public function getVary() + { + return (string) $this->getHeader('Vary'); + } + + /** + * Get the Via HTTP header + * + * @return string|null Informs the client of proxies through which the response was sent. + */ + public function getVia() + { + return (string) $this->getHeader('Via'); + } + + /** + * Get the Warning HTTP header + * + * @return string|null A general warning about possible problems with the entity body + */ + public function getWarning() + { + return (string) $this->getHeader('Warning'); + } + + /** + * Get the WWW-Authenticate HTTP header + * + * @return string|null Indicates the authentication scheme that should be used to access the requested entity + */ + public function getWwwAuthenticate() + { + return (string) $this->getHeader('WWW-Authenticate'); + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return bool + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + public function isError() + { + return $this->isClientError() || $this->isServerError(); + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return bool + */ + public function isInformational() + { + return $this->statusCode < 200; + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return bool + */ + public function isRedirect() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return bool + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Checks if HTTP Status code is Successful (2xx | 304) + * + * @return bool + */ + public function isSuccessful() + { + return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; + } + + /** + * Check if the response can be cached based on the response headers + * + * @return bool Returns TRUE if the response can be cached or false if not + */ + public function canCache() + { + // Check if the response is cacheable based on the code + if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { + return false; + } + + // Make sure a valid body was returned and can be cached + if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) + && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { + return false; + } + + // Never cache no-store resources (this is a private cache, so private + // can be cached) + if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); + } + + /** + * Gets the number of seconds from the current time in which this response is still considered fresh + * + * @return int|null Returns the number of seconds + */ + public function getMaxAge() + { + if ($header = $this->getHeader('Cache-Control')) { + // s-max-age, then max-age, then Expires + if ($age = $header->getDirective('s-maxage')) { + return $age; + } + if ($age = $header->getDirective('max-age')) { + return $age; + } + } + + if ($this->getHeader('Expires')) { + return strtotime($this->getExpires()) - time(); + } + + return null; + } + + /** + * Check if the response is considered fresh. + * + * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the + * response. + * + * @return bool|null + */ + public function isFresh() + { + $fresh = $this->getFreshness(); + + return $fresh === null ? null : $fresh >= 0; + } + + /** + * Check if the response can be validated against the origin server using a conditional GET request. + * + * @return bool + */ + public function canValidate() + { + return $this->getEtag() || $this->getLastModified(); + } + + /** + * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the + * age of the response (max-age - age). + * + * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. + * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL + * result means that no freshness information is available. + * + * @return int + */ + public function getFreshness() + { + $maxAge = $this->getMaxAge(); + $age = $this->calculateAge(); + + return $maxAge && $age ? ($maxAge - $age) : null; + } + + /** + * Parse the JSON response body and return an array + * + * @return array|string|int|bool|float + * @throws RuntimeException if the response body is not in JSON format + */ + public function json() + { + $data = json_decode((string) $this->body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); + } + + return $data === null ? array() : $data; + } + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * @return \SimpleXMLElement + * @throws RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + */ + public function xml() + { + $errorMessage = null; + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + try { + $xml = new \SimpleXMLElement((string) $this->body ?: '', LIBXML_NONET); + if ($error = libxml_get_last_error()) { + $errorMessage = $error->message; + } + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + if ($errorMessage) { + throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); + } + + return $xml; + } + + /** + * Get the redirect count of this response + * + * @return int + */ + public function getRedirectCount() + { + return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); + } + + /** + * Set the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @param string $url The effective URL + * + * @return self + */ + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + + return $this; + } + + /** + * Get the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @return string + */ + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getPreviousResponse() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); + return null; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setRequest($request) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getRequest() + { + Version::warn(__METHOD__ . ' is deprecated'); + return null; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php new file mode 100644 index 0000000..d71586a --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php @@ -0,0 +1,962 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php new file mode 100644 index 0000000..4b4e49d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php @@ -0,0 +1,20 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); + } else { + return array($key => implode(',', $value)); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php new file mode 100644 index 0000000..1bf1730 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php @@ -0,0 +1,22 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); + } else { + return array($key => $value); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php new file mode 100644 index 0000000..133ea2b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php @@ -0,0 +1,27 @@ + $v) { + $k = "{$key}[{$k}]"; + if (is_array($v)) { + $ret = array_merge($ret, self::aggregate($k, $v, $query)); + } else { + $ret[$query->encodeValue($k)] = $query->encodeValue($v); + } + } + + return $ret; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php new file mode 100644 index 0000000..72bee62 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php @@ -0,0 +1,22 @@ +add($key, $value); + $foundDuplicates = true; + } elseif ($paramIsPhpStyleArray) { + $q[$key] = array($value); + } else { + $q[$key] = $value; + } + } else { + // Uses false by default to represent keys with no trailing "=" sign. + $q->add($key, false); + } + } + + // Use the duplicate aggregator if duplicates were found and not using PHP style arrays + if ($foundDuplicates && !$foundPhpStyle) { + $q->setAggregator(new DuplicateAggregator()); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + * @throws RuntimeException + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + $queryList = array(); + foreach ($this->prepareData($this->data) as $name => $value) { + $queryList[] = $this->convertKvp($name, $value); + } + + return implode($this->fieldSeparator, $queryList); + } + + /** + * Get the query string field separator + * + * @return string + */ + public function getFieldSeparator() + { + return $this->fieldSeparator; + } + + /** + * Get the query string value separator + * + * @return string + */ + public function getValueSeparator() + { + return $this->valueSeparator; + } + + /** + * Returns the type of URL encoding used by the query string + * + * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" + * + * @return bool|string + */ + public function getUrlEncoding() + { + return $this->urlEncode; + } + + /** + * Returns true or false if using URL encoding + * + * @return bool + */ + public function isUrlEncoding() + { + return $this->urlEncode !== false; + } + + /** + * Provide a function for combining multi-valued query string parameters into a single or multiple fields + * + * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting + * deeply nested query string variables into a flattened array. + * Pass null to use the default PHP style aggregator. For legacy + * reasons, this function accepts a callable that must accepts a + * $key, $value, and query object. + * @return self + * @see \Guzzle\Http\QueryString::aggregateUsingComma() + */ + public function setAggregator(QueryAggregatorInterface $aggregator = null) + { + // Use the default aggregator if none was set + if (!$aggregator) { + if (!self::$defaultAggregator) { + self::$defaultAggregator = new PhpAggregator(); + } + $aggregator = self::$defaultAggregator; + } + + $this->aggregator = $aggregator; + + return $this; + } + + /** + * Set whether or not field names and values should be rawurlencoded + * + * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or + * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) + * @return self + */ + public function useUrlEncoding($encode) + { + $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; + + return $this; + } + + /** + * Set the query string separator + * + * @param string $separator The query string separator that will separate fields + * + * @return self + */ + public function setFieldSeparator($separator) + { + $this->fieldSeparator = $separator; + + return $this; + } + + /** + * Set the query string value separator + * + * @param string $separator The query string separator that will separate values from fields + * + * @return self + */ + public function setValueSeparator($separator) + { + $this->valueSeparator = $separator; + + return $this; + } + + /** + * Returns an array of url encoded field names and values + * + * @return array + */ + public function urlEncode() + { + return $this->prepareData($this->data); + } + + /** + * URL encodes a value based on the url encoding type of the query string object + * + * @param string $value Value to encode + * + * @return string + */ + public function encodeValue($value) + { + if ($this->urlEncode == self::RFC_3986) { + return rawurlencode($value); + } elseif ($this->urlEncode == self::FORM_URLENCODED) { + return urlencode($value); + } else { + return (string) $value; + } + } + + /** + * Url encode parameter data and convert nested query strings into a flattened hash. + * + * @param array $data The data to encode + * + * @return array Returns an array of encoded values and keys + */ + protected function prepareData(array $data) + { + // If no aggregator is present then set the default + if (!$this->aggregator) { + $this->setAggregator(null); + } + + $temp = array(); + foreach ($data as $key => $value) { + if ($value === false || $value === null) { + // False and null will not include the "=". Use an empty string to include the "=". + $temp[$this->encodeValue($key)] = $value; + } elseif (is_array($value)) { + $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); + } else { + $temp[$this->encodeValue($key)] = $this->encodeValue($value); + } + } + + return $temp; + } + + /** + * Converts a key value pair that can contain strings, nulls, false, or arrays + * into a single string. + * + * @param string $name Name of the field + * @param mixed $value Value of the field + * @return string + */ + private function convertKvp($name, $value) + { + if ($value === self::BLANK || $value === null || $value === false) { + return $name; + } elseif (!is_array($value)) { + return $name . $this->valueSeparator . $value; + } + + $result = ''; + foreach ($value as $v) { + $result .= $this->convertKvp($name, $v) . $this->fieldSeparator; + } + + return rtrim($result, $this->fieldSeparator); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php new file mode 100644 index 0000000..ef28273 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php @@ -0,0 +1,122 @@ +setLimit($limit)->setOffset($offset); + } + + /** + * Returns only a subset of the decorated entity body when cast as a string + * {@inheritdoc} + */ + public function __toString() + { + if (!$this->body->isReadable() || + (!$this->body->isSeekable() && $this->body->isConsumed()) + ) { + return ''; + } + + $originalPos = $this->body->ftell(); + $this->body->seek($this->offset); + $data = ''; + while (!$this->feof()) { + $data .= $this->read(1048576); + } + $this->body->seek($originalPos); + + return (string) $data ?: ''; + } + + public function isConsumed() + { + return $this->body->isConsumed() || + ($this->body->ftell() >= $this->offset + $this->limit); + } + + /** + * Returns the Content-Length of the limited subset of data + * {@inheritdoc} + */ + public function getContentLength() + { + $length = $this->body->getContentLength(); + + return $length === false + ? $this->limit + : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); + } + + /** + * Allow for a bounded seek on the read limited entity body + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + return $whence === SEEK_SET + ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) + : false; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + */ + public function setOffset($offset) + { + $this->body->seek($offset); + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the stream + * + * @param int $limit Total number of bytes to allow to be read from the stream + * + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + // Check if the current position is less than the total allowed bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->body->ftell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte limit is not exceeded + return $this->body->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php new file mode 100644 index 0000000..1a824b8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php @@ -0,0 +1,250 @@ + array('onRequestSent', 100), + 'request.clone' => 'cleanupRequest', + 'request.before_send' => 'cleanupRequest' + ); + } + + /** + * Clean up the parameters of a request when it is cloned + * + * @param Event $event Event emitted + */ + public function cleanupRequest(Event $event) + { + $params = $event['request']->getParams(); + unset($params[self::REDIRECT_COUNT]); + unset($params[self::PARENT_REQUEST]); + } + + /** + * Called when a request receives a redirect response + * + * @param Event $event Event emitted + */ + public function onRequestSent(Event $event) + { + $response = $event['response']; + $request = $event['request']; + + // Only act on redirect requests with Location headers + if (!$response || $request->getParams()->get(self::DISABLE)) { + return; + } + + // Trace the original request based on parameter history + $original = $this->getOriginalRequest($request); + + // Terminating condition to set the effective response on the original request + if (!$response->isRedirect() || !$response->hasHeader('Location')) { + if ($request !== $original) { + // This is a terminating redirect response, so set it on the original request + $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); + $original->setResponse($response); + $response->setEffectiveUrl($request->getUrl()); + } + return; + } + + $this->sendRedirectRequest($original, $request, $response); + } + + /** + * Get the original request that initiated a series of redirects + * + * @param RequestInterface $request Request to get the original request from + * + * @return RequestInterface + */ + protected function getOriginalRequest(RequestInterface $request) + { + $original = $request; + // The number of redirects is held on the original request, so determine which request that is + while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { + $original = $parent; + } + + return $original; + } + + /** + * Create a redirect request for a specific request object + * + * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do + * (e.g. redirect POST with GET). + * + * @param RequestInterface $request Request being redirected + * @param RequestInterface $original Original request + * @param int $statusCode Status code of the redirect + * @param string $location Location header of the redirect + * + * @return RequestInterface Returns a new redirect request + * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot + */ + protected function createRedirectRequest( + RequestInterface $request, + $statusCode, + $location, + RequestInterface $original + ) { + $redirectRequest = null; + $strict = $original->getParams()->get(self::STRICT_REDIRECTS); + + // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC + // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. + if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) { + $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); + } else { + $redirectRequest = clone $request; + } + + $redirectRequest->setIsRedirect(true); + // Always use the same response body when redirecting + $redirectRequest->setResponseBody($request->getResponseBody()); + + $location = Url::factory($location); + // If the location is not absolute, then combine it with the original URL + if (!$location->isAbsolute()) { + $originalUrl = $redirectRequest->getUrl(true); + // Remove query string parameters and just take what is present on the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine((string) $location, true); + } + + $redirectRequest->setUrl($location); + + // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) + $redirectRequest->getEventDispatcher()->addListener( + 'request.before_send', + $func = function ($e) use (&$func, $request, $redirectRequest) { + $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); + $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); + } + ); + + // Rewind the entity body of the request if needed + if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { + $body = $redirectRequest->getBody(); + // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails + if ($body->ftell() && !$body->rewind()) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' + . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' + . 'entity body of the request using setRewindFunction().' + ); + } + } + + return $redirectRequest; + } + + /** + * Prepare the request for redirection and enforce the maximum number of allowed redirects per client + * + * @param RequestInterface $original Original request + * @param RequestInterface $request Request to prepare and validate + * @param Response $response The current response + * + * @return RequestInterface + */ + protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) + { + $params = $original->getParams(); + // This is a new redirect, so increment the redirect counter + $current = $params[self::REDIRECT_COUNT] + 1; + $params[self::REDIRECT_COUNT] = $current; + // Use a provided maximum value or default to a max redirect count of 5 + $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; + + // Throw an exception if the redirect count is exceeded + if ($current > $max) { + $this->throwTooManyRedirectsException($original, $max); + return false; + } else { + // Create a redirect request based on the redirect rules set on the request + return $this->createRedirectRequest( + $request, + $response->getStatusCode(), + trim($response->getLocation()), + $original + ); + } + } + + /** + * Send a redirect request and handle any errors + * + * @param RequestInterface $original The originating request + * @param RequestInterface $request The current request being redirected + * @param Response $response The response of the current request + * + * @throws BadResponseException|\Exception + */ + protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) + { + // Validate and create a redirect request based on the original request and current response + if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { + try { + $redirectRequest->send(); + } catch (BadResponseException $e) { + $e->getResponse(); + if (!$e->getResponse()) { + throw $e; + } + } + } + } + + /** + * Throw a too many redirects exception for a request + * + * @param RequestInterface $original Request + * @param int $max Max allowed redirects + * + * @throws TooManyRedirectsException when too many redirects have been issued + */ + protected function throwTooManyRedirectsException(RequestInterface $original, $max) + { + $original->getEventDispatcher()->addListener( + 'request.complete', + $func = function ($e) use (&$func, $original, $max) { + $original->getEventDispatcher()->removeListener('request.complete', $func); + $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); + throw new TooManyRedirectsException($str); + } + ); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem new file mode 100644 index 0000000..18ce703 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem @@ -0,0 +1,3870 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla downloaded on: Wed Aug 13 21:49:32 2014 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl verison 1.22. +## SHA1: bf2c15b3019e696660321d2227d942936dc50aa7 +## + + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +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 +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +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 +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +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 +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php new file mode 100644 index 0000000..dbd4c18 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/StaticClient.php @@ -0,0 +1,157 @@ +createRequest($method, $url, null, null, $options); + + if (isset($options['stream'])) { + if ($options['stream'] instanceof StreamRequestFactoryInterface) { + return $options['stream']->fromRequest($request); + } elseif ($options['stream'] == true) { + $streamFactory = new PhpStreamRequestFactory(); + return $streamFactory->fromRequest($request); + } + } + + return $request->send(); + } + + /** + * Send a GET request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function get($url, $options = array()) + { + return self::request('GET', $url, $options); + } + + /** + * Send a HEAD request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function head($url, $options = array()) + { + return self::request('HEAD', $url, $options); + } + + /** + * Send a DELETE request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function delete($url, $options = array()) + { + return self::request('DELETE', $url, $options); + } + + /** + * Send a POST request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function post($url, $options = array()) + { + return self::request('POST', $url, $options); + } + + /** + * Send a PUT request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function put($url, $options = array()) + { + return self::request('PUT', $url, $options); + } + + /** + * Send a PATCH request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function patch($url, $options = array()) + { + return self::request('PATCH', $url, $options); + } + + /** + * Send an OPTIONS request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function options($url, $options = array()) + { + return self::request('OPTIONS', $url, $options); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php new file mode 100644 index 0000000..6a4e772 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php @@ -0,0 +1,554 @@ + null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + if (false === ($parts = parse_url($url))) { + throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a QueryString object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = QueryString::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (isset($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (isset($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) + && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && 0 !== strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something is before the path + if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $url .= '?' . $parts['query']; + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param QueryString|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + if (!$query) { + $this->query = new QueryString(); + } else { + $this->setQuery($query); + } + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + $this->query = clone $this->query; + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return self::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + $query = (string) $this->query; + + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->getPath(), + 'query' => $query !== '' ? $query : null, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + + return $this; + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Url + */ + public function setScheme($scheme) + { + if ($this->scheme == 'http' && $this->port == 80) { + $this->port = null; + } elseif ($this->scheme == 'https' && $this->port == 443) { + $this->port = null; + } + + $this->scheme = $scheme; + + return $this; + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + * + * @return Url + */ + public function setPort($port) + { + $this->port = $port; + + return $this; + } + + /** + * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif ($this->scheme == 'http') { + return 80; + } elseif ($this->scheme == 'https') { + return 443; + } + + return null; + } + + /** + * Set the path part of the URL + * + * @param array|string $path Path string or array of path segments + * + * @return Url + */ + public function setPath($path) + { + static $pathReplace = array(' ' => '%20', '?' => '%3F'); + if (is_array($path)) { + $path = '/' . implode('/', $path); + } + + $this->path = strtr($path, $pathReplace); + + return $this; + } + + /** + * Normalize the URL so that double slashes and relative paths are removed + * + * @return Url + */ + public function normalizePath() + { + if (!$this->path || $this->path == '/' || $this->path == '*') { + return $this; + } + + $results = array(); + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif ($segment != '.' && $segment != '') { + $results[] = $segment; + } + } + + // Combine the normalized parts and add the leading slash if needed + $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results); + + // Add the trailing slash if necessary + if ($this->path != '/' && end($segments) == '') { + $this->path .= '/'; + } + + return $this; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + * + * @return Url + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) { + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + $this->setPath(str_replace('//', '/', $this->path . $relativePath)); + } + + return $this; + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return array_slice(explode('/', $this->getPath()), 1); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + * + * @return Url + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + * + * @return Url + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a QueryString object + * + * @return QueryString + */ + public function getQuery() + { + return $this->query; + } + + /** + * Set the query part of the URL + * + * @param QueryString|string|array $query Query to set + * + * @return Url + */ + public function setQuery($query) + { + if (is_string($query)) { + $output = null; + parse_str($query, $output); + $this->query = new QueryString($output); + } elseif (is_array($query)) { + $this->query = new QueryString($query); + } elseif ($query instanceof QueryString) { + $this->query = $query; + } + + return $this; + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + * + * @return Url + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first + * released, Guzzle used an incorrect algorithm for combining relative URL paths. In + * order to not break users, we introduced this flag to allow the merging of URLs based + * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with + * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would + * become "http://a.com/foo/baz/bar". + * @return Url + * @throws InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url, $strictRfc3986 = false) + { + $url = self::factory($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + // Passing a URL with a scheme overrides everything + if ($buffer = $url->getScheme()) { + $this->scheme = $buffer; + $this->host = $url->getHost(); + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + // Setting a host overrides the entire rest of the URL + if ($buffer = $url->getHost()) { + $this->host = $buffer; + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + $path = $url->getPath(); + $query = $url->getQuery(); + + if (!$path) { + if (count($query)) { + $this->addQuery($query, $strictRfc3986); + } + } else { + if ($path[0] == '/') { + $this->path = $path; + } elseif ($strictRfc3986) { + $this->path .= '/../' . $path; + } else { + $this->path .= '/' . $path; + } + $this->normalizePath(); + $this->addQuery($query, $strictRfc3986); + } + + $this->fragment = $url->getFragment(); + + return $this; + } + + private function addQuery(QueryString $new, $strictRfc386) + { + if (!$strictRfc386) { + $new->merge($this->query); + } + + $this->query = $new; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json new file mode 100644 index 0000000..9384a5b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json @@ -0,0 +1,32 @@ +{ + "name": "guzzle/http", + "description": "HTTP libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": ["http client", "http", "client", "Guzzle", "curl"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version", + "guzzle/parser": "self.version", + "guzzle/stream": "self.version" + }, + "suggest": { + "ext-curl": "*" + }, + "autoload": { + "psr-0": { "Guzzle\\Http": "" } + }, + "target-dir": "Guzzle/Http", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php new file mode 100644 index 0000000..c699773 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php @@ -0,0 +1,38 @@ + array(), + 'camel' => array() + ); + + /** @var int Max entries per cache */ + protected $maxCacheSize; + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param int $maxCacheSize Maximum number of cached items to hold per cache + */ + public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) + { + $this->decoratedInflector = $inflector; + $this->maxCacheSize = $maxCacheSize; + } + + public function snake($word) + { + if (!isset($this->cache['snake'][$word])) { + $this->pruneCache('snake'); + $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); + } + + return $this->cache['snake'][$word]; + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + if (!isset($this->cache['camel'][$word])) { + $this->pruneCache('camel'); + $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); + } + + return $this->cache['camel'][$word]; + } + + /** + * Prune one of the named caches by removing 20% of the cache if it is full + * + * @param string $cache Type of cache to prune + */ + protected function pruneCache($cache) + { + if (count($this->cache[$cache]) == $this->maxCacheSize) { + $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php new file mode 100644 index 0000000..db37e4f --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php @@ -0,0 +1,59 @@ + array(), + 'camel' => array() + ); + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param array $snake Hash of pre-computed camel to snake + * @param array $camel Hash of pre-computed snake to camel + * @param bool $mirror Mirror snake and camel reflections + */ + public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) + { + if ($mirror) { + $camel = array_merge(array_flip($snake), $camel); + $snake = array_merge(array_flip($camel), $snake); + } + + $this->decoratedInflector = $inflector; + $this->mapping = array( + 'snake' => $snake, + 'camel' => $camel + ); + } + + public function snake($word) + { + return isset($this->mapping['snake'][$word]) + ? $this->mapping['snake'][$word] + : $this->decoratedInflector->snake($word); + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + return isset($this->mapping['camel'][$word]) + ? $this->mapping['camel'][$word] + : $this->decoratedInflector->camel($word); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json new file mode 100644 index 0000000..93f9e7b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Inflection/composer.json @@ -0,0 +1,26 @@ +{ + "name": "guzzle/inflection", + "description": "Guzzle inflection component", + "homepage": "http://guzzlephp.org/", + "keywords": ["inflection", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Inflection": "" } + }, + "target-dir": "Guzzle/Inflection", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php new file mode 100644 index 0000000..1b6bd7e --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php @@ -0,0 +1,19 @@ +getArrayIterator()->append($iterator); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php new file mode 100644 index 0000000..d76cdd4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php @@ -0,0 +1,56 @@ +chunkSize = $chunkSize; + } + + public function rewind() + { + parent::rewind(); + $this->next(); + } + + public function next() + { + $this->chunk = array(); + for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) { + $this->chunk[] = parent::current(); + parent::next(); + } + } + + public function current() + { + return $this->chunk; + } + + public function valid() + { + return (bool) $this->chunk; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php new file mode 100644 index 0000000..b103367 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php @@ -0,0 +1,36 @@ +callback = $callback; + } + + public function accept() + { + return call_user_func($this->callback, $this->current()); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php new file mode 100644 index 0000000..7e586bd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php @@ -0,0 +1,34 @@ +callback = $callback; + } + + public function current() + { + return call_user_func($this->callback, parent::current()); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php new file mode 100644 index 0000000..de4ab03 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php @@ -0,0 +1,27 @@ +getInnerIterator(); + while ($i instanceof \OuterIterator) { + $i = $i->getInnerIterator(); + } + + return call_user_func_array(array($i, $name), $args); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md new file mode 100644 index 0000000..8bb7e08 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/README.md @@ -0,0 +1,25 @@ +Guzzle Iterator +=============== + +Provides useful Iterators and Iterator decorators + +- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays +- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available +- MapIterator: Maps values before yielding +- MethodProxyIterator: Proxies missing method calls to the innermost iterator + +### Installing via Composer + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/iterator:~3.0 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json new file mode 100644 index 0000000..ee17379 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Iterator/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/iterator", + "description": "Provides helpful iterators and iterator decorators", + "keywords": ["iterator", "guzzle"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": ">=2.8.0" + }, + "autoload": { + "psr-0": { "Guzzle\\Iterator": "/" } + }, + "target-dir": "Guzzle/Iterator", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php new file mode 100644 index 0000000..7f6271b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php @@ -0,0 +1,16 @@ +log; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php new file mode 100644 index 0000000..a70fc8d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php @@ -0,0 +1,34 @@ +logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); + } + + /** + * Get logged entries + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears logged entries + */ + public function clearLogs() + { + $this->logs = array(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php new file mode 100644 index 0000000..d4bb73f --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php @@ -0,0 +1,23 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + call_user_func($this->log, $message, $priority, $extras); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php new file mode 100644 index 0000000..d7ac4ea --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php @@ -0,0 +1,18 @@ +>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}"; + const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}'; + + /** + * @var string Template used to format log messages + */ + protected $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::DEFAULT_FORMAT) + { + $this->template = $template ?: self::DEFAULT_FORMAT; + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->template = $template; + + return $this; + } + + /** + * Returns a formatted message + * + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received + * @param CurlHandle $handle Curl handle associated with the message + * @param array $customData Associative array of custom template data + * + * @return string + */ + public function format( + RequestInterface $request, + Response $response = null, + CurlHandle $handle = null, + array $customData = array() + ) { + $cache = $customData; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $handle, &$cache) { + + if (array_key_exists($matches[1], $cache)) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = (string) $request; + break; + case 'response': + $result = (string) $response; + break; + case 'req_body': + $result = $request instanceof EntityEnclosingRequestInterface + ? (string) $request->getBody() : ''; + break; + case 'res_body': + $result = $response ? $response->getBody(true) : ''; + break; + case 'ts': + $result = gmdate('c'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'url': + $result = (string) $request->getUrl(); + break; + case 'resource': + $result = $request->getResource(); + break; + case 'protocol': + $result = 'HTTP'; + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'host': + $result = $request->getHost(); + break; + case 'hostname': + $result = gethostname(); + break; + case 'port': + $result = $request->getPort(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : ''; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : ''; + break; + case 'connect_time': + $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) + ? $handle->getInfo(CURLINFO_CONNECT_TIME) + : ($response ? $response->getInfo('connect_time') : ''); + break; + case 'total_time': + $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) + ? $handle->getInfo(CURLINFO_TOTAL_TIME) + : ($response ? $response->getInfo('total_time') : ''); + break; + case 'curl_error': + $result = $handle ? $handle->getError() : ''; + break; + case 'curl_code': + $result = $handle ? $handle->getErrorNo() : ''; + break; + case 'curl_stderr': + $result = $handle ? $handle->getStderr() : ''; + break; + default: + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeader(substr($matches[1], 11)); + } elseif ($response && strpos($matches[1], 'res_header_') === 0) { + $result = $response->getHeader(substr($matches[1], 11)); + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php new file mode 100644 index 0000000..6afe7b6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php @@ -0,0 +1,34 @@ + Logger::DEBUG, + LOG_INFO => Logger::INFO, + LOG_WARNING => Logger::WARNING, + LOG_ERR => Logger::ERROR, + LOG_CRIT => Logger::CRITICAL, + LOG_ALERT => Logger::ALERT + ); + + public function __construct(Logger $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->addRecord(self::$mapping[$priority], $message, $extras); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php new file mode 100644 index 0000000..38a2b60 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php @@ -0,0 +1,36 @@ + LogLevel::DEBUG, + LOG_INFO => LogLevel::INFO, + LOG_WARNING => LogLevel::WARNING, + LOG_ERR => LogLevel::ERROR, + LOG_CRIT => LogLevel::CRITICAL, + LOG_ALERT => LogLevel::ALERT + ); + + public function __construct(LoggerInterface $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log(self::$mapping[$priority], $message, $extras); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php new file mode 100644 index 0000000..0ea8e3b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php @@ -0,0 +1,24 @@ +log = $logObject; + Version::warn(__CLASS__ . ' is deprecated'); + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($message, $priority, $extras); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php new file mode 100644 index 0000000..863f6a1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php @@ -0,0 +1,21 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($priority, $message, $extras); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json new file mode 100644 index 0000000..a8213e8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Log/composer.json @@ -0,0 +1,29 @@ +{ + "name": "guzzle/log", + "description": "Guzzle log adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["log", "adapter", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Log": "" } + }, + "suggest": { + "guzzle/http": "self.version" + }, + "target-dir": "Guzzle/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php new file mode 100644 index 0000000..4349eeb --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php @@ -0,0 +1,131 @@ + 'Domain', + 'path' => 'Path', + 'max_age' => 'Max-Age', + 'expires' => 'Expires', + 'version' => 'Version', + 'secure' => 'Secure', + 'port' => 'Port', + 'discard' => 'Discard', + 'comment' => 'Comment', + 'comment_url' => 'Comment-Url', + 'http_only' => 'HttpOnly' + ); + + public function parseCookie($cookie, $host = null, $path = null, $decode = false) + { + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return false; + } + + // Create the default return array + $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( + 'cookies' => array(), + 'data' => array(), + 'path' => null, + 'http_only' => false, + 'discard' => false, + 'domain' => $host + )); + $foundNonCookies = 0; + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + + if (count($cookieParts) == 1) { + // Can be a single value (e.g. secure, httpOnly) + $value = true; + } else { + // Be sure to strip wrapping quotes + $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); + if ($decode) { + $value = urldecode($value); + } + } + + // Only check for non-cookies when cookies have been found + if (!empty($data['cookies'])) { + foreach (self::$cookieParts as $mapValue => $search) { + if (!strcasecmp($search, $key)) { + $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; + $foundNonCookies++; + continue 2; + } + } + } + + // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a + // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. + $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; + } + + // Calculate the expires date + if (!$data['expires'] && $data['max_age']) { + $data['expires'] = time() + (int) $data['max_age']; + } + + // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4 + // "If the attribute-value is empty or if the first character of the + // attribute-value is not %x2F ("/"): + // Let cookie-path be the default-path. + // Otherwise: + // Let cookie-path be the attribute-value." + if (!$data['path'] || substr($data['path'], 0, 1) !== '/') { + $data['path'] = $this->getDefaultPath($path); + } + + return $data; + } + + /** + * Get default cookie path according to RFC 6265 + * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match + * + * @param string $path Request uri-path + * + * @return string + */ + protected function getDefaultPath($path) { + // "The user agent MUST use an algorithm equivalent to the following algorithm + // to compute the default-path of a cookie:" + + // "2. If the uri-path is empty or if the first character of the uri-path is not + // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. + if (empty($path) || substr($path, 0, 1) !== '/') { + return '/'; + } + + // "3. If the uri-path contains no more than one %x2F ("/") character, output + // %x2F ("/") and skip the remaining step." + if ($path === "/") { + return $path; + } + + $rightSlashPos = strrpos($path, '/'); + if ($rightSlashPos === 0) { + return "/"; + } + + // "4. Output the characters of the uri-path from the first character up to, + // but not including, the right-most %x2F ("/")." + return substr($path, 0, $rightSlashPos); + + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php new file mode 100644 index 0000000..d21ffe2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php @@ -0,0 +1,33 @@ + $requestUrl, + 'scheme' => 'http' + ); + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php new file mode 100644 index 0000000..efc1aa3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php @@ -0,0 +1,110 @@ +parseMessage($message); + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = array( + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage(isset($parts['start_line'][1]) ? $parts['start_line'][1] : '' , $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = $this->parseMessage($message); + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return array( + 'protocol' => $protocol, + 'version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array + */ + protected function parseMessage($message) + { + $startLine = null; + $headers = array(); + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = array($headers[$key], $value); + } else { + $headers[$key][] = $value; + } + } + } + + return array( + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php new file mode 100644 index 0000000..cc44808 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php @@ -0,0 +1,27 @@ + $parts->requestMethod, + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'headers' => $parts->headers, + 'body' => $parts->body + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = http_parse_message($message); + + return array( + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'code' => $parts->responseCode, + 'reason_phrase' => $parts->responseStatus, + 'headers' => $parts->headers, + 'body' => $parts->body + ); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php new file mode 100644 index 0000000..f838683 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php @@ -0,0 +1,75 @@ + 'Guzzle\\Parser\\Message\\MessageParser', + 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', + 'url' => 'Guzzle\\Parser\\Url\\UrlParser', + 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', + ); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new static; + } + + return self::$instance; + } + + public function __construct() + { + // Use the PECL URI template parser if available + if (extension_loaded('uri_template')) { + $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; + } + } + + /** + * Get a parser by name from an instance + * + * @param string $name Name of the parser to retrieve + * + * @return mixed|null + */ + public function getParser($name) + { + if (!isset($this->instances[$name])) { + if (!isset($this->mapping[$name])) { + return null; + } + $class = $this->mapping[$name]; + $this->instances[$name] = new $class(); + } + + return $this->instances[$name]; + } + + /** + * Register a custom parser by name with the register + * + * @param string $name Name or handle of the parser to register + * @param mixed $parser Instantiated parser to register + */ + public function registerParser($name, $parser) + { + $this->instances[$name] = $parser; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php new file mode 100644 index 0000000..b0764e8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php @@ -0,0 +1,26 @@ + true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true + ); + + /** @var array Delimiters */ + private static $delims = array( + ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' + ); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array( + '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D' + ); + + public function expand($template, array $variables) + { + if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template); + } + + /** + * Set the regex patten used to expand URI templates + * + * @param string $regexPattern + */ + public function setRegex($regexPattern) + { + $this->regex = $regexPattern; + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + // Check for URI operators + $operator = ''; + + if (isset(self::$operatorHash[$expression[0]])) { + $operator = $expression[0]; + $expression = substr($expression, 1); + } + + $values = explode(',', $expression); + foreach ($values as &$value) { + $value = trim($value); + $varspec = array(); + $substrPos = strpos($value, ':'); + if ($substrPos) { + $varspec['value'] = substr($value, 0, $substrPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $substrPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $value = $varspec; + } + + return array( + 'operator' => $operator, + 'values' => $values + ); + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array( + '+' => '%20', + '%7e' => '~' + ); + + $parsed = self::parseExpression($matches[1]); + $replacements = array(); + + $prefix = $parsed['operator']; + $joiner = $parsed['operator']; + $useQueryString = false; + if ($parsed['operator'] == '?') { + $joiner = '&'; + $useQueryString = true; + } elseif ($parsed['operator'] == '&') { + $useQueryString = true; + } elseif ($parsed['operator'] == '#') { + $joiner = ','; + } elseif ($parsed['operator'] == ';') { + $useQueryString = true; + } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { + $joiner = ','; + $prefix = ''; + } + + foreach ($parsed['values'] as $value) { + + if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQueryString = $useQueryString; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested structures + $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQueryString) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQueryString = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode modifier with an associative array + $actuallyUseQueryString = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the explode modifier is not set, then the + // result must be a comma separated list of keys followed by their respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQueryString) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Removes percent encoding on reserved characters (used with + and # modifiers) + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php new file mode 100644 index 0000000..c81d515 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php @@ -0,0 +1,21 @@ +utf8 = $utf8; + } + + public function parseUrl($url) + { + Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); + + static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + $parts = parse_url($url); + + // Need to handle query parsing specially for UTF-8 requirements + if ($this->utf8 && isset($parts['query'])) { + $queryPos = strpos($url, '?'); + if (isset($parts['fragment'])) { + $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); + } else { + $parts['query'] = substr($url, $queryPos + 1); + } + } + + return $parts + $defaults; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php new file mode 100644 index 0000000..89ac4b3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php @@ -0,0 +1,19 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Parser": "" } + }, + "target-dir": "Guzzle/Parser", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php new file mode 100644 index 0000000..ae59418 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php @@ -0,0 +1,84 @@ + 'onBeforeSend', + 'request.exception' => 'onRequestTimeout', + 'request.sent' => 'onRequestSent', + 'curl.callback.progress' => 'onCurlProgress' + ); + } + + /** + * Event used to ensure that progress callback are emitted from the curl handle's request mediator. + * + * @param Event $event + */ + public function onBeforeSend(Event $event) + { + // Ensure that progress callbacks are dispatched + $event['request']->getCurlOptions()->set('progress', true); + } + + /** + * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to + * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with + * transmitting the request, and tell curl not download a body. + * + * @param Event $event + */ + public function onCurlProgress(Event $event) + { + if ($event['handle'] && + ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded'])) + ) { + // Timeout after 1ms + curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1); + // Even if the response is quick, tell curl not to download the body. + // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the + // request method is not converted to a HEAD request before the request was sent via curl. + if ($event['uploaded']) { + curl_setopt($event['handle'], CURLOPT_NOBODY, true); + } + } + } + + /** + * Event emitted when a curl exception occurs. Ignore the exception and set a mock response. + * + * @param Event $event + */ + public function onRequestTimeout(Event $event) + { + if ($event['exception'] instanceof CurlException) { + $event['request']->setResponse(new Response(200, array( + 'X-Guzzle-Async' => 'Did not wait for the response' + ))); + } + } + + /** + * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the + * caller that there is no body in the message. + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + // Let the caller know this was meant to be async + $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json new file mode 100644 index 0000000..dc3fc5b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-async", + "description": "Guzzle async request plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Async": "" } + }, + "target-dir": "Guzzle/Plugin/Async", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php new file mode 100644 index 0000000..0a85983 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php @@ -0,0 +1,91 @@ +next = $next; + } + + /** + * Get the next backoff strategy in the chain + * + * @return AbstractBackoffStrategy|null + */ + public function getNext() + { + return $this->next; + } + + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ) { + $delay = $this->getDelay($retries, $request, $response, $e); + if ($delay === false) { + // The strategy knows that this must not be retried + return false; + } elseif ($delay === null) { + // If the strategy is deferring a decision and the next strategy will not make a decision then return false + return !$this->next || !$this->next->makesDecision() + ? false + : $this->next->getBackoffPeriod($retries, $request, $response, $e); + } elseif ($delay === true) { + // if the strategy knows that it must retry but is deferring to the next to determine the delay + if (!$this->next) { + return 0; + } else { + $next = $this->next; + while ($next->makesDecision() && $next->getNext()) { + $next = $next->getNext(); + } + return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; + } + } else { + return $delay; + } + } + + /** + * Check if the strategy does filtering and makes decisions on whether or not to retry. + * + * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff + * decision. + * + * @return bool + */ + abstract public function makesDecision(); + + /** + * Implement the concrete strategy + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true + * or null to defer to the next strategy if available, and if not, return 0. + */ + abstract protected function getDelay( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php new file mode 100644 index 0000000..6ebee6c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php @@ -0,0 +1,40 @@ +errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); + $this->next = $next; + } + + /** + * Get the default failure codes to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return static::$defaultErrorCodes; + } + + public function makesDecision() + { + return true; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php new file mode 100644 index 0000000..ec54c28 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php @@ -0,0 +1,76 @@ +logger = $logger; + $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->formatter->setTemplate($template); + + return $this; + } + + /** + * Called when a request is being retried + * + * @param Event $event Event emitted + */ + public function onRequestRetry(Event $event) + { + $this->logger->log($this->formatter->format( + $event['request'], + $event['response'], + $event['handle'], + array( + 'retries' => $event['retries'], + 'delay' => $event['delay'] + ) + )); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php new file mode 100644 index 0000000..99ace05 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php @@ -0,0 +1,126 @@ +strategy = $strategy; + } + + /** + * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors + * + * @param int $maxRetries Maximum number of retries + * @param array $httpCodes HTTP response codes to retry + * @param array $curlCodes cURL error codes to retry + * + * @return self + */ + public static function getExponentialBackoff( + $maxRetries = 3, + array $httpCodes = null, + array $curlCodes = null + ) { + return new self(new TruncatedBackoffStrategy($maxRetries, + new HttpBackoffStrategy($httpCodes, + new CurlBackoffStrategy($curlCodes, + new ExponentialBackoffStrategy() + ) + ) + )); + } + + public static function getAllEvents() + { + return array(self::RETRY_EVENT); + } + + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + + $params = $request->getParams(); + $retries = (int) $params->get(self::RETRY_PARAM); + $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); + + if ($delay !== false) { + // Calculate how long to wait until the request should be retried + $params->set(self::RETRY_PARAM, ++$retries) + ->set(self::DELAY_PARAM, microtime(true) + $delay); + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $this->dispatch(self::RETRY_EVENT, array( + 'request' => $request, + 'response' => $response, + 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, + 'retries' => $retries, + 'delay' => $delay + )); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. This is required for cURL to know that we + // want to retry sending the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php new file mode 100644 index 0000000..4e590db --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php @@ -0,0 +1,30 @@ +callback = $callback; + $this->decision = (bool) $decision; + $this->next = $next; + } + + public function makesDecision() + { + return $this->decision; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return call_user_func($this->callback, $retries, $request, $response, $e); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php new file mode 100644 index 0000000..061d2a4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php @@ -0,0 +1,34 @@ +delay = $delay; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $this->delay; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php new file mode 100644 index 0000000..a584ed4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php @@ -0,0 +1,28 @@ +errorCodes[$e->getErrorNo()]) ? true : null; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php new file mode 100644 index 0000000..fb2912d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php @@ -0,0 +1,25 @@ +isSuccessful()) { + return false; + } else { + return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php new file mode 100644 index 0000000..b35e8a4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php @@ -0,0 +1,36 @@ +step = $step; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries * $this->step; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php new file mode 100644 index 0000000..4fd73fe --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php @@ -0,0 +1,25 @@ +errorCodes[$response->getReasonPhrase()]) ? true : null; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php new file mode 100644 index 0000000..3608f35 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php @@ -0,0 +1,36 @@ +max = $maxRetries; + $this->next = $next; + } + + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries < $this->max ? null : false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json new file mode 100644 index 0000000..91c122c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-backoff", + "description": "Guzzle backoff retry plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Backoff": "" } + }, + "target-dir": "Guzzle/Plugin/Backoff", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php new file mode 100644 index 0000000..7790f88 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php @@ -0,0 +1,11 @@ + new DefaultCacheStorage($options)); + } elseif ($options instanceof CacheStorageInterface) { + $options = array('storage' => $options); + } elseif ($options) { + $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options))); + } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) { + // @codeCoverageIgnoreStart + throw new InvalidArgumentException('No cache was provided and Doctrine is not installed'); + // @codeCoverageIgnoreEnd + } + } + + $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false; + + // Add a cache storage if a cache adapter was provided + $this->storage = isset($options['storage']) + ? $options['storage'] + : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + + if (!isset($options['can_cache'])) { + $this->canCache = new DefaultCanCacheStrategy(); + } else { + $this->canCache = is_callable($options['can_cache']) + ? new CallbackCanCacheStrategy($options['can_cache']) + : $options['can_cache']; + } + + // Use the provided revalidation strategy or the default + $this->revalidation = isset($options['revalidation']) + ? $options['revalidation'] + : new DefaultRevalidation($this->storage, $this->canCache); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -255), + 'request.sent' => array('onRequestSent', 255), + 'request.error' => array('onRequestError', 0), + 'request.exception' => array('onRequestException', 0), + ); + } + + /** + * Check if a response in cache will satisfy the request before sending + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + if (!$this->canCache->canCacheRequest($request)) { + switch ($request->getMethod()) { + case 'PURGE': + $this->purge($request); + $request->setResponse(new Response(200, array(), 'purged')); + break; + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + if ($this->autoPurge) { + $this->purge($request); + } + } + return; + } + + if ($response = $this->storage->fetch($request)) { + $params = $request->getParams(); + $params['cache.lookup'] = true; + $response->setHeader( + 'Age', + time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now') + ); + // Validate that the response satisfies the request + if ($this->canResponseSatisfyRequest($request, $response)) { + if (!isset($params['cache.hit'])) { + $params['cache.hit'] = true; + } + $request->setResponse($response); + } + } + } + + /** + * If possible, store a response in cache after sending + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + + if ($request->getParams()->get('cache.hit') === null && + $this->canCache->canCacheRequest($request) && + $this->canCache->canCacheResponse($response) + ) { + $this->storage->cache($request, $response); + } + + $this->addResponseHeaders($request, $response); + } + + /** + * If possible, return a cache response on an error + * + * @param Event $event + */ + public function onRequestError(Event $event) + { + $request = $event['request']; + + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader( + 'Age', + time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now') + ); + + if ($this->canResponseSatisfyFailedRequest($request, $response)) { + $request->getParams()->set('cache.hit', 'error'); + $this->addResponseHeaders($request, $response); + $event['response'] = $response; + $event->stopPropagation(); + } + } + } + + /** + * If possible, set a cache response on a cURL exception + * + * @param Event $event + * + * @return null + */ + public function onRequestException(Event $event) + { + if (!$event['exception'] instanceof CurlException) { + return; + } + + $request = $event['request']; + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now')); + if (!$this->canResponseSatisfyFailedRequest($request, $response)) { + return; + } + $request->getParams()->set('cache.hit', 'error'); + $request->setResponse($response); + $this->addResponseHeaders($request, $response); + $event->stopPropagation(); + } + } + + /** + * Check if a cache response satisfies a request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyRequest(RequestInterface $request, Response $response) + { + $responseAge = $response->calculateAge(); + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + + // Check the request's max-age header against the age of the response + if ($reqc && $reqc->hasDirective('max-age') && + $responseAge > $reqc->getDirective('max-age')) { + return false; + } + + // Check the response's max-age header + if ($response->isFresh() === false) { + $maxStale = $reqc ? $reqc->getDirective('max-stale') : null; + if (null !== $maxStale) { + if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { + return false; + } + } elseif ($resc && $resc->hasDirective('max-age') + && $responseAge > $resc->getDirective('max-age') + ) { + return false; + } + } + + if ($this->revalidation->shouldRevalidate($request, $response)) { + try { + return $this->revalidation->revalidate($request, $response); + } catch (CurlException $e) { + $request->getParams()->set('cache.hit', 'error'); + return $this->canResponseSatisfyFailedRequest($request, $response); + } + } + + return true; + } + + /** + * Check if a cache response satisfies a failed request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response) + { + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null; + $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null; + + if (!$requestStaleIfError && !$responseStaleIfError) { + return false; + } + + if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) { + return false; + } + + if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) { + return false; + } + + return true; + } + + /** + * Purge all cache entries for a given URL + * + * @param string $url URL to purge + */ + public function purge($url) + { + // BC compatibility with previous version that accepted a Request object + $url = $url instanceof RequestInterface ? $url->getUrl() : $url; + $this->storage->purge($url); + } + + /** + * Add the plugin's headers to a response + * + * @param RequestInterface $request Request + * @param Response $response Response to add headers to + */ + protected function addResponseHeaders(RequestInterface $request, Response $response) + { + $params = $request->getParams(); + $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; + if ($header = $response->getHeader('X-Cache-Lookup')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $lookup; + $response->setHeader('X-Cache-Lookup', array_unique($values)); + } else { + $response->setHeader('X-Cache-Lookup', $lookup); + } + + if ($params['cache.hit'] === true) { + $xcache = 'HIT from GuzzleCache'; + } elseif ($params['cache.hit'] == 'error') { + $xcache = 'HIT_ERROR from GuzzleCache'; + } else { + $xcache = 'MISS from GuzzleCache'; + } + + if ($header = $response->getHeader('X-Cache')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $xcache; + $response->setHeader('X-Cache', array_unique($values)); + } else { + $response->setHeader('X-Cache', $xcache); + } + + if ($response->isFresh() === false) { + $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); + if ($params['cache.hit'] === 'error') { + $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php new file mode 100644 index 0000000..f3d9154 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php @@ -0,0 +1,43 @@ +requestCallback = $requestCallback; + $this->responseCallback = $responseCallback; + } + + public function canCacheRequest(RequestInterface $request) + { + return $this->requestCallback + ? call_user_func($this->requestCallback, $request) + : parent::canCacheRequest($request); + } + + public function canCacheResponse(Response $response) + { + return $this->responseCallback + ? call_user_func($this->responseCallback, $response) + : parent::canCacheResponse($response); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php new file mode 100644 index 0000000..6e01a8e --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php @@ -0,0 +1,30 @@ +getParams()->get(self::CACHE_KEY); + + if (!$key) { + + $cloned = clone $request; + $cloned->removeHeader('Cache-Control'); + + // Check to see how and if the key should be filtered + foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { + $pieces = array_map('trim', explode('=', $part)); + if (isset($pieces[1])) { + foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { + if ($pieces[0] == 'header') { + $cloned->removeHeader($remove); + } elseif ($pieces[0] == 'query') { + $cloned->getQuery()->remove($remove); + } + } + } + } + + $raw = (string) $cloned; + $key = 'GZ' . md5($raw); + $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); + } + + return $key; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php new file mode 100644 index 0000000..26d7a8b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php @@ -0,0 +1,266 @@ +cache = CacheAdapterFactory::fromCache($cache); + $this->defaultTtl = $defaultTtl; + $this->keyPrefix = $keyPrefix; + } + + public function cache(RequestInterface $request, Response $response) + { + $currentTime = time(); + + $overrideTtl = $request->getParams()->get('cache.override_ttl'); + if ($overrideTtl) { + $ttl = $overrideTtl; + } else { + $maxAge = $response->getMaxAge(); + if ($maxAge !== null) { + $ttl = $maxAge; + } else { + $ttl = $this->defaultTtl; + } + } + + if ($cacheControl = $response->getHeader('Cache-Control')) { + $stale = $cacheControl->getDirective('stale-if-error'); + if ($stale === true) { + $ttl += $ttl; + } else if (is_numeric($stale)) { + $ttl += $stale; + } + } + + // Determine which manifest key should be used + $key = $this->getCacheKey($request); + $persistedRequest = $this->persistHeaders($request); + $entries = array(); + + if ($manifest = $this->cache->fetch($key)) { + // Determine which cache entries should still be in the cache + $vary = $response->getVary(); + foreach (unserialize($manifest) as $entry) { + // Check if the entry is expired + if ($entry[4] < $currentTime) { + continue; + } + $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : ''; + if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) { + $entries[] = $entry; + } + } + } + + // Persist the response body if needed + $bodyDigest = null; + if ($response->getBody() && $response->getBody()->getContentLength() > 0) { + $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody()); + $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl); + } + + array_unshift($entries, array( + $persistedRequest, + $this->persistHeaders($response), + $response->getStatusCode(), + $bodyDigest, + $currentTime + $ttl + )); + + $this->cache->save($key, serialize($entries)); + } + + public function delete(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if ($entries = $this->cache->fetch($key)) { + // Delete each cached body + foreach (unserialize($entries) as $entry) { + if ($entry[3]) { + $this->cache->delete($entry[3]); + } + } + $this->cache->delete($key); + } + } + + public function purge($url) + { + foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $this->delete(new Request($method, $url)); + } + } + + public function fetch(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if (!($entries = $this->cache->fetch($key))) { + return null; + } + + $match = null; + $headers = $this->persistHeaders($request); + $entries = unserialize($entries); + foreach ($entries as $index => $entry) { + if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) { + $match = $entry; + break; + } + } + + if (!$match) { + return null; + } + + // Ensure that the response is not expired + $response = null; + if ($match[4] < time()) { + $response = -1; + } else { + $response = new Response($match[2], $match[1]); + if ($match[3]) { + if ($body = $this->cache->fetch($match[3])) { + $response->setBody($body); + } else { + // The response is not valid because the body was somehow deleted + $response = -1; + } + } + } + + if ($response === -1) { + // Remove the entry from the metadata and update the cache + unset($entries[$index]); + if ($entries) { + $this->cache->save($key, serialize($entries)); + } else { + $this->cache->delete($key); + } + return null; + } + + return $response; + } + + /** + * Hash a request URL into a string that returns cache metadata + * + * @param RequestInterface $request + * + * @return string + */ + protected function getCacheKey(RequestInterface $request) + { + // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) + if ($filter = $request->getParams()->get('cache.key_filter')) { + $url = $request->getUrl(true); + foreach (explode(',', $filter) as $remove) { + $url->getQuery()->remove(trim($remove)); + } + } else { + $url = $request->getUrl(); + } + + return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); + } + + /** + * Create a cache key for a response's body + * + * @param string $url URL of the entry + * @param EntityBodyInterface $body Response body + * + * @return string + */ + protected function getBodyKey($url, EntityBodyInterface $body) + { + return $this->keyPrefix . md5($url) . $body->getContentMd5(); + } + + /** + * Determines whether two Request HTTP header sets are non-varying + * + * @param string $vary Response vary header + * @param array $r1 HTTP header array + * @param array $r2 HTTP header array + * + * @return bool + */ + private function requestsMatch($vary, $r1, $r2) + { + if ($vary) { + foreach (explode(',', $vary) as $header) { + $key = trim(strtolower($header)); + $v1 = isset($r1[$key]) ? $r1[$key] : null; + $v2 = isset($r2[$key]) ? $r2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + } + + return true; + } + + /** + * Creates an array of cacheable and normalized message headers + * + * @param MessageInterface $message + * + * @return array + */ + private function persistHeaders(MessageInterface $message) + { + // Headers are excluded from the caching (see RFC 2616:13.5.1) + static $noCache = array( + 'age' => true, + 'connection' => true, + 'keep-alive' => true, + 'proxy-authenticate' => true, + 'proxy-authorization' => true, + 'te' => true, + 'trailers' => true, + 'transfer-encoding' => true, + 'upgrade' => true, + 'set-cookie' => true, + 'set-cookie2' => true + ); + + // Clone the response to not destroy any necessary headers when caching + $headers = $message->getHeaders()->getAll(); + $headers = array_diff_key($headers, $noCache); + // Cast the headers to a string + $headers = array_map(function ($h) { return (string) $h; }, $headers); + + return $headers; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php new file mode 100644 index 0000000..3ca1fbf --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php @@ -0,0 +1,32 @@ +getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) { + return false; + } + + // Never cache requests when using no-store + if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return true; + } + + public function canCacheResponse(Response $response) + { + return $response->isSuccessful() && $response->canCache(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php new file mode 100644 index 0000000..af33234 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php @@ -0,0 +1,174 @@ +storage = $cache; + $this->canCache = $canCache ?: new DefaultCanCacheStrategy(); + } + + public function revalidate(RequestInterface $request, Response $response) + { + try { + $revalidate = $this->createRevalidationRequest($request, $response); + $validateResponse = $revalidate->send(); + if ($validateResponse->getStatusCode() == 200) { + return $this->handle200Response($request, $validateResponse); + } elseif ($validateResponse->getStatusCode() == 304) { + return $this->handle304Response($request, $validateResponse, $response); + } + } catch (BadResponseException $e) { + $this->handleBadResponse($e); + } + + // Other exceptions encountered in the revalidation request are ignored + // in hopes that sending a request to the origin server will fix it + return false; + } + + public function shouldRevalidate(RequestInterface $request, Response $response) + { + if ($request->getMethod() != RequestInterface::GET) { + return false; + } + + $reqCache = $request->getHeader('Cache-Control'); + $resCache = $response->getHeader('Cache-Control'); + + $revalidate = $request->getHeader('Pragma') == 'no-cache' || + ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) || + ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'))); + + // Use the strong ETag validator if available and the response contains no Cache-Control directive + if (!$revalidate && !$resCache && $response->hasHeader('ETag')) { + $revalidate = true; + } + + return $revalidate; + } + + /** + * Handles a bad response when attempting to revalidate + * + * @param BadResponseException $e Exception encountered + * + * @throws BadResponseException + */ + protected function handleBadResponse(BadResponseException $e) + { + // 404 errors mean the resource no longer exists, so remove from + // cache, and prevent an additional request by throwing the exception + if ($e->getResponse()->getStatusCode() == 404) { + $this->storage->delete($e->getRequest()); + throw $e; + } + } + + /** + * Creates a request to use for revalidation + * + * @param RequestInterface $request Request + * @param Response $response Response to revalidate + * + * @return RequestInterface returns a revalidation request + */ + protected function createRevalidationRequest(RequestInterface $request, Response $response) + { + $revalidate = clone $request; + $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control'); + + if ($response->getLastModified()) { + $revalidate->setHeader('If-Modified-Since', $response->getLastModified()); + } + + if ($response->getEtag()) { + $revalidate->setHeader('If-None-Match', $response->getEtag()); + } + + // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations + $dispatcher = $revalidate->getEventDispatcher(); + foreach ($dispatcher->getListeners() as $eventName => $listeners) { + foreach ($listeners as $listener) { + if (is_array($listener) && $listener[0] instanceof CachePlugin) { + $dispatcher->removeListener($eventName, $listener); + } + } + } + + return $revalidate; + } + + /** + * Handles a 200 response response from revalidating. The server does not support validation, so use this response. + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle200Response(RequestInterface $request, Response $validateResponse) + { + $request->setResponse($validateResponse); + if ($this->canCache->canCacheResponse($validateResponse)) { + $this->storage->cache($request, $validateResponse); + } + + return false; + } + + /** + * Handle a 304 response and ensure that it is still valid + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * @param Response $response Original cached response + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response) + { + static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); + + // Make sure that this response has the same ETag + if ($validateResponse->getEtag() != $response->getEtag()) { + return false; + } + + // Replace cached headers with any of these headers from the + // origin server that might be more up to date + $modified = false; + foreach ($replaceHeaders as $name) { + if ($validateResponse->hasHeader($name)) { + $modified = true; + $response->setHeader($name, $validateResponse->getHeader($name)); + } + } + + // Store the updated response in cache + if ($modified && $this->canCache->canCacheResponse($response)) { + $this->storage->cache($request, $response); + } + + return true; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php new file mode 100644 index 0000000..88b86f3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php @@ -0,0 +1,19 @@ +=5.3.2", + "guzzle/http": "self.version", + "guzzle/cache": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cache": "" } + }, + "target-dir": "Guzzle/Plugin/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php new file mode 100644 index 0000000..5218e5f --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php @@ -0,0 +1,538 @@ + '', + 'value' => '', + 'domain' => '', + 'path' => '/', + 'expires' => null, + 'max_age' => 0, + 'comment' => null, + 'comment_url' => null, + 'port' => array(), + 'version' => null, + 'secure' => false, + 'discard' => false, + 'http_only' => false + ); + + $this->data = array_merge($defaults, $data); + // Extract the expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the expires date + $this->setExpires(time() + (int) $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires(strtotime($this->getExpires())); + } + } + + /** + * Get the cookie as an array + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return Cookie + */ + public function setName($name) + { + return $this->setData('name', $name); + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return Cookie + */ + public function setValue($value) + { + return $this->setData('value', $value); + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return Cookie + */ + public function setDomain($domain) + { + return $this->setData('domain', $domain); + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return Cookie + */ + public function setPath($path) + { + return $this->setData('path', $path); + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['max_age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return Cookie + */ + public function setMaxAge($maxAge) + { + return $this->setData('max_age', $maxAge); + } + + /** + * The UNIX timestamp when the cookie expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return Cookie + */ + public function setExpires($timestamp) + { + return $this->setData('expires', $timestamp); + } + + /** + * Version of the cookie specification. RFC 2965 is 1 + * + * @return mixed + */ + public function getVersion() + { + return $this->data['version']; + } + + /** + * Set the cookie version + * + * @param string|int $version Version to set + * + * @return Cookie + */ + public function setVersion($version) + { + return $this->setData('version', $version); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return Cookie + */ + public function setSecure($secure) + { + return $this->setData('secure', (bool) $secure); + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return Cookie + */ + public function setDiscard($discard) + { + return $this->setData('discard', $discard); + } + + /** + * Get the comment + * + * @return string|null + */ + public function getComment() + { + return $this->data['comment']; + } + + /** + * Set the comment of the cookie + * + * @param string $comment Cookie comment + * + * @return Cookie + */ + public function setComment($comment) + { + return $this->setData('comment', $comment); + } + + /** + * Get the comment URL of the cookie + * + * @return string|null + */ + public function getCommentUrl() + { + return $this->data['comment_url']; + } + + /** + * Set the comment URL of the cookie + * + * @param string $commentUrl Cookie comment URL for more information + * + * @return Cookie + */ + public function setCommentUrl($commentUrl) + { + return $this->setData('comment_url', $commentUrl); + } + + /** + * Get an array of acceptable ports this cookie can be used with + * + * @return array + */ + public function getPorts() + { + return $this->data['port']; + } + + /** + * Set a list of acceptable ports this cookie can be used with + * + * @param array $ports Array of acceptable ports + * + * @return Cookie + */ + public function setPorts(array $ports) + { + return $this->setData('port', $ports); + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['http_only']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return Cookie + */ + public function setHttpOnly($httpOnly) + { + return $this->setData('http_only', $httpOnly); + } + + /** + * Get an array of extra cookie data + * + * @return array + */ + public function getAttributes() + { + return $this->data['data']; + } + + /** + * Get a specific data point from the extra cookie data + * + * @param string $name Name of the data point to retrieve + * + * @return null|string + */ + public function getAttribute($name) + { + return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; + } + + /** + * Set a cookie data attribute + * + * @param string $name Name of the attribute to set + * @param string $value Value to set + * + * @return Cookie + */ + public function setAttribute($name, $value) + { + $this->data['data'][$name] = $value; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4 + // A request-path path-matches a given cookie-path if at least one of + // the following conditions holds: + + // o The cookie-path and the request-path are identical. + if ($path == $this->getPath()) { + return true; + } + + $pos = stripos($path, $this->getPath()); + if ($pos === 0) { + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + if (substr($this->getPath(), -1, 1) === "/") { + return true; + } + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + if (substr($path, strlen($this->getPath()), 1) === "/") { + return true; + } + } + + return false; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain); + } + + /** + * Check if the cookie is compatible with a specific port + * + * @param int $port Port to check + * + * @return bool + */ + public function matchesPort($port) + { + return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (strpbrk($name, self::getInvalidCharacters()) !== false) { + return 'The cookie name must not contain invalid characters: ' . $name; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name in a private network + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } + + /** + * Set a value and return the cookie object + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return Cookie + */ + private function setData($key, $value) + { + $this->data[$key] = $value; + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php new file mode 100644 index 0000000..6b67503 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php @@ -0,0 +1,237 @@ +strictMode = $strictMode; + } + + /** + * Enable or disable strict mode on the cookie jar + * + * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them. + * + * @return self + */ + public function setStrictMode($strictMode) + { + $this->strictMode = $strictMode; + } + + public function remove($domain = null, $path = null, $name = null) + { + $cookies = $this->all($domain, $path, $name, false, false); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { + return !in_array($cookie, $cookies, true); + }); + + return $this; + } + + public function removeTemporary() + { + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + }); + + return $this; + } + + public function removeExpired() + { + $currentTime = time(); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { + return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); + }); + + return $this; + } + + public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) + { + return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( + $domain, + $path, + $name, + $skipDiscardable, + $skipExpired + ) { + return false === (($name && $cookie->getName() != $name) || + ($skipExpired && $cookie->isExpired()) || + ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || + ($path && !$cookie->matchesPath($path)) || + ($domain && !$cookie->matchesDomain($domain))); + })); + } + + public function add(Cookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new InvalidCookieException($result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, domain, port and name are identical + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getPorts() != $cookie->getPorts() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + /** + * Serializes the cookie cookieJar + * + * @return string + */ + public function serialize() + { + // Only serialize long term cookies and unexpired cookies + return json_encode(array_map(function (Cookie $cookie) { + return $cookie->toArray(); + }, $this->all(null, null, null, true, true))); + } + + /** + * Unserializes the cookie cookieJar + */ + public function unserialize($data) + { + $data = json_decode($data, true); + if (empty($data)) { + $this->cookies = array(); + } else { + $this->cookies = array_map(function (array $cookie) { + return new Cookie($cookie); + }, $data); + } + } + + /** + * Returns the total number of stored cookies + * + * @return int + */ + public function count() + { + return count($this->cookies); + } + + /** + * Returns an iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + public function addCookiesFromResponse(Response $response, RequestInterface $request = null) + { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + $parser = ParserRegistry::getInstance()->getParser('cookie'); + foreach ($cookieHeader as $cookie) { + if ($parsed = $request + ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) + : $parser->parseCookie($cookie) + ) { + // Break up cookie v2 into multiple cookies + foreach ($parsed['cookies'] as $key => $value) { + $row = $parsed; + $row['name'] = $key; + $row['value'] = $value; + unset($row['cookies']); + $this->add(new Cookie($row)); + } + } + } + } + } + + public function getMatchingCookies(RequestInterface $request) + { + // Find cookies that match this request + $cookies = $this->all($request->getHost(), $request->getPath()); + // Remove ineligible cookies + foreach ($cookies as $index => $cookie) { + if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { + unset($cookies[$index]); + } + }; + + return $cookies; + } + + /** + * If a cookie already exists and the server asks to set it again with a null value, the + * cookie must be deleted. + * + * @param \Guzzle\Plugin\Cookie\Cookie $cookie + */ + private function removeCookieIfEmpty(Cookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php new file mode 100644 index 0000000..7faa7d2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php @@ -0,0 +1,85 @@ +filename = $cookieFile; + $this->load(); + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->persist(); + } + + /** + * Save the contents of the data array to the file + * + * @throws RuntimeException if the file cannot be found or created + */ + protected function persist() + { + if (false === file_put_contents($this->filename, $this->serialize())) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load the contents of the json formatted file into the data array and discard any unsaved state + */ + protected function load() + { + $json = file_get_contents($this->filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + + $this->unserialize($json); + $this->cookies = $this->cookies ?: array(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php new file mode 100644 index 0000000..df3210e --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php @@ -0,0 +1,70 @@ +cookieJar = $cookieJar ?: new ArrayCookieJar(); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', 125), + 'request.sent' => array('onRequestSent', 125) + ); + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Add cookies before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + if (!$request->getParams()->get('cookies.disable')) { + $request->removeHeader('Cookie'); + // Find cookies that match this request + foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { + $request->addCookie($cookie->getName(), $cookie->getValue()); + } + } + } + + /** + * Extract cookies from a sent request + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php new file mode 100644 index 0000000..b1fa6fd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cookie": "" } + }, + "target-dir": "Guzzle/Plugin/Cookie", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php new file mode 100644 index 0000000..610e60c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php @@ -0,0 +1,46 @@ +getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest'); + */ +class CurlAuthPlugin implements EventSubscriberInterface +{ + private $username; + private $password; + private $scheme; + + /** + * @param string $username HTTP basic auth username + * @param string $password Password + * @param int $scheme Curl auth scheme + */ + public function __construct($username, $password, $scheme=CURLAUTH_BASIC) + { + Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');"); + $this->username = $username; + $this->password = $password; + $this->scheme = $scheme; + } + + public static function getSubscribedEvents() + { + return array('client.create_request' => array('onRequestCreate', 255)); + } + + /** + * Add basic auth + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + $event['request']->setAuth($this->username, $this->password, $this->scheme); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json new file mode 100644 index 0000000..edc8b24 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-curlauth", + "description": "Guzzle cURL authorization plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "curl", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" } + }, + "target-dir": "Guzzle/Plugin/CurlAuth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php new file mode 100644 index 0000000..5dce8bd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php @@ -0,0 +1,22 @@ + array('onCommandBeforeSend', -1)); + } + + /** + * Adds a listener to requests before they sent from a command + * + * @param Event $event Event emitted + */ + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + if ($operation = $command->getOperation()) { + if ($operation->getErrorResponses()) { + $request = $command->getRequest(); + $request->getEventDispatcher() + ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation)); + } + } + } + + /** + * @param RequestInterface $request Request that received an error + * @param CommandInterface $command Command that created the request + * @param Operation $operation Operation that defines the request and errors + * + * @return \Closure Returns a closure + * @throws ErrorResponseException + */ + protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation) + { + return function (Event $event) use ($request, $command, $operation) { + $response = $event['response']; + foreach ($operation->getErrorResponses() as $error) { + if (!isset($error['class'])) { + continue; + } + if (isset($error['code']) && $response->getStatusCode() != $error['code']) { + continue; + } + if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) { + continue; + } + $className = $error['class']; + $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface'; + if (!class_exists($className)) { + throw new ErrorResponseException("{$className} does not exist"); + } elseif (!(in_array($errorClassInterface, class_implements($className)))) { + throw new ErrorResponseException("{$className} must implement {$errorClassInterface}"); + } + throw $className::fromCommand($command, $response); + } + }; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php new file mode 100644 index 0000000..1d89e40 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/service": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" } + }, + "target-dir": "Guzzle/Plugin/ErrorResponse", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php new file mode 100644 index 0000000..7375e89 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php @@ -0,0 +1,163 @@ + array('onRequestSent', 9999)); + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param Response $response Response of the request + * + * @return HistoryPlugin + */ + public function add(RequestInterface $request, Response $response = null) + { + if (!$response && $request->getResponse()) { + $response = $request->getResponse(); + } + + $this->transactions[] = array('request' => $request, 'response' => $response); + if (count($this->transactions) > $this->getlimit()) { + array_shift($this->transactions); + } + + return $this; + } + + /** + * Set the max number of requests to store + * + * @param int $limit Limit + * + * @return HistoryPlugin + */ + public function setLimit($limit) + { + $this->limit = (int) $limit; + + return $this; + } + + /** + * Get the request limit + * + * @return int + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Get all of the raw transactions in the form of an array of associative arrays containing + * 'request' and 'response' keys. + * + * @return array + */ + public function getAll() + { + return $this->transactions; + } + + /** + * Get the requests in the history + * + * @return \ArrayIterator + */ + public function getIterator() + { + // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll()) + return new \ArrayIterator(array_map(function ($entry) { + $entry['request']->getParams()->set('actual_response', $entry['response']); + return $entry['request']; + }, $this->transactions)); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + $last = end($this->transactions); + + return $last['request']; + } + + /** + * Get the last response in the history + * + * @return Response|null + */ + public function getLastResponse() + { + $last = end($this->transactions); + + return isset($last['response']) ? $last['response'] : null; + } + + /** + * Clears the history + * + * @return HistoryPlugin + */ + public function clear() + { + $this->transactions = array(); + + return $this; + } + + public function onRequestSent(Event $event) + { + $this->add($event['request'], $event['response']); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json new file mode 100644 index 0000000..ba0bf2c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-history", + "description": "Guzzle history plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\History": "" } + }, + "target-dir": "Guzzle/Plugin/History", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php new file mode 100644 index 0000000..cabdea8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php @@ -0,0 +1,161 @@ +logAdapter = $logAdapter; + $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter); + $this->wireBodies = $wireBodies; + } + + /** + * Get a log plugin that outputs full request, response, and curl error information to stderr + * + * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable + * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available + * + * @return self + */ + public static function getDebugPlugin($wireBodies = true, $stream = null) + { + if ($stream === null) { + if (defined('STDERR')) { + $stream = STDERR; + } else { + $stream = fopen('php://output', 'w'); + } + } + + return new self(new ClosureLogAdapter(function ($m) use ($stream) { + fwrite($stream, $m . PHP_EOL); + }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies); + } + + public static function getSubscribedEvents() + { + return array( + 'curl.callback.write' => array('onCurlWrite', 255), + 'curl.callback.read' => array('onCurlRead', 255), + 'request.before_send' => array('onRequestBeforeSend', 255), + 'request.sent' => array('onRequestSent', 255) + ); + } + + /** + * Event triggered when curl data is read from a request + * + * @param Event $event + */ + public function onCurlRead(Event $event) + { + // Stream the request body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('request_wire')) { + $wire->write($event['read']); + } + } + + /** + * Event triggered when curl data is written to a response + * + * @param Event $event + */ + public function onCurlWrite(Event $event) + { + // Stream the response body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('response_wire')) { + $wire->write($event['write']); + } + } + + /** + * Called before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + if ($this->wireBodies) { + $request = $event['request']; + // Ensure that curl IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + // We need to make special handling for content wiring and non-repeatable streams. + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable()) + ) { + // The body of the request cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('request_wire', EntityBody::factory()); + } + if (!$request->getResponseBody()->isRepeatable()) { + // The body of the response cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('response_wire', EntityBody::factory()); + } + } + } + + /** + * Triggers the actual log write when a request completes + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $handle = $event['handle']; + + if ($wire = $request->getParams()->get('request_wire')) { + $request = clone $request; + $request->setBody($wire); + } + + if ($wire = $request->getParams()->get('response_wire')) { + $response = clone $response; + $response->setBody($wire); + } + + // Send the log message to the adapter, adding a category and host + $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG; + $message = $this->formatter->format($request, $response, $handle); + $this->logAdapter->log($message, $priority, array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle + )); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json new file mode 100644 index 0000000..130e6da --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-log", + "description": "Guzzle log plugin for over the wire logging", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "log", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Log": "" } + }, + "target-dir": "Guzzle/Plugin/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php new file mode 100644 index 0000000..8512424 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php @@ -0,0 +1,57 @@ +contentMd5Param = $contentMd5Param; + $this->validateMd5Param = $validateMd5Param; + } + + public static function getSubscribedEvents() + { + return array('command.before_send' => array('onCommandBeforeSend', -255)); + } + + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + $request = $command->getRequest(); + + // Only add an MD5 is there is a MD5 option on the operation and it has a payload + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && $command->getOperation()->hasParam($this->contentMd5Param)) { + // Check if an MD5 checksum value should be passed along to the request + if ($command[$this->contentMd5Param] === true) { + if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) { + $request->setHeader('Content-MD5', $md5); + } + } + } + + // Check if MD5 validation should be used with the response + if ($command[$this->validateMd5Param] === true) { + $request->addSubscriber(new Md5ValidatorPlugin(true, false)); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php new file mode 100644 index 0000000..5d7a378 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php @@ -0,0 +1,88 @@ +contentLengthCutoff = $contentLengthCutoff; + $this->contentEncoded = $contentEncoded; + } + + public static function getSubscribedEvents() + { + return array('request.complete' => array('onRequestComplete', 255)); + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException + */ + public function onRequestComplete(Event $event) + { + $response = $event['response']; + + if (!$contentMd5 = $response->getContentMd5()) { + return; + } + + $contentEncoding = $response->getContentEncoding(); + if ($contentEncoding && !$this->contentEncoded) { + return false; + } + + // Make sure that the size of the request is under the cutoff size + if ($this->contentLengthCutoff) { + $size = $response->getContentLength() ?: $response->getBody()->getSize(); + if (!$size || $size > $this->contentLengthCutoff) { + return; + } + } + + if (!$contentEncoding) { + $hash = $response->getBody()->getContentMd5(); + } elseif ($contentEncoding == 'gzip') { + $response->getBody()->compress('zlib.deflate'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } elseif ($contentEncoding == 'compress') { + $response->getBody()->compress('bzip2.compress'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } else { + return; + } + + if ($contentMd5 !== $hash) { + throw new UnexpectedValueException( + "The response entity body may have been modified over the wire. The Content-MD5 " + . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})." + ); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json new file mode 100644 index 0000000..0602d06 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-md5", + "description": "Guzzle MD5 plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Md5": "" } + }, + "target-dir": "Guzzle/Plugin/Md5", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php new file mode 100644 index 0000000..2440578 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php @@ -0,0 +1,245 @@ +readBodies = $readBodies; + $this->temporary = $temporary; + if ($items) { + foreach ($items as $item) { + if ($item instanceof \Exception) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + } + + public static function getSubscribedEvents() + { + // Use a number lower than the CachePlugin + return array('request.before_send' => array('onRequestBeforeSend', -999)); + } + + public static function getAllEvents() + { + return array('mock.request'); + } + + /** + * Get a mock response from a file + * + * @param string $path File to retrieve a mock response from + * + * @return Response + * @throws InvalidArgumentException if the file is not found + */ + public static function getMockFile($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentException('Unable to open mock file: ' . $path); + } + + return Response::fromMessage(file_get_contents($path)); + } + + /** + * Set whether or not to consume the entity body of a request when a mock + * response is used + * + * @param bool $readBodies Set to true to read and consume entity bodies + * + * @return self + */ + public function readBodies($readBodies) + { + $this->readBodies = $readBodies; + + return $this; + } + + /** + * Returns the number of remaining mock responses + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|Response $response Response object or path to response file + * + * @return MockPlugin + * @throws InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (!($response instanceof Response)) { + if (!is_string($response)) { + throw new InvalidArgumentException('Invalid response'); + } + $response = self::getMockFile($response); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param CurlException $e Exception to throw when the request is executed + * + * @return MockPlugin + */ + public function addException(CurlException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Clear the queue + * + * @return MockPlugin + */ + public function clearQueue() + { + $this->queue = array(); + + return $this; + } + + /** + * Returns an array of mock responses remaining in the queue + * + * @return array + */ + public function getQueue() + { + return $this->queue; + } + + /** + * Check if this is a temporary plugin + * + * @return bool + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Get a response from the front of the list and add it to a request + * + * @param RequestInterface $request Request to mock + * + * @return self + * @throws CurlException When request.send is called and an exception is queued + */ + public function dequeue(RequestInterface $request) + { + $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request)); + + $item = array_shift($this->queue); + if ($item instanceof Response) { + if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) { + $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) { + while ($data = $event['request']->getBody()->read(8096)); + // Remove the listener after one-time use + $event['request']->getEventDispatcher()->removeListener('request.sent', $f); + }); + } + $request->setResponse($item); + } elseif ($item instanceof CurlException) { + // Emulates exceptions encountered while transferring requests + $item->setRequest($request); + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item)); + // Only throw if the exception wasn't handled + if ($state == RequestInterface::STATE_ERROR) { + throw $item; + } + } + + return $this; + } + + /** + * Clear the array of received requests + */ + public function flush() + { + $this->received = array(); + } + + /** + * Get an array of requests that were mocked by this plugin + * + * @return array + */ + public function getReceivedRequests() + { + return $this->received; + } + + /** + * Called when a request is about to be sent + * + * @param Event $event + * @throws \OutOfBoundsException When queue is empty + */ + public function onRequestBeforeSend(Event $event) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + $request = $event['request']; + $this->received[] = $request; + // Detach the filter from the client so it's a one-time use + if ($this->temporary && count($this->queue) == 1 && $request->getClient()) { + $request->getClient()->getEventDispatcher()->removeSubscriber($this); + } + $this->dequeue($request); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json new file mode 100644 index 0000000..f8201e3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-mock", + "description": "Guzzle Mock plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["mock", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Mock": "" } + }, + "target-dir": "Guzzle/Plugin/Mock", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php new file mode 100644 index 0000000..95e0c3e --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php @@ -0,0 +1,306 @@ +config = Collection::fromConfig($config, array( + 'version' => '1.0', + 'request_method' => self::REQUEST_METHOD_HEADER, + 'consumer_key' => 'anonymous', + 'consumer_secret' => 'anonymous', + 'signature_method' => 'HMAC-SHA1', + 'signature_callback' => function($stringToSign, $key) { + return hash_hmac('sha1', $stringToSign, $key, true); + } + ), array( + 'signature_method', 'signature_callback', 'version', + 'consumer_key', 'consumer_secret' + )); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -1000) + ); + } + + /** + * Request before-send event handler + * + * @param Event $event Event received + * @return array + * @throws \InvalidArgumentException + */ + public function onRequestBeforeSend(Event $event) + { + $timestamp = $this->getTimestamp($event); + $request = $event['request']; + $nonce = $this->generateNonce($request); + $authorizationParams = $this->getOauthParams($timestamp, $nonce); + $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce); + + switch ($this->config['request_method']) { + case self::REQUEST_METHOD_HEADER: + $request->setHeader( + 'Authorization', + $this->buildAuthorizationHeader($authorizationParams) + ); + break; + case self::REQUEST_METHOD_QUERY: + foreach ($authorizationParams as $key => $value) { + $request->getQuery()->set($key, $value); + } + break; + default: + throw new \InvalidArgumentException(sprintf( + 'Invalid consumer method "%s"', + $this->config['request_method'] + )); + } + + return $authorizationParams; + } + + /** + * Builds the Authorization header for a request + * + * @param array $authorizationParams Associative array of authorization parameters + * + * @return string + */ + private function buildAuthorizationHeader($authorizationParams) + { + $authorizationString = 'OAuth '; + foreach ($authorizationParams as $key => $val) { + if ($val) { + $authorizationString .= $key . '="' . urlencode($val) . '", '; + } + } + + return substr($authorizationString, 0, -2); + } + + /** + * Calculate signature for request + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getSignature(RequestInterface $request, $timestamp, $nonce) + { + $string = $this->getStringToSign($request, $timestamp, $nonce); + $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); + + return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); + } + + /** + * Calculate string to sign + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getStringToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getParamsToSign($request, $timestamp, $nonce); + + // Convert booleans to strings. + $params = $this->prepareParameters($params); + + // Build signing string from combined params + $parameterString = clone $request->getQuery(); + $parameterString->replace($params); + + $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); + + return strtoupper($request->getMethod()) . '&' + . rawurlencode($url) . '&' + . rawurlencode((string) $parameterString); + } + + /** + * Get the oauth parameters as named by the oauth spec + * + * @param $timestamp + * @param $nonce + * @return Collection + */ + protected function getOauthParams($timestamp, $nonce) + { + $params = new Collection(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $nonce, + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + )); + + // Optional parameters should not be set if they have not been set in the config as + // the parameter may be considered invalid by the Oauth service. + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'version' => 'oauth_version' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + if (isset($this->config[$optionName]) == true) { + $params[$oauthName] = $this->config[$optionName]; + } + } + + return $params; + } + + /** + * Get all of the parameters required to sign a request including: + * * The oauth params + * * The request GET params + * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded) + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return array + */ + public function getParamsToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getOauthParams($timestamp, $nonce); + + // Add query string parameters + $params->merge($request->getQuery()); + + // Add POST fields to signing string if required + if ($this->shouldPostFieldsBeSigned($request)) + { + $params->merge($request->getPostFields()); + } + + // Sort params + $params = $params->toArray(); + uksort($params, 'strcmp'); + + return $params; + } + + /** + * Decide whether the post fields should be added to the base string that Oauth signs. + * This implementation is correct. Non-conformant APIs may require that this method be + * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type + * is 'application/x-www-form-urlencoded' + * + * @param $request + * @return bool Whether the post fields should be signed or not + */ + public function shouldPostFieldsBeSigned($request) + { + if (!$this->config->get('disable_post_params') && + $request instanceof EntityEnclosingRequestInterface && + false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) + { + return true; + } + + return false; + } + + /** + * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same + * exact timestamp to use separate nonce's. + * + * @param RequestInterface $request Request to generate a nonce for + * + * @return string + */ + public function generateNonce(RequestInterface $request) + { + return sha1(uniqid('', true) . $request->getUrl()); + } + + /** + * Gets timestamp from event or create new timestamp + * + * @param Event $event Event containing contextual information + * + * @return int + */ + public function getTimestamp(Event $event) + { + return $event['timestamp'] ?: time(); + } + + /** + * Convert booleans to strings, removed unset parameters, and sorts the array + * + * @param array $data Data array + * + * @return array + */ + protected function prepareParameters($data) + { + ksort($data); + foreach ($data as $key => &$value) { + switch (gettype($value)) { + case 'NULL': + unset($data[$key]); + break; + case 'array': + $data[$key] = self::prepareParameters($value); + break; + case 'boolean': + $data[$key] = $value ? 'true' : 'false'; + break; + } + } + + return $data; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json new file mode 100644 index 0000000..c9766ba --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-oauth", + "description": "Guzzle OAuth plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["oauth", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Oauth": "" } + }, + "target-dir": "Guzzle/Plugin/Oauth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json new file mode 100644 index 0000000..2bbe64c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Plugin/composer.json @@ -0,0 +1,44 @@ +{ + "name": "guzzle/plugin", + "description": "Guzzle plugin component containing all Guzzle HTTP plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["http", "client", "plugin", "extension", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "suggest": { + "guzzle/cache": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin": "" } + }, + "target-dir": "Guzzle/Plugin", + "replace": { + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version" + }, + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php new file mode 100644 index 0000000..cd06f57 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php @@ -0,0 +1,177 @@ + 'JSON_ERROR_NONE - No errors', + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ); + + public function load($config, array $options = array()) + { + // Reset the array of loaded files because this is a new config + $this->loadedFiles = array(); + + if (is_string($config)) { + $config = $this->loadFile($config); + } elseif (!is_array($config)) { + throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); + } else { + $this->mergeIncludes($config); + } + + return $this->build($config, $options); + } + + /** + * Add an include alias to the loader + * + * @param string $filename Filename to alias (e.g. _foo) + * @param string $alias Actual file to use (e.g. /path/to/foo.json) + * + * @return self + */ + public function addAlias($filename, $alias) + { + $this->aliases[$filename] = $alias; + + return $this; + } + + /** + * Remove an alias from the loader + * + * @param string $alias Alias to remove + * + * @return self + */ + public function removeAlias($alias) + { + unset($this->aliases[$alias]); + + return $this; + } + + /** + * Perform the parsing of a config file and create the end result + * + * @param array $config Configuration data + * @param array $options Options to use when building + * + * @return mixed + */ + protected abstract function build($config, array $options); + + /** + * Load a configuration file (can load JSON or PHP files that return an array when included) + * + * @param string $filename File to load + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException when the JSON cannot be parsed + */ + protected function loadFile($filename) + { + if (isset($this->aliases[$filename])) { + $filename = $this->aliases[$filename]; + } + + switch (pathinfo($filename, PATHINFO_EXTENSION)) { + case 'js': + case 'json': + $level = error_reporting(0); + $json = file_get_contents($filename); + error_reporting($level); + + if ($json === false) { + $err = error_get_last(); + throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']); + } + + $config = json_decode($json, true); + // Throw an exception if there was an error loading the file + if ($error = json_last_error()) { + $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error'; + throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}"); + } + break; + case 'php': + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + $config = require $filename; + if (!is_array($config)) { + throw new InvalidArgumentException('PHP files must return an array of configuration data'); + } + break; + default: + throw new InvalidArgumentException('Unknown file extension: ' . $filename); + } + + // Keep track of this file being loaded to prevent infinite recursion + $this->loadedFiles[$filename] = true; + + // Merge include files into the configuration array + $this->mergeIncludes($config, dirname($filename)); + + return $config; + } + + /** + * Merges in all include files + * + * @param array $config Config data that contains includes + * @param string $basePath Base path to use when a relative path is encountered + * + * @return array Returns the merged and included data + */ + protected function mergeIncludes(&$config, $basePath = null) + { + if (!empty($config['includes'])) { + foreach ($config['includes'] as &$path) { + // Account for relative paths + if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { + $path = "{$basePath}/{$path}"; + } + // Don't load the same files more than once + if (!isset($this->loadedFiles[$path])) { + $this->loadedFiles[$path] = true; + $config = $this->mergeData($this->loadFile($path), $config); + } + } + } + } + + /** + * Default implementation for merging two arrays of data (uses array_merge_recursive) + * + * @param array $a Original data + * @param array $b Data to merge into the original and overwrite existing values + * + * @return array + */ + protected function mergeData(array $a, array $b) + { + return array_merge_recursive($a, $b); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php new file mode 100644 index 0000000..38150db --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -0,0 +1,189 @@ +load($config, $globalParameters); + } + + /** + * @param array $serviceBuilderConfig Service configuration settings: + * - name: Name of the service + * - class: Client class to instantiate using a factory method + * - params: array of key value pair configuration settings for the builder + */ + public function __construct(array $serviceBuilderConfig = array()) + { + $this->builderConfig = $serviceBuilderConfig; + } + + public static function getAllEvents() + { + return array('service_builder.create_client'); + } + + public function unserialize($serialized) + { + $this->builderConfig = json_decode($serialized, true); + } + + public function serialize() + { + return json_encode($this->builderConfig); + } + + /** + * Attach a plugin to every client created by the builder + * + * @param EventSubscriberInterface $plugin Plugin to attach to each client + * + * @return self + */ + public function addGlobalPlugin(EventSubscriberInterface $plugin) + { + $this->plugins[] = $plugin; + + return $this; + } + + /** + * Get data from the service builder without triggering the building of a service + * + * @param string $name Name of the service to retrieve + * + * @return array|null + */ + public function getData($name) + { + return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null; + } + + public function get($name, $throwAway = false) + { + if (!isset($this->builderConfig[$name])) { + + // Check to see if arbitrary data is being referenced + if (isset($this->clients[$name])) { + return $this->clients[$name]; + } + + // Check aliases and return a match if found + foreach ($this->builderConfig as $actualName => $config) { + if (isset($config['alias']) && $config['alias'] == $name) { + return $this->get($actualName, $throwAway); + } + } + throw new ServiceNotFoundException('No service is registered as ' . $name); + } + + if (!$throwAway && isset($this->clients[$name])) { + return $this->clients[$name]; + } + + $builder =& $this->builderConfig[$name]; + + // Convert references to the actual client + foreach ($builder['params'] as &$v) { + if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') { + $v = $this->get(trim($v, '{} ')); + } + } + + // Get the configured parameters and merge in any parameters provided for throw-away clients + $config = $builder['params']; + if (is_array($throwAway)) { + $config = $throwAway + $config; + } + + $client = $builder['class']::factory($config); + + if (!$throwAway) { + $this->clients[$name] = $client; + } + + if ($client instanceof ClientInterface) { + foreach ($this->plugins as $plugin) { + $client->addSubscriber($plugin); + } + // Dispatch an event letting listeners know a client was created + $this->dispatch('service_builder.create_client', array('client' => $client)); + } + + return $client; + } + + public function set($key, $service) + { + if (is_array($service) && isset($service['class']) && isset($service['params'])) { + $this->builderConfig[$key] = $service; + } else { + $this->clients[$key] = $service; + } + + return $this; + } + + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->builderConfig[$offset]); + unset($this->clients[$offset]); + } + + public function offsetExists($offset) + { + return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]); + } + + public function offsetGet($offset) + { + return $this->get($offset); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php new file mode 100644 index 0000000..4fc310a --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php @@ -0,0 +1,40 @@ + &$service) { + + $service['params'] = isset($service['params']) ? $service['params'] : array(); + + // Check if this client builder extends another client + if (!empty($service['extends'])) { + + // Make sure that the service it's extending has been defined + if (!isset($services[$service['extends']])) { + throw new ServiceNotFoundException( + "{$name} is trying to extend a non-existent service: {$service['extends']}" + ); + } + + $extended = &$services[$service['extends']]; + + // Use the correct class attribute + if (empty($service['class'])) { + $service['class'] = isset($extended['class']) ? $extended['class'] : ''; + } + if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) { + $service['params'] = $service['params'] + $extendsParams; + } + } + + // Overwrite default values with global parameter values + if (!empty($options)) { + $service['params'] = $options + $service['params']; + } + + $service['class'] = isset($service['class']) ? $service['class'] : ''; + } + + return new $class($services); + } + + protected function mergeData(array $a, array $b) + { + $result = $b + $a; + + // Merge services using a recursive union of arrays + if (isset($a['services']) && $b['services']) { + + // Get a union of the services of the two arrays + $result['services'] = $b['services'] + $a['services']; + + // Merge each service in using a union of the two arrays + foreach ($result['services'] as $name => &$service) { + + // By default, services completely override a previously defined service unless it extends itself + if (isset($a['services'][$name]['extends']) + && isset($b['services'][$name]['extends']) + && $b['services'][$name]['extends'] == $name + ) { + $service += $a['services'][$name]; + // Use the `extends` attribute of the parent + $service['extends'] = $a['services'][$name]['extends']; + // Merge parameters using a union if both have parameters + if (isset($a['services'][$name]['params'])) { + $service['params'] += $a['services'][$name]['params']; + } + } + } + } + + return $result; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php new file mode 100644 index 0000000..26f8360 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php @@ -0,0 +1,46 @@ +loader = $loader; + $this->cache = $cache; + } + + public function load($config, array $options = array()) + { + if (!is_string($config)) { + $key = false; + } else { + $key = 'loader_' . crc32($config); + if ($result = $this->cache->fetch($key)) { + return $result; + } + } + + $result = $this->loader->load($config, $options); + if ($key) { + $this->cache->save($key, $result); + } + + return $result; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php new file mode 100644 index 0000000..3e5f8e5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php @@ -0,0 +1,297 @@ +getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); + } + + public function getCommand($name, array $args = array()) + { + // Add global client options to the command + if ($options = $this->getConfig(self::COMMAND_PARAMS)) { + $args += $options; + } + + if (!($command = $this->getCommandFactory()->factory($name, $args))) { + throw new InvalidArgumentException("Command was not found matching {$name}"); + } + + $command->setClient($this); + $this->dispatch('client.command.create', array('client' => $this, 'command' => $command)); + + return $command; + } + + /** + * Set the command factory used to create commands by name + * + * @param CommandFactoryInterface $factory Command factory + * + * @return self + */ + public function setCommandFactory(CommandFactoryInterface $factory) + { + $this->commandFactory = $factory; + + return $this; + } + + /** + * Set the resource iterator factory associated with the client + * + * @param ResourceIteratorFactoryInterface $factory Resource iterator factory + * + * @return self + */ + public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) + { + $this->resourceIteratorFactory = $factory; + + return $this; + } + + public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) + { + if (!($command instanceof CommandInterface)) { + $command = $this->getCommand($command, $commandOptions ?: array()); + } + + return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); + } + + public function execute($command) + { + if ($command instanceof CommandInterface) { + $this->send($this->prepareCommand($command)); + $this->dispatch('command.after_send', array('command' => $command)); + return $command->getResult(); + } elseif (is_array($command) || $command instanceof \Traversable) { + return $this->executeMultiple($command); + } else { + throw new InvalidArgumentException('Command must be a command or array of commands'); + } + } + + public function setDescription(ServiceDescriptionInterface $service) + { + $this->serviceDescription = $service; + + if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) { + $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service)); + } + + // If a baseUrl was set on the description, then update the client + if ($baseUrl = $service->getBaseUrl()) { + $this->setBaseUrl($baseUrl); + } + + return $this; + } + + public function getDescription() + { + return $this->serviceDescription; + } + + /** + * Set the inflector used with the client + * + * @param InflectorInterface $inflector Inflection object + * + * @return self + */ + public function setInflector(InflectorInterface $inflector) + { + $this->inflector = $inflector; + + return $this; + } + + /** + * Get the inflector used with the client + * + * @return self + */ + public function getInflector() + { + if (!$this->inflector) { + $this->inflector = Inflector::getDefault(); + } + + return $this->inflector; + } + + /** + * Prepare a command for sending and get the RequestInterface object created by the command + * + * @param CommandInterface $command Command to prepare + * + * @return RequestInterface + */ + protected function prepareCommand(CommandInterface $command) + { + // Set the client and prepare the command + $request = $command->setClient($this)->prepare(); + // Set the state to new if the command was previously executed + $request->setState(RequestInterface::STATE_NEW); + $this->dispatch('command.before_send', array('command' => $command)); + + return $request; + } + + /** + * Execute multiple commands in parallel + * + * @param array|Traversable $commands Array of CommandInterface objects to execute + * + * @return array Returns an array of the executed commands + * @throws Exception\CommandTransferException + */ + protected function executeMultiple($commands) + { + $requests = array(); + $commandRequests = new \SplObjectStorage(); + + foreach ($commands as $command) { + $request = $this->prepareCommand($command); + $commandRequests[$request] = $command; + $requests[] = $request; + } + + try { + $this->send($requests); + foreach ($commands as $command) { + $this->dispatch('command.after_send', array('command' => $command)); + } + return $commands; + } catch (MultiTransferException $failureException) { + // Throw a CommandTransferException using the successful and failed commands + $e = CommandTransferException::fromMultiTransferException($failureException); + + // Remove failed requests from the successful requests array and add to the failures array + foreach ($failureException->getFailedRequests() as $request) { + if (isset($commandRequests[$request])) { + $e->addFailedCommand($commandRequests[$request]); + unset($commandRequests[$request]); + } + } + + // Always emit the command after_send events for successful commands + foreach ($commandRequests as $success) { + $e->addSuccessfulCommand($commandRequests[$success]); + $this->dispatch('command.after_send', array('command' => $commandRequests[$success])); + } + + throw $e; + } + } + + protected function getResourceIteratorFactory() + { + if (!$this->resourceIteratorFactory) { + // Build the default resource iterator factory if one is not set + $clientClass = get_class($this); + $prefix = substr($clientClass, 0, strrpos($clientClass, '\\')); + $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array( + "{$prefix}\\Iterator", + "{$prefix}\\Model" + )); + } + + return $this->resourceIteratorFactory; + } + + /** + * Get the command factory associated with the client + * + * @return CommandFactoryInterface + */ + protected function getCommandFactory() + { + if (!$this->commandFactory) { + $this->commandFactory = CompositeFactory::getDefaultChain($this); + } + + return $this->commandFactory; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function enableMagicMethods($isEnabled) + { + Version::warn(__METHOD__ . ' is deprecated'); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php new file mode 100644 index 0000000..814154f --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php @@ -0,0 +1,68 @@ +operation = $operation ?: $this->createOperation(); + foreach ($this->operation->getParams() as $name => $arg) { + $currentValue = $this[$name]; + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be updated on the config object + if ($currentValue !== $configValue) { + $this[$name] = $configValue; + } + } + + $headers = $this[self::HEADERS_OPTION]; + if (!$headers instanceof Collection) { + $this[self::HEADERS_OPTION] = new Collection((array) $headers); + } + + // You can set a command.on_complete option in your parameters to set an onComplete callback + if ($onComplete = $this['command.on_complete']) { + unset($this['command.on_complete']); + $this->setOnComplete($onComplete); + } + + // Set the hidden additional parameters + if (!$this[self::HIDDEN_PARAMS]) { + $this[self::HIDDEN_PARAMS] = array( + self::HEADERS_OPTION, + self::RESPONSE_PROCESSING, + self::HIDDEN_PARAMS, + self::REQUEST_OPTIONS + ); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Execute the command in the same manner as calling a function + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + */ + public function __invoke() + { + return $this->execute(); + } + + public function getName() + { + return $this->operation->getName(); + } + + /** + * Get the API command information about the command + * + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + public function execute() + { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be executed.'); + } + + return $this->client->execute($this); + } + + public function getClient() + { + return $this->client; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + public function getResponse() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + return $this->request->getResponse(); + } + + public function getResult() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + public function isPrepared() + { + return $this->request !== null; + } + + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be prepared.'); + } + + // If no response processing value was specified, then attempt to use the highest level of processing + if (!isset($this[self::RESPONSE_PROCESSING])) { + $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; + } + + // Notify subscribers of the client that the command is being prepared + $this->client->dispatch('command.before_prepare', array('command' => $this)); + + // Fail on missing required arguments, and change parameters via filters + $this->validate(); + // Delegate to the subclass that implements the build method + $this->build(); + + // Add custom request headers set on the command + if ($headers = $this[self::HEADERS_OPTION]) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + + // Add any curl options to the request + if ($options = $this[Client::CURL_OPTIONS]) { + $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); + } + + // Set a custom response body + if ($responseBody = $this[self::RESPONSE_BODY]) { + $this->request->setResponseBody($responseBody); + } + + $this->client->dispatch('command.after_prepare', array('command' => $this)); + } + + return $this->request; + } + + /** + * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is + * set, then the command will validate using the default {@see SchemaValidator}. + * + * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema + * + * @return self + */ + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + + return $this; + } + + public function getRequestHeaders() + { + return $this[self::HEADERS_OPTION]; + } + + /** + * Initialize the command (hook that can be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Hook used to create an operation for concrete commands that are not associated with a service description + * + * @return OperationInterface + */ + protected function createOperation() + { + return new Operation(array('name' => get_class($this))); + } + + /** + * Create the result of the command after the request has been completed. + * Override this method in subclasses to customize this behavior + */ + protected function process() + { + $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW + ? DefaultResponseParser::getInstance()->parse($this) + : $this->request->getResponse(); + } + + /** + * Validate and prepare the command based on the schema and rules defined by the command's Operation object + * + * @throws ValidationException when validation errors occur + */ + protected function validate() + { + // Do not perform request validation/transformation if it is disable + if ($this[self::DISABLE_VALIDATION]) { + return; + } + + $errors = array(); + $validator = $this->getValidator(); + foreach ($this->operation->getParams() as $name => $schema) { + $value = $this[$name]; + if (!$validator->validate($schema, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + // Update the config value if it changed and no validation errors were encountered + $this->data[$name] = $value; + } + } + + // Validate additional parameters + $hidden = $this[self::HIDDEN_PARAMS]; + + if ($properties = $this->operation->getAdditionalParameters()) { + foreach ($this->toArray() as $name => $value) { + // It's only additional if it isn't defined in the schema + if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { + // Always set the name so that error messages are useful + $properties->setName($name); + if (!$validator->validate($properties, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + $this->data[$name] = $value; + } + } + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } + + /** + * Get the validator used to prepare and validate properties. If no validator has been set on the command, then + * the default {@see SchemaValidator} will be used. + * + * @return ValidatorInterface + */ + protected function getValidator() + { + if (!$this->validator) { + $this->validator = SchemaValidator::getInstance(); + } + + return $this->validator; + } + + /** + * Get array of any validation errors + * If no validator has been set then return false + */ + public function getValidationErrors() + { + if (!$this->validator) { + return false; + } + + return $this->validator->getErrors(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100644 index 0000000..cb6ac40 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,41 @@ +request = $closure($this, $this->operation); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100644 index 0000000..fbb61d2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,128 @@ +stopPropagation(); + } + + /** + * Get the created object + * + * @return mixed + */ + public function getResult() + { + return $this['result']; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php new file mode 100644 index 0000000..2dc4acd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php @@ -0,0 +1,169 @@ +factory = $factory; + } + + /** + * Add a location visitor to the serializer + * + * @param string $location Location to associate with the visitor + * @param RequestVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, RequestVisitorInterface $visitor) + { + $this->factory->addRequestVisitor($location, $visitor); + + return $this; + } + + public function prepare(CommandInterface $command) + { + $request = $this->createRequest($command); + // Keep an array of visitors found in the operation + $foundVisitors = array(); + $operation = $command->getOperation(); + + // Add arguments to the request using the location attribute + foreach ($operation->getParams() as $name => $arg) { + /** @var $arg \Guzzle\Service\Description\Parameter */ + $location = $arg->getLocation(); + // Skip 'uri' locations because they've already been processed + if ($location && $location != 'uri') { + // Instantiate visitors as they are detected in the properties + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getRequestVisitor($location); + } + // Ensure that a value has been set for this parameter + $value = $command[$name]; + if ($value !== null) { + // Apply the parameter value with the location visitor + $foundVisitors[$location]->visit($command, $request, $arg, $value); + } + } + } + + // Serialize additional parameters + if ($additional = $operation->getAdditionalParameters()) { + if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { + $foundVisitors[$additional->getLocation()] = $visitor; + } + } + + // Call the after method on each visitor found in the operation + foreach ($foundVisitors as $visitor) { + $visitor->after($command, $request); + } + + return $request; + } + + /** + * Serialize additional parameters + * + * @param OperationInterface $operation Operation that owns the command + * @param CommandInterface $command Command to prepare + * @param RequestInterface $request Request to serialize + * @param Parameter $additional Additional parameters + * + * @return null|RequestVisitorInterface + */ + protected function prepareAdditionalParameters( + OperationInterface $operation, + CommandInterface $command, + RequestInterface $request, + Parameter $additional + ) { + if (!($location = $additional->getLocation())) { + return; + } + + $visitor = $this->factory->getRequestVisitor($location); + $hidden = $command[$command::HIDDEN_PARAMS]; + + foreach ($command->toArray() as $key => $value) { + // Ignore values that are null or built-in command options + if ($value !== null + && !in_array($key, $hidden) + && !$operation->hasParam($key) + ) { + $additional->setName($key); + $visitor->visit($command, $request, $additional, $value); + } + } + + return $visitor; + } + + /** + * Create a request for the command and operation + * + * @param CommandInterface $command Command to create a request for + * + * @return RequestInterface + */ + protected function createRequest(CommandInterface $command) + { + $operation = $command->getOperation(); + $client = $command->getClient(); + $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); + + // If the command does not specify a template, then assume the base URL of the client + if (!($uri = $operation->getUri())) { + return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); + } + + // Get the path values and use the client config settings + $variables = array(); + foreach ($operation->getParams() as $name => $arg) { + if ($arg->getLocation() == 'uri') { + if (isset($command[$name])) { + $variables[$name] = $arg->filter($command[$name]); + if (!is_array($variables[$name])) { + $variables[$name] = (string) $variables[$name]; + } + } + } + } + + return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php new file mode 100644 index 0000000..4fe3803 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php @@ -0,0 +1,55 @@ +getRequest()->getResponse(); + + // Account for hard coded content-type values specified in service descriptions + if ($contentType = $command['command.expects']) { + $response->setHeader('Content-Type', $contentType); + } else { + $contentType = (string) $response->getHeader('Content-Type'); + } + + return $this->handleParsing($command, $response, $contentType); + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $result = $response; + if ($result->getBody()) { + if (stripos($contentType, 'json') !== false) { + $result = $result->json(); + } elseif (stripos($contentType, 'xml') !== false) { + $result = $result->xml(); + } + } + + return $result; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100644 index 0000000..1c5ce07 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,39 @@ +client = $client; + $this->aliases = $aliases; + } + + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100644 index 0000000..8c46983 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,154 @@ +getDescription()) { + $factories[] = new ServiceDescriptionFactory($description); + } + $factories[] = new ConcreteClassFactory($client); + + return new self($factories); + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object + * matching a class name. + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + public function count() + { + return count($this->factories); + } + + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100644 index 0000000..0e93dea --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,47 @@ +client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the current client and the default directory + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100644 index 0000000..35c299d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +map = $map; + } + + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100644 index 0000000..b943a5b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,71 @@ +setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescriptionInterface $description Service description to use + * + * @return FactoryInterface + */ + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescriptionInterface + */ + public function getServiceDescription() + { + return $this->description; + } + + public function factory($name, array $args = array()) + { + $command = $this->description->getOperation($name); + + // If a command wasn't found, then try to uppercase the first letter and try again + if (!$command) { + $command = $this->description->getOperation(ucfirst($name)); + // If an inflector was passed, then attempt to get the command using snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getOperation($this->inflector->snake($name)); + } + } + + if ($command) { + $class = $command->getClass(); + return new $class($args, $command, $this->description); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php new file mode 100644 index 0000000..adcfca1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php @@ -0,0 +1,69 @@ +resolveRecursively($value, $param) + : $param->filter($value); + } + + /** + * Map nested parameters into the location_key based parameters + * + * @param array $value Value to map + * @param Parameter $param Parameter that holds information about the current key + * + * @return array Returns the mapped array + */ + protected function resolveRecursively(array $value, Parameter $param) + { + foreach ($value as $name => &$v) { + switch ($param->getType()) { + case 'object': + if ($subParam = $param->getProperty($name)) { + $key = $subParam->getWireName(); + $value[$key] = $this->prepareValue($v, $subParam); + if ($name != $key) { + unset($value[$name]); + } + } elseif ($param->getAdditionalProperties() instanceof Parameter) { + $v = $this->prepareValue($v, $param->getAdditionalProperties()); + } + break; + case 'array': + if ($items = $param->getItems()) { + $v = $this->prepareValue($v, $items); + } + break; + } + } + + return $param->filter($value); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php new file mode 100644 index 0000000..168d780 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php @@ -0,0 +1,58 @@ +filter($value); + $entityBody = EntityBody::factory($value); + $request->setBody($entityBody); + $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); + // Add the Content-Encoding header if one is set on the EntityBody + if ($encoding = $entityBody->getContentEncoding()) { + $request->setHeader('Content-Encoding', $encoding); + } + } + + /** + * Add the appropriate expect header to a request + * + * @param EntityEnclosingRequestInterface $request Request to update + * @param EntityBodyInterface $body Entity body of the request + * @param string|int $expect Expect header setting + */ + protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) + { + // Allow the `expect` data parameter to be set to remove the Expect header from the request + if ($expect === false) { + $request->removeHeader('Expect'); + } elseif ($expect !== true) { + // Default to using a MB as the point in which to start using the expect header + $expect = $expect ?: 1048576; + // If the expect_header value is numeric then only add if the size is greater than the cutoff + if (is_numeric($expect) && $body->getSize()) { + if ($body->getSize() < $expect) { + $request->removeHeader('Expect'); + } else { + $request->setHeader('Expect', '100-Continue'); + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php new file mode 100644 index 0000000..2a53754 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php @@ -0,0 +1,44 @@ +filter($value); + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->addPrefixedHeaders($request, $param, $value); + } else { + $request->setHeader($param->getWireName(), $value); + } + } + + /** + * Add a prefixed array of headers to the request + * + * @param RequestInterface $request Request to update + * @param Parameter $param Parameter object + * @param array $value Header array to add + * + * @throws InvalidArgumentException + */ + protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) + { + if (!is_array($value)) { + throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); + } + $prefix = $param->getSentAs(); + foreach ($value as $headerName => $headerValue) { + $request->setHeader($prefix . $headerName, $headerValue); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php new file mode 100644 index 0000000..757e1c5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php @@ -0,0 +1,63 @@ +data = new \SplObjectStorage(); + } + + /** + * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a + * Content-Type header unless you specify one here. + * + * @param string $header Header to set when JSON is added (e.g. application/json) + * + * @return self + */ + public function setContentTypeHeader($header = 'application/json') + { + $this->jsonContentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + if (isset($this->data[$command])) { + $json = $this->data[$command]; + } else { + $json = array(); + } + $json[$param->getWireName()] = $this->prepareValue($value, $param); + $this->data[$command] = $json; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + if (isset($this->data[$command])) { + // Don't overwrite the Content-Type if one is set + if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->jsonContentType); + } + + $request->setBody(json_encode($this->data[$command])); + unset($this->data[$command]); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php new file mode 100644 index 0000000..975850b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php @@ -0,0 +1,18 @@ +setPostField($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php new file mode 100644 index 0000000..0853ebe --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php @@ -0,0 +1,24 @@ +filter($value); + if ($value instanceof PostFileInterface) { + $request->addPostFile($value); + } else { + $request->addPostFile($param->getWireName(), $value); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php new file mode 100644 index 0000000..315877a --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php @@ -0,0 +1,18 @@ +getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php new file mode 100644 index 0000000..14e0b2d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php @@ -0,0 +1,31 @@ +setResponseBody($value); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php new file mode 100644 index 0000000..5b71487 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php @@ -0,0 +1,252 @@ +data = new \SplObjectStorage(); + } + + /** + * Change the content-type header that is added when XML is found + * + * @param string $header Header to set when XML is found + * + * @return self + */ + public function setContentTypeHeader($header) + { + $this->contentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $xml = isset($this->data[$command]) + ? $this->data[$command] + : $this->createRootElement($param->getParent()); + $this->addXml($xml, $param, $value); + + $this->data[$command] = $xml; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + $xml = null; + + // If data was found that needs to be serialized, then do so + if (isset($this->data[$command])) { + $xml = $this->finishDocument($this->data[$command]); + unset($this->data[$command]); + } else { + // Check if XML should always be sent for the command + $operation = $command->getOperation(); + if ($operation->getData('xmlAllowEmpty')) { + $xmlWriter = $this->createRootElement($operation); + $xml = $this->finishDocument($xmlWriter); + } + } + + if ($xml) { + // Don't overwrite the Content-Type if one is set + if ($this->contentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->contentType); + } + $request->setBody($xml); + } + } + + /** + * Create the root XML element to use with a request + * + * @param Operation $operation Operation object + * + * @return \XMLWriter + */ + protected function createRootElement(Operation $operation) + { + static $defaultRoot = array('name' => 'Request'); + // If no root element was specified, then just wrap the XML in 'Request' + $root = $operation->getData('xmlRoot') ?: $defaultRoot; + // Allow the XML declaration to be customized with xmlEncoding + $encoding = $operation->getData('xmlEncoding'); + + $xmlWriter = $this->startDocument($encoding); + + $xmlWriter->startElement($root['name']); + // Create the wrapping element with no namespaces if no namespaces were present + if (!empty($root['namespaces'])) { + // Create the wrapping element with an array of one or more namespaces + foreach ((array) $root['namespaces'] as $prefix => $uri) { + $nsLabel = 'xmlns'; + if (!is_numeric($prefix)) { + $nsLabel .= ':'.$prefix; + } + $xmlWriter->writeAttribute($nsLabel, $uri); + } + } + return $xmlWriter; + } + + /** + * Recursively build the XML body + * + * @param \XMLWriter $xmlWriter XML to modify + * @param Parameter $param API Parameter + * @param mixed $value Value to add + */ + protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) + { + if ($value === null) { + return; + } + + $value = $param->filter($value); + $type = $param->getType(); + $name = $param->getWireName(); + $prefix = null; + $namespace = $param->getData('xmlNamespace'); + if (false !== strpos($name, ':')) { + list($prefix, $name) = explode(':', $name, 2); + } + + if ($type == 'object' || $type == 'array') { + if (!$param->getData('xmlFlattened')) { + $xmlWriter->startElementNS(null, $name, $namespace); + } + if ($param->getType() == 'array') { + $this->addXmlArray($xmlWriter, $param, $value); + } elseif ($param->getType() == 'object') { + $this->addXmlObject($xmlWriter, $param, $value); + } + if (!$param->getData('xmlFlattened')) { + $xmlWriter->endElement(); + } + return; + } + if ($param->getData('xmlAttribute')) { + $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); + } else { + $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); + } + } + + /** + * Write an attribute with namespace if used + * + * @param \XMLWriter $xmlWriter XMLWriter instance + * @param string $prefix Namespace prefix if any + * @param string $name Attribute name + * @param string $namespace The uri of the namespace + * @param string $value The attribute content + */ + protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) + { + if (empty($namespace)) { + $xmlWriter->writeAttribute($name, $value); + } else { + $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); + } + } + + /** + * Write an element with namespace if used + * + * @param \XMLWriter $xmlWriter XML writer resource + * @param string $prefix Namespace prefix if any + * @param string $name Element name + * @param string $namespace The uri of the namespace + * @param string $value The element content + */ + protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) + { + $xmlWriter->startElementNS($prefix, $name, $namespace); + if (strpbrk($value, '<>&')) { + $xmlWriter->writeCData($value); + } else { + $xmlWriter->writeRaw($value); + } + $xmlWriter->endElement(); + } + + /** + * Create a new xml writer and start a document + * + * @param string $encoding document encoding + * + * @return \XMLWriter the writer resource + */ + protected function startDocument($encoding) + { + $xmlWriter = new \XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->startDocument('1.0', $encoding); + + return $xmlWriter; + } + + /** + * End the document and return the output + * + * @param \XMLWriter $xmlWriter + * + * @return \string the writer resource + */ + protected function finishDocument($xmlWriter) + { + $xmlWriter->endDocument(); + + return $xmlWriter->outputMemory(); + } + + /** + * Add an array to the XML + */ + protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + if ($items = $param->getItems()) { + foreach ($value as $v) { + $this->addXml($xmlWriter, $items, $v); + } + } + } + + /** + * Add an object to the XML + */ + protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + $noAttributes = array(); + // add values which have attributes + foreach ($value as $name => $v) { + if ($property = $param->getProperty($name)) { + if ($property->getData('xmlAttribute')) { + $this->addXml($xmlWriter, $property, $v); + } else { + $noAttributes[] = array('value' => $v, 'property' => $property); + } + } + } + // now add values with no attributes + foreach ($noAttributes as $element) { + $this->addXml($xmlWriter, $element['property'], $element['value']); + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php new file mode 100644 index 0000000..d87eeb9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php @@ -0,0 +1,26 @@ +getName()] = $param->filter($response->getBody()); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php new file mode 100644 index 0000000..0f8737c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php @@ -0,0 +1,50 @@ +getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->processPrefixedHeaders($response, $param, $value); + } else { + $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); + } + } + + /** + * Process a prefixed header array + * + * @param Response $response Response that contains the headers + * @param Parameter $param Parameter object + * @param array $value Value response array to modify + */ + protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) + { + // Grab prefixed headers that should be placed into an array with the prefix stripped + if ($prefix = $param->getSentAs()) { + $container = $param->getName(); + $len = strlen($prefix); + // Find all matching headers and place them into the containing element + foreach ($response->getHeaders()->toArray() as $key => $header) { + if (stripos($key, $prefix) === 0) { + // Account for multi-value headers + $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php new file mode 100644 index 0000000..a609ebd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php @@ -0,0 +1,93 @@ +getResponse()->json(); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $name = $param->getName(); + $key = $param->getWireName(); + if (isset($value[$key])) { + $this->recursiveProcess($param, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + if ($value === null) { + return; + } + + if (is_array($value)) { + $type = $param->getType(); + if ($type == 'array') { + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } elseif ($type == 'object' && !isset($value[0])) { + // On the above line, we ensure that the array is associative and not numerically indexed + $knownProperties = array(); + if ($properties = $param->getProperties()) { + foreach ($properties as $property) { + $name = $property->getName(); + $key = $property->getWireName(); + $knownProperties[$name] = 1; + if (isset($value[$key])) { + $this->recursiveProcess($property, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } elseif (($additional = $param->getAdditionalProperties()) !== true) { + // Validate and filter additional properties + foreach ($value as &$v) { + $this->recursiveProcess($additional, $v); + } + } + } + } + + $value = $param->filter($value); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php new file mode 100644 index 0000000..1b10ebc --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php @@ -0,0 +1,23 @@ +getName()] = $response->getReasonPhrase(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php new file mode 100644 index 0000000..033f40c --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php @@ -0,0 +1,46 @@ +getName()] = $response->getStatusCode(); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php new file mode 100644 index 0000000..bb7124b --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php @@ -0,0 +1,151 @@ +getResponse()->xml()), true); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $sentAs = $param->getWireName(); + $name = $param->getName(); + if (isset($value[$sentAs])) { + $this->recursiveProcess($param, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being processed + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + $type = $param->getType(); + + if (!is_array($value)) { + if ($type == 'array') { + // Cast to an array if the value was a string, but should be an array + $this->recursiveProcess($param->getItems(), $value); + $value = array($value); + } + } elseif ($type == 'object') { + $this->processObject($param, $value); + } elseif ($type == 'array') { + $this->processArray($param, $value); + } elseif ($type == 'string' && gettype($value) == 'array') { + $value = ''; + } + + if ($value !== null) { + $value = $param->filter($value); + } + } + + /** + * Process an array + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processArray(Parameter $param, &$value) + { + // Convert the node if it was meant to be an array + if (!isset($value[0])) { + // Collections fo nodes are sometimes wrapped in an additional array. For example: + // 12 should become: + // array('Items' => array(array('a' => 1), array('a' => 2)) + // Some nodes are not wrapped. For example: 12 + // should become array('Foo' => array(array('a' => 1), array('a' => 2)) + if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { + // Account for the case of a collection wrapping wrapped nodes: Items => Item[] + $value = $value[$param->getItems()->getWireName()]; + // If the wrapped node only had one value, then make it an array of nodes + if (!isset($value[0]) || !is_array($value)) { + $value = array($value); + } + } elseif (!empty($value)) { + // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the + // value is set and not empty + $value = array($value); + } + } + + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } + + /** + * Process an object + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processObject(Parameter $param, &$value) + { + // Ensure that the array is associative and not numerically indexed + if (!isset($value[0]) && ($properties = $param->getProperties())) { + $knownProperties = array(); + foreach ($properties as $property) { + $name = $property->getName(); + $sentAs = $property->getWireName(); + $knownProperties[$name] = 1; + if ($property->getData('xmlAttribute')) { + $this->processXmlAttribute($property, $value); + } elseif (isset($value[$sentAs])) { + $this->recursiveProcess($property, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } + } + } + + /** + * Process an XML attribute property + * + * @param Parameter $property Property to process + * @param array $value Value to process and update + */ + protected function processXmlAttribute(Parameter $property, array &$value) + { + $sentAs = $property->getWireName(); + if (isset($value['@attributes'][$sentAs])) { + $value[$property->getName()] = $value['@attributes'][$sentAs]; + unset($value['@attributes'][$sentAs]); + if (empty($value['@attributes'])) { + unset($value['@attributes']); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php new file mode 100644 index 0000000..74cb628 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php @@ -0,0 +1,138 @@ + 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', + 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', + 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', + 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', + 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', + 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', + 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', + 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', + 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', + 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', + 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', + 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', + 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' + ); + + /** @var array Array of mappings of location names to classes */ + protected $mappings; + + /** @var array Cache of instantiated visitors */ + protected $cache = array(); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to + * use the default values. + */ + public function __construct(array $mappings = null) + { + $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; + } + + /** + * Get an instance of a request visitor by location name + * + * @param string $visitor Visitor name + * + * @return RequestVisitorInterface + */ + public function getRequestVisitor($visitor) + { + return $this->getKey('request.' . $visitor); + } + + /** + * Get an instance of a response visitor by location name + * + * @param string $visitor Visitor name + * + * @return ResponseVisitorInterface + */ + public function getResponseVisitor($visitor) + { + return $this->getKey('response.' . $visitor); + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param RequestVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addRequestVisitor($name, RequestVisitorInterface $visitor) + { + $this->cache['request.' . $name] = $visitor; + + return $this; + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param ResponseVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addResponseVisitor($name, ResponseVisitorInterface $visitor) + { + $this->cache['response.' . $name] = $visitor; + + return $this; + } + + /** + * Get a visitor by key value name + * + * @param string $key Key name to retrieve + * + * @return mixed + * @throws InvalidArgumentException + */ + private function getKey($key) + { + if (!isset($this->cache[$key])) { + if (!isset($this->mappings[$key])) { + list($type, $name) = explode('.', $key); + throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); + } + $this->cache[$key] = new $this->mappings[$key]; + } + + return $this->cache[$key]; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php new file mode 100644 index 0000000..0748b5a --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php @@ -0,0 +1,89 @@ +responseParser = $parser; + + return $this; + } + + /** + * Set the request serializer used with the command + * + * @param RequestSerializerInterface $serializer Request serializer + * + * @return self + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->requestSerializer = $serializer; + + return $this; + } + + /** + * Get the request serializer used with the command + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + if (!$this->requestSerializer) { + // Use the default request serializer if none was found + $this->requestSerializer = DefaultRequestSerializer::getInstance(); + } + + return $this->requestSerializer; + } + + /** + * Get the response parser used for the operation + * + * @return ResponseParserInterface + */ + public function getResponseParser() + { + if (!$this->responseParser) { + // Use the default response parser if none was found + $this->responseParser = OperationResponseParser::getInstance(); + } + + return $this->responseParser; + } + + protected function build() + { + // Prepare and serialize the request + $this->request = $this->getRequestSerializer()->prepare($this); + } + + protected function process() + { + // Do not process the response if 'command.response_processing' is set to 'raw' + $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW + ? $this->request->getResponse() + : $this->getResponseParser()->parse($this); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php new file mode 100644 index 0000000..ca00bc0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php @@ -0,0 +1,195 @@ +factory = $factory; + $this->schemaInModels = $schemaInModels; + } + + /** + * Add a location visitor to the command + * + * @param string $location Location to associate with the visitor + * @param ResponseVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, ResponseVisitorInterface $visitor) + { + $this->factory->addResponseVisitor($location, $visitor); + + return $this; + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $operation = $command->getOperation(); + $type = $operation->getResponseType(); + $model = null; + + if ($type == OperationInterface::TYPE_MODEL) { + $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); + } elseif ($type == OperationInterface::TYPE_CLASS) { + return $this->parseClass($command); + } + + if (!$model) { + // Return basic processing if the responseType is not model or the model cannot be found + return parent::handleParsing($command, $response, $contentType); + } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { + // Returns a model with no visiting if the command response processing is not model + return new Model(parent::handleParsing($command, $response, $contentType)); + } else { + // Only inject the schema into the model if "schemaInModel" is true + return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); + } + } + + /** + * Parse a class object + * + * @param CommandInterface $command Command to parse into an object + * + * @return mixed + * @throws ResponseClassException + */ + protected function parseClass(CommandInterface $command) + { + // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result + $event = new CreateResponseClassEvent(array('command' => $command)); + $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); + if ($result = $event->getResult()) { + return $result; + } + + $className = $command->getOperation()->getResponseClass(); + if (!method_exists($className, 'fromCommand')) { + throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); + } + + return $className::fromCommand($command); + } + + /** + * Perform transformations on the result array + * + * @param Parameter $model Model that defines the structure + * @param CommandInterface $command Command that performed the operation + * @param Response $response Response received + * + * @return array Returns the array of result data + */ + protected function visitResult(Parameter $model, CommandInterface $command, Response $response) + { + $foundVisitors = $result = $knownProps = array(); + $props = $model->getProperties(); + + foreach ($props as $schema) { + if ($location = $schema->getLocation()) { + // Trigger the before method on the first found visitor of this type + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + } + } + + // Visit additional properties when it is an actual schema + if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { + $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); + } + + // Apply the parameter value with the location visitor + foreach ($props as $schema) { + $knownProps[$schema->getName()] = 1; + if ($location = $schema->getLocation()) { + $foundVisitors[$location]->visit($command, $response, $schema, $result); + } + } + + // Remove any unknown and potentially unsafe top-level properties + if ($additional === false) { + $result = array_intersect_key($result, $knownProps); + } + + // Call the after() method of each found visitor + foreach ($foundVisitors as $visitor) { + $visitor->after($command); + } + + return $result; + } + + protected function visitAdditionalProperties( + Parameter $model, + CommandInterface $command, + Response $response, + Parameter $additional, + &$result, + array &$foundVisitors + ) { + // Only visit when a location is specified + if ($location = $additional->getLocation()) { + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + // Only traverse if an array was parsed from the before() visitors + if (is_array($result)) { + // Find each additional property + foreach (array_keys($result) as $key) { + // Check if the model actually knows this property. If so, then it is not additional + if (!$model->getProperty($key)) { + // Set the name to the key so that we can parse it with each visitor + $additional->setName($key); + $foundVisitors[$location]->visit($command, $response, $additional, $result); + } + } + // Reset the additionalProperties name to null + $additional->setName(null); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php new file mode 100644 index 0000000..60b9334 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php @@ -0,0 +1,21 @@ + true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true, + 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true, + 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true, + 'errorResponses' => true + ); + + /** @var array Parameters */ + protected $parameters = array(); + + /** @var Parameter Additional parameters schema */ + protected $additionalParameters; + + /** @var string Name of the command */ + protected $name; + + /** @var string HTTP method */ + protected $httpMethod; + + /** @var string This is a short summary of what the operation does */ + protected $summary; + + /** @var string A longer text field to explain the behavior of the operation. */ + protected $notes; + + /** @var string Reference URL providing more information about the operation */ + protected $documentationUrl; + + /** @var string HTTP URI of the command */ + protected $uri; + + /** @var string Class of the command object */ + protected $class; + + /** @var string This is what is returned from the method */ + protected $responseClass; + + /** @var string Type information about the response */ + protected $responseType; + + /** @var string Information about the response returned by the operation */ + protected $responseNotes; + + /** @var bool Whether or not the command is deprecated */ + protected $deprecated; + + /** @var array Array of errors that could occur when running the command */ + protected $errorResponses; + + /** @var ServiceDescriptionInterface */ + protected $description; + + /** @var array Extra operation information */ + protected $data; + + /** + * Builds an Operation object using an array of configuration data: + * - name: (string) Name of the command + * - httpMethod: (string) HTTP method of the operation + * - uri: (string) URI template that can create a relative or absolute URL + * - class: (string) Concrete class that implements this command + * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information. + * - summary: (string) This is a short summary of what the operation does + * - notes: (string) A longer text field to explain the behavior of the operation. + * - documentationUrl: (string) Reference URL providing more information about the operation + * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant + * class name, or model. + * - responseNotes: (string) Information about the response returned by the operation + * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this + * value will be automatically inferred based on whether or not there is a model matching the + * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default. + * - deprecated: (bool) Set to true if this is a deprecated command + * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a + * 'code' (the HTTP response code), 'reason' (response reason phrase or description of the + * error), and 'class' (a custom exception class that would be thrown if the error is + * encountered). + * - data: (array) Any extra data that might be used to help build or serialize the operation + * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is + * not in the schema + * + * @param array $config Array of configuration data + * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found + */ + public function __construct(array $config = array(), ServiceDescriptionInterface $description = null) + { + $this->description = $description; + + // Get the intersection of the available properties and properties set on the operation + foreach (array_intersect_key($config, self::$properties) as $key => $value) { + $this->{$key} = $value; + } + + $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS; + $this->deprecated = (bool) $this->deprecated; + $this->errorResponses = $this->errorResponses ?: array(); + $this->data = $this->data ?: array(); + + if (!$this->responseClass) { + $this->responseClass = 'array'; + $this->responseType = 'primitive'; + } elseif ($this->responseType) { + // Set the response type to perform validation + $this->setResponseType($this->responseType); + } else { + // A response class was set and no response type was set, so guess what the type is + $this->inferResponseType(); + } + + // Parameters need special handling when adding + if ($this->parameters) { + foreach ($this->parameters as $name => $param) { + if ($param instanceof Parameter) { + $param->setName($name)->setParent($this); + } elseif (is_array($param)) { + $param['name'] = $name; + $this->addParam(new Parameter($param, $this->description)); + } + } + } + + if ($this->additionalParameters) { + if ($this->additionalParameters instanceof Parameter) { + $this->additionalParameters->setParent($this); + } elseif (is_array($this->additionalParameters)) { + $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description)); + } + } + } + + public function toArray() + { + $result = array(); + // Grab valid properties and filter out values that weren't set + foreach (array_keys(self::$properties) as $check) { + if ($value = $this->{$check}) { + $result[$check] = $value; + } + } + // Remove the name property + unset($result['name']); + // Parameters need to be converted to arrays + $result['parameters'] = array(); + foreach ($this->parameters as $key => $param) { + $result['parameters'][$key] = $param->toArray(); + } + // Additional parameters need to be cast to an array + if ($this->additionalParameters instanceof Parameter) { + $result['additionalParameters'] = $this->additionalParameters->toArray(); + } + + return $result; + } + + public function getServiceDescription() + { + return $this->description; + } + + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + public function getParams() + { + return $this->parameters; + } + + public function getParamNames() + { + return array_keys($this->parameters); + } + + public function hasParam($name) + { + return isset($this->parameters[$name]); + } + + public function getParam($param) + { + return isset($this->parameters[$param]) ? $this->parameters[$param] : null; + } + + /** + * Add a parameter to the command + * + * @param Parameter $param Parameter to add + * + * @return self + */ + public function addParam(Parameter $param) + { + $this->parameters[$param->getName()] = $param; + $param->setParent($this); + + return $this; + } + + /** + * Remove a parameter from the command + * + * @param string $name Name of the parameter to remove + * + * @return self + */ + public function removeParam($name) + { + unset($this->parameters[$name]); + + return $this; + } + + public function getHttpMethod() + { + return $this->httpMethod; + } + + /** + * Set the HTTP method of the command + * + * @param string $httpMethod Method to set + * + * @return self + */ + public function setHttpMethod($httpMethod) + { + $this->httpMethod = $httpMethod; + + return $this; + } + + public function getClass() + { + return $this->class; + } + + /** + * Set the concrete class of the command + * + * @param string $className Concrete class name + * + * @return self + */ + public function setClass($className) + { + $this->class = $className; + + return $this; + } + + public function getName() + { + return $this->name; + } + + /** + * Set the name of the command + * + * @param string $name Name of the command + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getSummary() + { + return $this->summary; + } + + /** + * Set a short summary of what the operation does + * + * @param string $summary Short summary of the operation + * + * @return self + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + public function getNotes() + { + return $this->notes; + } + + /** + * Set a longer text field to explain the behavior of the operation. + * + * @param string $notes Notes on the operation + * + * @return self + */ + public function setNotes($notes) + { + $this->notes = $notes; + + return $this; + } + + public function getDocumentationUrl() + { + return $this->documentationUrl; + } + + /** + * Set the URL pointing to additional documentation on the command + * + * @param string $docUrl Documentation URL + * + * @return self + */ + public function setDocumentationUrl($docUrl) + { + $this->documentationUrl = $docUrl; + + return $this; + } + + public function getResponseClass() + { + return $this->responseClass; + } + + /** + * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array', + * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID). + * + * @param string $responseClass Type of response + * + * @return self + */ + public function setResponseClass($responseClass) + { + $this->responseClass = $responseClass; + $this->inferResponseType(); + + return $this; + } + + public function getResponseType() + { + return $this->responseType; + } + + /** + * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation' + * + * @param string $responseType Response type information + * + * @return self + * @throws InvalidArgumentException + */ + public function setResponseType($responseType) + { + static $types = array( + self::TYPE_PRIMITIVE => true, + self::TYPE_CLASS => true, + self::TYPE_MODEL => true, + self::TYPE_DOCUMENTATION => true + ); + if (!isset($types[$responseType])) { + throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types))); + } + + $this->responseType = $responseType; + + return $this; + } + + public function getResponseNotes() + { + return $this->responseNotes; + } + + /** + * Set notes about the response of the operation + * + * @param string $notes Response notes + * + * @return self + */ + public function setResponseNotes($notes) + { + $this->responseNotes = $notes; + + return $this; + } + + public function getDeprecated() + { + return $this->deprecated; + } + + /** + * Set whether or not the command is deprecated + * + * @param bool $isDeprecated Set to true to mark as deprecated + * + * @return self + */ + public function setDeprecated($isDeprecated) + { + $this->deprecated = $isDeprecated; + + return $this; + } + + public function getUri() + { + return $this->uri; + } + + /** + * Set the URI template of the command + * + * @param string $uri URI template to set + * + * @return self + */ + public function setUri($uri) + { + $this->uri = $uri; + + return $this; + } + + public function getErrorResponses() + { + return $this->errorResponses; + } + + /** + * Add an error to the command + * + * @param string $code HTTP response code + * @param string $reason HTTP response reason phrase or information about the error + * @param string $class Exception class associated with the error + * + * @return self + */ + public function addErrorResponse($code, $reason, $class) + { + $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class); + + return $this; + } + + /** + * Set all of the error responses of the operation + * + * @param array $errorResponses Hash of error name to a hash containing a code, reason, class + * + * @return self + */ + public function setErrorResponses(array $errorResponses) + { + $this->errorResponses = $errorResponses; + + return $this; + } + + public function getData($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + /** + * Set a particular data point on the operation + * + * @param string $name Name of the data value + * @param mixed $value Value to set + * + * @return self + */ + public function setData($name, $value) + { + $this->data[$name] = $value; + + return $this; + } + + /** + * Get the additionalParameters of the operation + * + * @return Parameter|null + */ + public function getAdditionalParameters() + { + return $this->additionalParameters; + } + + /** + * Set the additionalParameters of the operation + * + * @param Parameter|null $parameter Parameter to set + * + * @return self + */ + public function setAdditionalParameters($parameter) + { + if ($this->additionalParameters = $parameter) { + $this->additionalParameters->setParent($this); + } + + return $this; + } + + /** + * Infer the response type from the responseClass value + */ + protected function inferResponseType() + { + static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1); + if (isset($primitives[$this->responseClass])) { + $this->responseType = self::TYPE_PRIMITIVE; + } elseif ($this->description && $this->description->hasModel($this->responseClass)) { + $this->responseType = self::TYPE_MODEL; + } else { + $this->responseType = self::TYPE_CLASS; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php new file mode 100644 index 0000000..4de41bd --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php @@ -0,0 +1,159 @@ +getModel($data['$ref'])) { + $data = $model->toArray() + $data; + } + } elseif (isset($data['extends'])) { + // If this parameter extends from another parameter then start with the actual data + // union in the parent's data (e.g. actual supersedes parent) + if ($extends = $description->getModel($data['extends'])) { + $data += $extends->toArray(); + } + } + } + + // Pull configuration data into the parameter + foreach ($data as $key => $value) { + $this->{$key} = $value; + } + + $this->serviceDescription = $description; + $this->required = (bool) $this->required; + $this->data = (array) $this->data; + + if ($this->filters) { + $this->setFilters((array) $this->filters); + } + + if ($this->type == 'object' && $this->additionalProperties === null) { + $this->additionalProperties = true; + } + } + + /** + * Convert the object to an array + * + * @return array + */ + public function toArray() + { + static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs', + 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum', + 'filters'); + + $result = array(); + + // Anything that is in the `Items` attribute of an array *must* include it's name if available + if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) { + $result['name'] = $this->name; + } + + foreach ($checks as $c) { + if ($value = $this->{$c}) { + $result[$c] = $value; + } + } + + if ($this->default !== null) { + $result['default'] = $this->default; + } + + if ($this->items !== null) { + $result['items'] = $this->getItems()->toArray(); + } + + if ($this->additionalProperties !== null) { + $result['additionalProperties'] = $this->getAdditionalProperties(); + if ($result['additionalProperties'] instanceof self) { + $result['additionalProperties'] = $result['additionalProperties']->toArray(); + } + } + + if ($this->type == 'object' && $this->properties) { + $result['properties'] = array(); + foreach ($this->getProperties() as $name => $property) { + $result['properties'][$name] = $property->toArray(); + } + } + + return $result; + } + + /** + * Get the default or static value of the command based on a value + * + * @param string $value Value that is currently set + * + * @return mixed Returns the value, a static value if one is present, or a default value + */ + public function getValue($value) + { + if ($this->static || ($this->default !== null && $value === null)) { + return $this->default; + } + + return $value; + } + + /** + * Run a value through the filters OR format attribute associated with the parameter + * + * @param mixed $value Value to filter + * + * @return mixed Returns the filtered value + */ + public function filter($value) + { + // Formats are applied exclusively and supersed filters + if ($this->format) { + return SchemaFormatter::format($this->format, $value); + } + + // Convert Boolean values + if ($this->type == 'boolean' && !is_bool($value)) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + // Apply filters to the value + if ($this->filters) { + foreach ($this->filters as $filter) { + if (is_array($filter)) { + // Convert complex filters that hold value place holders + foreach ($filter['args'] as &$data) { + if ($data == '@value') { + $data = $value; + } elseif ($data == '@api') { + $data = $this; + } + } + $value = call_user_func_array($filter['method'], $filter['args']); + } else { + $value = call_user_func($filter, $value); + } + } + } + + return $value; + } + + /** + * Get the name of the parameter + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the key of the parameter, where sentAs will supersede name if it is set + * + * @return string + */ + public function getWireName() + { + return $this->sentAs ?: $this->name; + } + + /** + * Set the name of the parameter + * + * @param string $name Name to set + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get the type(s) of the parameter + * + * @return string|array + */ + public function getType() + { + return $this->type; + } + + /** + * Set the type(s) of the parameter + * + * @param string|array $type Type of parameter or array of simple types used in a union + * + * @return self + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get if the parameter is required + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + + /** + * Set if the parameter is required + * + * @param bool $isRequired Whether or not the parameter is required + * + * @return self + */ + public function setRequired($isRequired) + { + $this->required = (bool) $isRequired; + + return $this; + } + + /** + * Get the default value of the parameter + * + * @return string|null + */ + public function getDefault() + { + return $this->default; + } + + /** + * Set the default value of the parameter + * + * @param string|null $default Default value to set + * + * @return self + */ + public function setDefault($default) + { + $this->default = $default; + + return $this; + } + + /** + * Get the description of the parameter + * + * @return string|null + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the description of the parameter + * + * @param string $description Description + * + * @return self + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Get the minimum acceptable value for an integer + * + * @return int|null + */ + public function getMinimum() + { + return $this->minimum; + } + + /** + * Set the minimum acceptable value for an integer + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinimum($min) + { + $this->minimum = $min; + + return $this; + } + + /** + * Get the maximum acceptable value for an integer + * + * @return int|null + */ + public function getMaximum() + { + return $this->maximum; + } + + /** + * Set the maximum acceptable value for an integer + * + * @param int $max Maximum + * + * @return self + */ + public function setMaximum($max) + { + $this->maximum = $max; + + return $this; + } + + /** + * Get the minimum allowed length of a string value + * + * @return int + */ + public function getMinLength() + { + return $this->minLength; + } + + /** + * Set the minimum allowed length of a string value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinLength($min) + { + $this->minLength = $min; + + return $this; + } + + /** + * Get the maximum allowed length of a string value + * + * @return int|null + */ + public function getMaxLength() + { + return $this->maxLength; + } + + /** + * Set the maximum allowed length of a string value + * + * @param int $max Maximum length + * + * @return self + */ + public function setMaxLength($max) + { + $this->maxLength = $max; + + return $this; + } + + /** + * Get the maximum allowed number of items in an array value + * + * @return int|null + */ + public function getMaxItems() + { + return $this->maxItems; + } + + /** + * Set the maximum allowed number of items in an array value + * + * @param int $max Maximum + * + * @return self + */ + public function setMaxItems($max) + { + $this->maxItems = $max; + + return $this; + } + + /** + * Get the minimum allowed number of items in an array value + * + * @return int + */ + public function getMinItems() + { + return $this->minItems; + } + + /** + * Set the minimum allowed number of items in an array value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinItems($min) + { + $this->minItems = $min; + + return $this; + } + + /** + * Get the location of the parameter + * + * @return string|null + */ + public function getLocation() + { + return $this->location; + } + + /** + * Set the location of the parameter + * + * @param string|null $location Location of the parameter + * + * @return self + */ + public function setLocation($location) + { + $this->location = $location; + + return $this; + } + + /** + * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being + * applied to a location. + * + * @return string|null + */ + public function getSentAs() + { + return $this->sentAs; + } + + /** + * Set the sentAs attribute + * + * @param string|null $name Name of the value as it is sent over the wire + * + * @return self + */ + public function setSentAs($name) + { + $this->sentAs = $name; + + return $this; + } + + /** + * Retrieve a known property from the parameter by name or a data property by name. When not specific name value + * is specified, all data properties will be returned. + * + * @param string|null $name Specify a particular property name to retrieve + * + * @return array|mixed|null + */ + public function getData($name = null) + { + if (!$name) { + return $this->data; + } + + if (isset($this->data[$name])) { + return $this->data[$name]; + } elseif (isset($this->{$name})) { + return $this->{$name}; + } + + return null; + } + + /** + * Set the extra data properties of the parameter or set a specific extra property + * + * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set + * @param mixed|null $data When setting a specific extra property, specify the data to set for it + * + * @return self + */ + public function setData($nameOrData, $data = null) + { + if (is_array($nameOrData)) { + $this->data = $nameOrData; + } else { + $this->data[$nameOrData] = $data; + } + + return $this; + } + + /** + * Get whether or not the default value can be changed + * + * @return mixed|null + */ + public function getStatic() + { + return $this->static; + } + + /** + * Set to true if the default value cannot be changed + * + * @param bool $static True or false + * + * @return self + */ + public function setStatic($static) + { + $this->static = (bool) $static; + + return $this; + } + + /** + * Get an array of filters used by the parameter + * + * @return array + */ + public function getFilters() + { + return $this->filters ?: array(); + } + + /** + * Set the array of filters used by the parameter + * + * @param array $filters Array of functions to use as filters + * + * @return self + */ + public function setFilters(array $filters) + { + $this->filters = array(); + foreach ($filters as $filter) { + $this->addFilter($filter); + } + + return $this; + } + + /** + * Add a filter to the parameter + * + * @param string|array $filter Method to filter the value through + * + * @return self + * @throws InvalidArgumentException + */ + public function addFilter($filter) + { + if (is_array($filter)) { + if (!isset($filter['method'])) { + throw new InvalidArgumentException('A [method] value must be specified for each complex filter'); + } + } + + if (!$this->filters) { + $this->filters = array($filter); + } else { + $this->filters[] = $filter; + } + + return $this; + } + + /** + * Get the parent object (an {@see OperationInterface} or {@see Parameter} + * + * @return OperationInterface|Parameter|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * Set the parent object of the parameter + * + * @param OperationInterface|Parameter|null $parent Parent container of the parameter + * + * @return self + */ + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Get the properties of the parameter + * + * @return array + */ + public function getProperties() + { + if (!$this->propertiesCache) { + $this->propertiesCache = array(); + foreach (array_keys($this->properties) as $name) { + $this->propertiesCache[$name] = $this->getProperty($name); + } + } + + return $this->propertiesCache; + } + + /** + * Get a specific property from the parameter + * + * @param string $name Name of the property to retrieve + * + * @return null|Parameter + */ + public function getProperty($name) + { + if (!isset($this->properties[$name])) { + return null; + } + + if (!($this->properties[$name] instanceof self)) { + $this->properties[$name]['name'] = $name; + $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription); + $this->properties[$name]->setParent($this); + } + + return $this->properties[$name]; + } + + /** + * Remove a property from the parameter + * + * @param string $name Name of the property to remove + * + * @return self + */ + public function removeProperty($name) + { + unset($this->properties[$name]); + $this->propertiesCache = null; + + return $this; + } + + /** + * Add a property to the parameter + * + * @param Parameter $property Properties to set + * + * @return self + */ + public function addProperty(Parameter $property) + { + $this->properties[$property->getName()] = $property; + $property->setParent($this); + $this->propertiesCache = null; + + return $this; + } + + /** + * Get the additionalProperties value of the parameter + * + * @return bool|Parameter|null + */ + public function getAdditionalProperties() + { + if (is_array($this->additionalProperties)) { + $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription); + $this->additionalProperties->setParent($this); + } + + return $this->additionalProperties; + } + + /** + * Set the additionalProperties value of the parameter + * + * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow + * + * @return self + */ + public function setAdditionalProperties($additional) + { + $this->additionalProperties = $additional; + + return $this; + } + + /** + * Set the items data of the parameter + * + * @param Parameter|null $items Items to set + * + * @return self + */ + public function setItems(Parameter $items = null) + { + if ($this->items = $items) { + $this->items->setParent($this); + } + + return $this; + } + + /** + * Get the item data of the parameter + * + * @return Parameter|null + */ + public function getItems() + { + if (is_array($this->items)) { + $this->items = new static($this->items, $this->serviceDescription); + $this->items->setParent($this); + } + + return $this->items; + } + + /** + * Get the class that the parameter must implement + * + * @return null|string + */ + public function getInstanceOf() + { + return $this->instanceOf; + } + + /** + * Set the class that the parameter must be an instance of + * + * @param string|null $instanceOf Class or interface name + * + * @return self + */ + public function setInstanceOf($instanceOf) + { + $this->instanceOf = $instanceOf; + + return $this; + } + + /** + * Get the enum of strings that are valid for the parameter + * + * @return array|null + */ + public function getEnum() + { + return $this->enum; + } + + /** + * Set the enum of strings that are valid for the parameter + * + * @param array|null $enum Array of strings or null + * + * @return self + */ + public function setEnum(array $enum = null) + { + $this->enum = $enum; + + return $this; + } + + /** + * Get the regex pattern that must match a value when the value is a string + * + * @return string + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Set the regex pattern that must match a value when the value is a string + * + * @param string $pattern Regex pattern + * + * @return self + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + + return $this; + } + + /** + * Get the format attribute of the schema + * + * @return string + */ + public function getFormat() + { + return $this->format; + } + + /** + * Set the format attribute of the schema + * + * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http) + * + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + + return $this; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php new file mode 100644 index 0000000..7f47fc9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php @@ -0,0 +1,156 @@ +setTimezone(self::getUtcTimeZone())->format($format); + } + + throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object'); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php new file mode 100644 index 0000000..b045422 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php @@ -0,0 +1,291 @@ +castIntegerToStringType = $castIntegerToStringType; + } + + public function validate(Parameter $param, &$value) + { + $this->errors = array(); + $this->recursiveProcess($param, $value); + + if (empty($this->errors)) { + return true; + } else { + sort($this->errors); + return false; + } + } + + /** + * Get the errors encountered while validating + * + * @return array + */ + public function getErrors() + { + return $this->errors ?: array(); + } + + /** + * Recursively validate a parameter + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and validate. The value may change during this validate. + * @param string $path Current validation path (used for error reporting) + * @param int $depth Current depth in the validation validate + * + * @return bool Returns true if valid, or false if invalid + */ + protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0) + { + // Update the value by adding default or static values + $value = $param->getValue($value); + + $required = $param->getRequired(); + // if the value is null and the parameter is not required or is static, then skip any further recursion + if ((null === $value && !$required) || $param->getStatic()) { + return true; + } + + $type = $param->getType(); + // Attempt to limit the number of times is_array is called by tracking if the value is an array + $valueIsArray = is_array($value); + // If a name is set then update the path so that validation messages are more helpful + if ($name = $param->getName()) { + $path .= "[{$name}]"; + } + + if ($type == 'object') { + + // Objects are either associative arrays, ToArrayInterface, or some other object + if ($param->getInstanceOf()) { + $instance = $param->getInstanceOf(); + if (!($value instanceof $instance)) { + $this->errors[] = "{$path} must be an instance of {$instance}"; + return false; + } + } + + // Determine whether or not this "value" has properties and should be traversed + $traverse = $temporaryValue = false; + + // Convert the value to an array + if (!$valueIsArray && $value instanceof ToArrayInterface) { + $value = $value->toArray(); + } + + if ($valueIsArray) { + // Ensure that the array is associative and not numerically indexed + if (isset($value[0])) { + $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; + return false; + } + $traverse = true; + } elseif ($value === null) { + // Attempt to let the contents be built up by default values if possible + $value = array(); + $temporaryValue = $valueIsArray = $traverse = true; + } + + if ($traverse) { + + if ($properties = $param->getProperties()) { + // if properties were found, the validate each property of the value + foreach ($properties as $property) { + $name = $property->getName(); + if (isset($value[$name])) { + $this->recursiveProcess($property, $value[$name], $path, $depth + 1); + } else { + $current = null; + $this->recursiveProcess($property, $current, $path, $depth + 1); + // Only set the value if it was populated with something + if (null !== $current) { + $value[$name] = $current; + } + } + } + } + + $additional = $param->getAdditionalProperties(); + if ($additional !== true) { + // If additional properties were found, then validate each against the additionalProperties attr. + $keys = array_keys($value); + // Determine the keys that were specified that were not listed in the properties of the schema + $diff = array_diff($keys, array_keys($properties)); + if (!empty($diff)) { + // Determine which keys are not in the properties + if ($additional instanceOf Parameter) { + foreach ($diff as $key) { + $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); + } + } else { + // if additionalProperties is set to false and there are additionalProperties in the values, then fail + foreach ($diff as $prop) { + $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); + } + } + } + } + + // A temporary value will be used to traverse elements that have no corresponding input value. + // This allows nested required parameters with default values to bubble up into the input. + // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value. + if ($temporaryValue && empty($value)) { + $value = null; + $valueIsArray = false; + } + } + + } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { + foreach ($value as $i => &$item) { + // Validate each item in an array against the items attribute of the schema + $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); + } + } + + // If the value is required and the type is not null, then there is an error if the value is not set + if ($required && $value === null && $type != 'null') { + $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required'); + if ($param->getDescription()) { + $message .= ': ' . $param->getDescription(); + } + $this->errors[] = $message; + return false; + } + + // Validate that the type is correct. If the type is string but an integer was passed, the class can be + // instructed to cast the integer to a string to pass validation. This is the default behavior. + if ($type && (!$type = $this->determineType($type, $value))) { + if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) { + $value = (string) $value; + } else { + $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); + } + } + + // Perform type specific validation for strings, arrays, and integers + if ($type == 'string') { + + // Strings can have enums which are a list of predefined values + if (($enum = $param->getEnum()) && !in_array($value, $enum)) { + $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { + return '"' . addslashes($s) . '"'; + }, $enum)); + } + // Strings can have a regex pattern that the value must match + if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { + $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; + } + + $strLen = null; + if ($min = $param->getMinLength()) { + $strLen = strlen($value); + if ($strLen < $min) { + $this->errors[] = "{$path} length must be greater than or equal to {$min}"; + } + } + if ($max = $param->getMaxLength()) { + if (($strLen ?: strlen($value)) > $max) { + $this->errors[] = "{$path} length must be less than or equal to {$max}"; + } + } + + } elseif ($type == 'array') { + + $size = null; + if ($min = $param->getMinItems()) { + $size = count($value); + if ($size < $min) { + $this->errors[] = "{$path} must contain {$min} or more elements"; + } + } + if ($max = $param->getMaxItems()) { + if (($size ?: count($value)) > $max) { + $this->errors[] = "{$path} must contain {$max} or fewer elements"; + } + } + + } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { + if (($min = $param->getMinimum()) && $value < $min) { + $this->errors[] = "{$path} must be greater than or equal to {$min}"; + } + if (($max = $param->getMaximum()) && $value > $max) { + $this->errors[] = "{$path} must be less than or equal to {$max}"; + } + } + + return empty($this->errors); + } + + /** + * From the allowable types, determine the type that the variable matches + * + * @param string $type Parameter type + * @param mixed $value Value to determine the type + * + * @return string|bool Returns the matching type on + */ + protected function determineType($type, $value) + { + foreach ((array) $type as $t) { + if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) { + return 'string'; + } elseif ($t == 'object' && (is_array($value) || is_object($value))) { + return 'object'; + } elseif ($t == 'array' && is_array($value)) { + return 'array'; + } elseif ($t == 'integer' && is_integer($value)) { + return 'integer'; + } elseif ($t == 'boolean' && is_bool($value)) { + return 'boolean'; + } elseif ($t == 'number' && is_numeric($value)) { + return 'number'; + } elseif ($t == 'numeric' && is_numeric($value)) { + return 'numeric'; + } elseif ($t == 'null' && !$value) { + return 'null'; + } elseif ($t == 'any') { + return 'any'; + } + } + + return false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php new file mode 100644 index 0000000..286e65e --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php @@ -0,0 +1,271 @@ +load($config, $options); + } + + /** + * @param array $config Array of configuration data + */ + public function __construct(array $config = array()) + { + $this->fromArray($config); + } + + public function serialize() + { + return json_encode($this->toArray()); + } + + public function unserialize($json) + { + $this->operations = array(); + $this->fromArray(json_decode($json, true)); + } + + public function toArray() + { + $result = array( + 'name' => $this->name, + 'apiVersion' => $this->apiVersion, + 'baseUrl' => $this->baseUrl, + 'description' => $this->description + ) + $this->extraData; + $result['operations'] = array(); + foreach ($this->getOperations() as $name => $operation) { + $result['operations'][$operation->getName() ?: $name] = $operation->toArray(); + } + if (!empty($this->models)) { + $result['models'] = array(); + foreach ($this->models as $id => $model) { + $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model; + } + } + + return array_filter($result); + } + + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Set the baseUrl of the description + * + * @param string $baseUrl Base URL of each operation + * + * @return self + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + + return $this; + } + + public function getOperations() + { + foreach (array_keys($this->operations) as $name) { + $this->getOperation($name); + } + + return $this->operations; + } + + public function hasOperation($name) + { + return isset($this->operations[$name]); + } + + public function getOperation($name) + { + // Lazily retrieve and build operations + if (!isset($this->operations[$name])) { + return null; + } + + if (!($this->operations[$name] instanceof Operation)) { + $this->operations[$name] = new Operation($this->operations[$name], $this); + } + + return $this->operations[$name]; + } + + /** + * Add a operation to the service description + * + * @param OperationInterface $operation Operation to add + * + * @return self + */ + public function addOperation(OperationInterface $operation) + { + $this->operations[$operation->getName()] = $operation->setServiceDescription($this); + + return $this; + } + + public function getModel($id) + { + if (!isset($this->models[$id])) { + return null; + } + + if (!($this->models[$id] instanceof Parameter)) { + $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this); + } + + return $this->models[$id]; + } + + public function getModels() + { + // Ensure all models are converted into parameter objects + foreach (array_keys($this->models) as $id) { + $this->getModel($id); + } + + return $this->models; + } + + public function hasModel($id) + { + return isset($this->models[$id]); + } + + /** + * Add a model to the service description + * + * @param Parameter $model Model to add + * + * @return self + */ + public function addModel(Parameter $model) + { + $this->models[$model->getName()] = $model; + + return $this; + } + + public function getApiVersion() + { + return $this->apiVersion; + } + + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } + + public function getData($key) + { + return isset($this->extraData[$key]) ? $this->extraData[$key] : null; + } + + public function setData($key, $value) + { + $this->extraData[$key] = $value; + + return $this; + } + + /** + * Initialize the state from an array + * + * @param array $config Configuration data + * @throws InvalidArgumentException + */ + protected function fromArray(array $config) + { + // Keep a list of default keys used in service descriptions that is later used to determine extra data keys + static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description'); + // Pull in the default configuration values + foreach ($defaultKeys as $key) { + if (isset($config[$key])) { + $this->{$key} = $config[$key]; + } + } + + // Account for the Swagger name for Guzzle's baseUrl + if (isset($config['basePath'])) { + $this->baseUrl = $config['basePath']; + } + + // Ensure that the models and operations properties are always arrays + $this->models = (array) $this->models; + $this->operations = (array) $this->operations; + + // We want to add operations differently than adding the other properties + $defaultKeys[] = 'operations'; + + // Create operations for each operation + if (isset($config['operations'])) { + foreach ($config['operations'] as $name => $operation) { + if (!($operation instanceof Operation) && !is_array($operation)) { + throw new InvalidArgumentException('Invalid operation in service description: ' + . gettype($operation)); + } + $this->operations[$name] = $operation; + } + } + + // Get all of the additional properties of the service description and store them in a data array + foreach (array_diff(array_keys($config), $defaultKeys) as $key) { + $this->extraData[$key] = $config[$key]; + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php new file mode 100644 index 0000000..5983e58 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php @@ -0,0 +1,106 @@ + $op) { + $name = $op['name'] = isset($op['name']) ? $op['name'] : $name; + // Extend other operations + if (!empty($op['extends'])) { + $this->resolveExtension($name, $op, $operations); + } + $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array(); + $operations[$name] = $op; + } + } + + return new ServiceDescription(array( + 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null, + 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null, + 'description' => isset($config['description']) ? $config['description'] : null, + 'operations' => $operations, + 'models' => isset($config['models']) ? $config['models'] : null + ) + $config); + } + + /** + * @param string $name Name of the operation + * @param array $op Operation value array + * @param array $operations Currently loaded operations + * @throws DescriptionBuilderException when extending a non-existent operation + */ + protected function resolveExtension($name, array &$op, array &$operations) + { + $resolved = array(); + $original = empty($op['parameters']) ? false: $op['parameters']; + $hasClass = !empty($op['class']); + foreach ((array) $op['extends'] as $extendedCommand) { + if (empty($operations[$extendedCommand])) { + throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}"); + } + $toArray = $operations[$extendedCommand]; + $resolved = empty($resolved) + ? $toArray['parameters'] + : array_merge($resolved, $toArray['parameters']); + + $op = $op + $toArray; + if (!$hasClass && isset($toArray['class'])) { + $op['class'] = $toArray['class']; + } + } + $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php new file mode 100644 index 0000000..94ca77d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php @@ -0,0 +1,28 @@ +getMessage(), $e->getCode(), $e->getPrevious()); + $ce->setSuccessfulRequests($e->getSuccessfulRequests()); + + $alreadyAddedExceptions = array(); + foreach ($e->getFailedRequests() as $request) { + if ($re = $e->getExceptionForFailedRequest($request)) { + $alreadyAddedExceptions[] = $re; + $ce->addFailedRequestWithException($request, $re); + } else { + $ce->addFailedRequest($request); + } + } + + // Add any exceptions that did not map to a request + if (count($alreadyAddedExceptions) < count($e)) { + foreach ($e as $ex) { + if (!in_array($ex, $alreadyAddedExceptions)) { + $ce->add($ex); + } + } + } + + return $ce; + } + + /** + * Get all of the commands in the transfer + * + * @return array + */ + public function getAllCommands() + { + return array_merge($this->successfulCommands, $this->failedCommands); + } + + /** + * Add to the array of successful commands + * + * @param CommandInterface $command Successful command + * + * @return self + */ + public function addSuccessfulCommand(CommandInterface $command) + { + $this->successfulCommands[] = $command; + + return $this; + } + + /** + * Add to the array of failed commands + * + * @param CommandInterface $command Failed command + * + * @return self + */ + public function addFailedCommand(CommandInterface $command) + { + $this->failedCommands[] = $command; + + return $this; + } + + /** + * Get an array of successful commands + * + * @return array + */ + public function getSuccessfulCommands() + { + return $this->successfulCommands; + } + + /** + * Get an array of failed commands + * + * @return array + */ + public function getFailedCommands() + { + return $this->failedCommands; + } + + /** + * Get the Exception that caused the given $command to fail + * + * @param CommandInterface $command Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedCommand(CommandInterface $command) + { + return $this->getExceptionForFailedRequest($command->getRequest()); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php new file mode 100644 index 0000000..1407e56 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php @@ -0,0 +1,7 @@ +invalidCommands = $commands; + parent::__construct( + 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . + 'strategy you use with a command transfer must divide command batches by client.' + ); + } + + /** + * Get the invalid commands + * + * @return array + */ + public function getCommands() + { + return $this->invalidCommands; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php new file mode 100644 index 0000000..d59ff21 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php @@ -0,0 +1,9 @@ +errors = $errors; + } + + /** + * Get any validation errors + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php new file mode 100644 index 0000000..21140e7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php @@ -0,0 +1,37 @@ +canBuild($command)) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + $className = $this->getClassName($command); + + return new $className($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return (bool) $this->getClassName($command); + } + + /** + * Get the name of the class to instantiate for the command + * + * @param CommandInterface $command Command that is associated with the iterator + * + * @return string + */ + abstract protected function getClassName(CommandInterface $command); +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php new file mode 100644 index 0000000..2efc133 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php @@ -0,0 +1,67 @@ +factories = $factories; + } + + public function build(CommandInterface $command, array $options = array()) + { + if (!($factory = $this->getFactory($command))) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + return $factory->build($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return $this->getFactory($command) !== false; + } + + /** + * Add a factory to the composite factory + * + * @param ResourceIteratorFactoryInterface $factory Factory to add + * + * @return self + */ + public function addFactory(ResourceIteratorFactoryInterface $factory) + { + $this->factories[] = $factory; + + return $this; + } + + /** + * Get the factory that matches the command object + * + * @param CommandInterface $command Command retrieving the iterator for + * + * @return ResourceIteratorFactoryInterface|bool + */ + protected function getFactory(CommandInterface $command) + { + foreach ($this->factories as $factory) { + if ($factory->canBuild($command)) { + return $factory; + } + } + + return false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php new file mode 100644 index 0000000..c71ca9d --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php @@ -0,0 +1,34 @@ +map = $map; + } + + public function getClassName(CommandInterface $command) + { + $className = $command->getName(); + + if (isset($this->map[$className])) { + return $this->map[$className]; + } elseif (isset($this->map['*'])) { + // If a wildcard was added, then always use that + return $this->map['*']; + } + + return null; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php new file mode 100644 index 0000000..2322434 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php @@ -0,0 +1,64 @@ +data = $data; + $this->structure = $structure; + } + + /** + * Get the structure of the model + * + * @return Parameter + */ + public function getStructure() + { + return $this->structure ?: new Parameter(); + } + + /** + * Provides debug information about the model object + * + * @return string + */ + public function __toString() + { + $output = 'Debug output of '; + if ($this->structure) { + $output .= $this->structure->getName() . ' '; + } + $output .= 'model'; + $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n"; + $output .= "Model data\n-----------\n\n"; + $output .= "This data can be retrieved from the model object using the get() method of the model " + . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n"; + $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1); + $output .= implode("\n", $lines); + + if ($this->structure) { + $output .= "\n\nModel structure\n---------------\n\n"; + $output .= "The following JSON document defines how the model was parsed from an HTTP response into the " + . "associative array structure you see above.\n\n"; + $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n"; + } + + return $output . "\n"; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php new file mode 100644 index 0000000..e141524 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php @@ -0,0 +1,254 @@ +originalCommand = $command; + + // Parse options from the array of options + $this->data = $data; + $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; + $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; + } + + /** + * Get all of the resources as an array (Warning: this could issue a large number of requests) + * + * @return array + */ + public function toArray() + { + return iterator_to_array($this, false); + } + + public function setLimit($limit) + { + $this->limit = $limit; + $this->resetState(); + + return $this; + } + + public function setPageSize($pageSize) + { + $this->pageSize = $pageSize; + $this->resetState(); + + return $this; + } + + /** + * Get an option from the iterator + * + * @param string $key Key of the option to retrieve + * + * @return mixed|null Returns NULL if not set or the value if set + */ + public function get($key) + { + return array_key_exists($key, $this->data) ? $this->data[$key] : null; + } + + /** + * Set an option on the iterator + * + * @param string $key Key of the option to set + * @param mixed $value Value to set for the option + * + * @return ResourceIterator + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + public function current() + { + return $this->resources ? current($this->resources) : false; + } + + public function key() + { + return max(0, $this->iteratedCount - 1); + } + + public function count() + { + return $this->retrievedCount; + } + + /** + * Get the total number of requests sent + * + * @return int + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * Rewind the Iterator to the first element and send the original command + */ + public function rewind() + { + // Use the original command + $this->command = clone $this->originalCommand; + $this->resetState(); + $this->next(); + } + + public function valid() + { + return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) + && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + public function next() + { + $this->iteratedCount++; + + // Check if a new set of resources needs to be retrieved + $sendRequest = false; + if (!$this->resources) { + $sendRequest = true; + } else { + // iterate over the internal array + $current = next($this->resources); + $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + if ($sendRequest) { + + $this->dispatch('resource_iterator.before_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + + // Get a new command object from the original command + $this->command = clone $this->originalCommand; + // Send a request and retrieve the newly loaded resources + $this->resources = $this->sendRequest(); + $this->requestCount++; + + // If no resources were found, then the last request was not needed + // and iteration must stop + if (empty($this->resources)) { + $this->invalid = true; + } else { + // Add to the number of retrieved resources + $this->retrievedCount += count($this->resources); + // Ensure that we rewind to the beginning of the array + reset($this->resources); + } + + $this->dispatch('resource_iterator.after_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + } + } + + /** + * Retrieve the NextToken that can be used in other iterators. + * + * @return string Returns a NextToken + */ + public function getNextToken() + { + return $this->nextToken; + } + + /** + * Returns the value that should be specified for the page size for a request that will maintain any hard limits, + * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit + * + * @return int Returns the page size of the next request. + */ + protected function calculatePageSize() + { + if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { + return 1 + ($this->limit - $this->iteratedCount); + } + + return (int) $this->pageSize; + } + + /** + * Reset the internal state of the iterator without triggering a rewind() + */ + protected function resetState() + { + $this->iteratedCount = 0; + $this->retrievedCount = 0; + $this->nextToken = false; + $this->resources = null; + $this->invalid = false; + } + + /** + * Send a request to retrieve the next page of results. Hook for subclasses to implement. + * + * @return array Returns the newly loaded resources + */ + abstract protected function sendRequest(); +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php new file mode 100644 index 0000000..6aa3615 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php @@ -0,0 +1,111 @@ +iterator = $iterator; + $this->callback = $callback; + Version::warn(__CLASS__ . ' is deprecated'); + } + + /** + * Apply the callback to the contents of the resource iterator + * + * @param int $perBatch The number of records to group per batch transfer + * + * @return int Returns the number of iterated resources + */ + public function apply($perBatch = 50) + { + $this->iterated = $this->batches = $batches = 0; + $that = $this; + $it = $this->iterator; + $callback = $this->callback; + + $batch = BatchBuilder::factory() + ->createBatchesWith(new BatchSizeDivisor($perBatch)) + ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { + $batches++; + $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); + call_user_func_array($callback, array($it, $batch)); + $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); + })) + ->autoFlushAt($perBatch) + ->build(); + + $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); + + foreach ($this->iterator as $resource) { + $this->iterated++; + $batch->add($resource); + } + + $batch->flush(); + $this->batches = $batches; + + return $this->iterated; + } + + /** + * Get the total number of batches sent + * + * @return int + */ + public function getBatchCount() + { + return $this->batches; + } + + /** + * Get the total number of iterated resources + * + * @return int + */ + public function getIteratedCount() + { + return $this->iterated; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php new file mode 100644 index 0000000..2fd9980 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php @@ -0,0 +1,60 @@ + AbcFoo). + */ +class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory +{ + /** @var array List of namespaces used to look for classes */ + protected $namespaces; + + /** @var InflectorInterface Inflector used to determine class names */ + protected $inflector; + + /** + * @param string|array $namespaces List of namespaces for iterator objects + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct($namespaces = array(), InflectorInterface $inflector = null) + { + $this->namespaces = (array) $namespaces; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * Registers a namespace to check for Iterators + * + * @param string $namespace Namespace which contains Iterator classes + * + * @return self + */ + public function registerNamespace($namespace) + { + array_unshift($this->namespaces, $namespace); + + return $this; + } + + protected function getClassName(CommandInterface $command) + { + $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator'; + + // Determine the name of the class to load + foreach ($this->namespaces as $namespace) { + $potentialClassName = $namespace . '\\' . $iteratorName; + if (class_exists($potentialClassName)) { + return $potentialClassName; + } + } + + return false; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php new file mode 100644 index 0000000..8b4e8db --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php @@ -0,0 +1,30 @@ +=5.3.2", + "guzzle/cache": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Service": "" } + }, + "target-dir": "Guzzle/Service", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php new file mode 100644 index 0000000..d115fd8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php @@ -0,0 +1,284 @@ +contextOptions = stream_context_get_options($context); + $this->context = $context; + } elseif (is_array($context) || !$context) { + $this->contextOptions = $context; + $this->createContext($params); + } elseif ($context) { + throw new InvalidArgumentException('$context must be an array or resource'); + } + + // Dispatch the before send event + $request->dispatch('request.before_send', array( + 'request' => $request, + 'context' => $this->context, + 'context_options' => $this->contextOptions + )); + + $this->setUrl($request); + $this->addDefaultContextOptions($request); + $this->addSslOptions($request); + $this->addBodyOptions($request); + $this->addProxyOptions($request); + + // Create the file handle but silence errors + return $this->createStream($params) + ->setCustomData('request', $request) + ->setCustomData('response_headers', $this->getLastResponseHeaders()); + } + + /** + * Set an option on the context and the internal options array + * + * @param string $wrapper Stream wrapper name of http + * @param string $name Context name + * @param mixed $value Context value + * @param bool $overwrite Set to true to overwrite an existing value + */ + protected function setContextValue($wrapper, $name, $value, $overwrite = false) + { + if (!isset($this->contextOptions[$wrapper])) { + $this->contextOptions[$wrapper] = array($name => $value); + } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { + return; + } + $this->contextOptions[$wrapper][$name] = $value; + stream_context_set_option($this->context, $wrapper, $name, $value); + } + + /** + * Create a stream context + * + * @param array $params Parameter array + */ + protected function createContext(array $params) + { + $options = $this->contextOptions; + $this->context = $this->createResource(function () use ($params, $options) { + return stream_context_create($options, $params); + }); + } + + /** + * Get the last response headers received by the HTTP request + * + * @return array + */ + public function getLastResponseHeaders() + { + return $this->lastResponseHeaders; + } + + /** + * Adds the default context options to the stream context options + * + * @param RequestInterface $request Request + */ + protected function addDefaultContextOptions(RequestInterface $request) + { + $this->setContextValue('http', 'method', $request->getMethod()); + $headers = $request->getHeaderLines(); + + // "Connection: close" is required to get streams to work in HTTP 1.1 + if (!$request->hasHeader('Connection')) { + $headers[] = 'Connection: close'; + } + + $this->setContextValue('http', 'header', $headers); + $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion()); + $this->setContextValue('http', 'ignore_errors', true); + } + + /** + * Set the URL to use with the factory + * + * @param RequestInterface $request Request that owns the URL + */ + protected function setUrl(RequestInterface $request) + { + $this->url = $request->getUrl(true); + + // Check for basic Auth username + if ($request->getUsername()) { + $this->url->setUsername($request->getUsername()); + } + + // Check for basic Auth password + if ($request->getPassword()) { + $this->url->setPassword($request->getPassword()); + } + } + + /** + * Add SSL options to the stream context + * + * @param RequestInterface $request Request + */ + protected function addSslOptions(RequestInterface $request) + { + if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { + $this->setContextValue('ssl', 'verify_peer', true, true); + if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { + $this->setContextValue('ssl', 'cafile', $cafile, true); + } + } else { + $this->setContextValue('ssl', 'verify_peer', false, true); + } + } + + /** + * Add body (content) specific options to the context options + * + * @param RequestInterface $request + */ + protected function addBodyOptions(RequestInterface $request) + { + // Add the content for the request if needed + if (!($request instanceof EntityEnclosingRequestInterface)) { + return; + } + + if (count($request->getPostFields())) { + $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); + } elseif ($request->getBody()) { + $this->setContextValue('http', 'content', (string) $request->getBody(), true); + } + + // Always ensure a content-length header is sent + if (isset($this->contextOptions['http']['content'])) { + $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); + $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); + $this->setContextValue('http', 'header', $headers, true); + } + } + + /** + * Add proxy parameters to the context if needed + * + * @param RequestInterface $request Request + */ + protected function addProxyOptions(RequestInterface $request) + { + if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { + $this->setContextValue('http', 'proxy', $proxy); + } + } + + /** + * Create the stream for the request with the context options + * + * @param array $params Parameters of the stream + * + * @return StreamInterface + */ + protected function createStream(array $params) + { + $http_response_header = null; + $url = $this->url; + $context = $this->context; + $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { + return fopen((string) $url, 'r', false, $context); + }); + + // Determine the class to instantiate + $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; + + /** @var $stream StreamInterface */ + $stream = new $className($fp); + + // Track the response headers of the request + if (isset($http_response_header)) { + $this->lastResponseHeaders = $http_response_header; + $this->processResponseHeaders($stream); + } + + return $stream; + } + + /** + * Process response headers + * + * @param StreamInterface $stream + */ + protected function processResponseHeaders(StreamInterface $stream) + { + // Set the size on the stream if it was returned in the response + foreach ($this->lastResponseHeaders as $header) { + if ((stripos($header, 'Content-Length:')) === 0) { + $stream->setSize(trim(substr($header, 15))); + } + } + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Closure to invoke that must return a valid resource + * + * @return resource + * @throws RuntimeException on error + */ + protected function createResource($callback) + { + $errors = null; + set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { + $errors[] = array( + 'message' => $msg, + 'file' => $file, + 'line' => $line + ); + return true; + }); + $resource = call_user_func($callback); + restore_error_handler(); + + if (!$resource) { + $message = 'Error creating resource. '; + foreach ($errors as $err) { + foreach ($err as $key => $value) { + $message .= "[$key] $value" . PHP_EOL; + } + } + throw new RuntimeException(trim($message)); + } + + return $resource; + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php new file mode 100644 index 0000000..12bed26 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/Stream.php @@ -0,0 +1,289 @@ + array( + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true + ), + 'write' => array( + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ) + ); + + /** + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. + * + * @throws InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + $this->setStream($stream, $size); + } + + /** + * Closes the stream when the helper is destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { + return ''; + } + + $originalPos = $this->ftell(); + $body = stream_get_contents($this->stream, -1, 0); + $this->seek($originalPos); + + return $body; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->cache[self::IS_READABLE] = false; + $this->cache[self::IS_WRITABLE] = false; + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return bool|string Returns false on failure or a hash string on success + */ + public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) + { + $pos = $stream->ftell(); + if (!$stream->seek(0)) { + return false; + } + + $ctx = hash_init($algo); + while (!$stream->feof()) { + hash_update($ctx, $stream->read(8192)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + public function getMetaData($key = null) + { + $meta = stream_get_meta_data($this->stream); + + return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); + } + + public function getStream() + { + return $this->stream; + } + + public function setStream($stream, $size = null) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->rebuildCache(); + + return $this; + } + + public function detachStream() + { + $this->stream = null; + + return $this; + } + + public function getWrapper() + { + return $this->cache[self::WRAPPER_TYPE]; + } + + public function getWrapperData() + { + return $this->getMetaData('wrapper_data') ?: array(); + } + + public function getStreamType() + { + return $this->cache[self::STREAM_TYPE]; + } + + public function getUri() + { + return $this->cache['uri']; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then use fstat + clearstatcache(true, $this->cache['uri']); + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { + // Only get the size based on the content if the the stream is readable and seekable + $pos = $this->ftell(); + $this->size = strlen((string) $this); + $this->seek($pos); + return $this->size; + } + + return false; + } + + public function isReadable() + { + return $this->cache[self::IS_READABLE]; + } + + public function isRepeatable() + { + return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; + } + + public function isWritable() + { + return $this->cache[self::IS_WRITABLE]; + } + + public function isConsumed() + { + return feof($this->stream); + } + + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->cache[self::IS_LOCAL]; + } + + public function isSeekable() + { + return $this->cache[self::SEEKABLE]; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; + } + + public function read($length) + { + return fread($this->stream, $length); + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return fwrite($this->stream, $string); + } + + public function ftell() + { + return ftell($this->stream); + } + + public function rewind() + { + return $this->seek(0); + } + + public function readLine($maxLength = null) + { + if (!$this->cache[self::IS_READABLE]) { + return false; + } else { + return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); + } + } + + public function setCustomData($key, $value) + { + $this->customData[$key] = $value; + + return $this; + } + + public function getCustomData($key) + { + return isset($this->customData[$key]) ? $this->customData[$key] : null; + } + + /** + * Reprocess stream metadata + */ + protected function rebuildCache() + { + $this->cache = stream_get_meta_data($this->stream); + $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); + $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); + $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); + } +} diff --git a/source/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php new file mode 100644 index 0000000..6d7dc37 --- /dev/null +++ b/source/vendor/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php @@ -0,0 +1,218 @@ +=5.3.2", + "guzzle/common": "self.version" + }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, + "autoload": { + "psr-0": { "Guzzle\\Stream": "" } + }, + "target-dir": "Guzzle/Stream", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php new file mode 100644 index 0000000..951738d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php @@ -0,0 +1,33 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $decoratorA = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($batch)) + ->getMockForAbstractClass(); + + $decoratorB = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($decoratorA)) + ->getMockForAbstractClass(); + + $decoratorA->add('foo'); + $this->assertFalse($decoratorB->isEmpty()); + $this->assertFalse($batch->isEmpty()); + $this->assertEquals(array($decoratorB, $decoratorA), $decoratorB->getDecorators()); + $this->assertEquals(array(), $decoratorB->flush()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php new file mode 100644 index 0000000..4da09d3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php @@ -0,0 +1,86 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + private function getMockBatchBuilder() + { + return BatchBuilder::factory() + ->transferWith($this->getMockTransfer()) + ->createBatchesWith($this->getMockDivisor()); + } + + public function testFactoryCreatesInstance() + { + $builder = BatchBuilder::factory(); + $this->assertInstanceOf('Guzzle\Batch\BatchBuilder', $builder); + } + + public function testAddsAutoFlush() + { + $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\FlushingBatch', $batch); + } + + public function testAddsExceptionBuffering() + { + $batch = $this->getMockBatchBuilder()->bufferExceptions()->build(); + $this->assertInstanceOf('Guzzle\Batch\ExceptionBufferingBatch', $batch); + } + + public function testAddHistory() + { + $batch = $this->getMockBatchBuilder()->keepHistory()->build(); + $this->assertInstanceOf('Guzzle\Batch\HistoryBatch', $batch); + } + + public function testAddsNotify() + { + $batch = $this->getMockBatchBuilder()->notify(function() {})->build(); + $this->assertInstanceOf('Guzzle\Batch\NotifyingBatch', $batch); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testTransferStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->createBatchesWith($this->getMockDivisor())->build(); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testDivisorStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->transferWith($this->getMockTransfer())->build(); + } + + public function testTransfersRequests() + { + $batch = BatchBuilder::factory()->transferRequests(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy')); + } + + public function testTransfersCommands() + { + $batch = BatchBuilder::factory()->transferCommands(10)->build(); + $this->assertInstanceOf('Guzzle\Batch\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php new file mode 100644 index 0000000..753db7d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php @@ -0,0 +1,36 @@ +createBatches($queue); + $this->assertEquals(array(array('foo'), array('baz')), $batches); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php new file mode 100644 index 0000000..6ba7ae0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php @@ -0,0 +1,52 @@ +itemsTransferred = null; + $itemsTransferred =& $this->itemsTransferred; + + $this->transferStrategy = new BatchClosureTransfer(function (array $batch) use (&$itemsTransferred) { + $itemsTransferred = $batch; + return; + }); + } + + public function testTransfersBatch() + { + $batchedItems = array('foo', 'bar', 'baz'); + $this->transferStrategy->transfer($batchedItems); + + $this->assertEquals($batchedItems, $this->itemsTransferred); + } + + public function testTransferBailsOnEmptyBatch() + { + $batchedItems = array(); + $this->transferStrategy->transfer($batchedItems); + + $this->assertNull($this->itemsTransferred); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsCallable() + { + $foo = new BatchClosureTransfer('uh oh!'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php new file mode 100644 index 0000000..a04efab --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php @@ -0,0 +1,83 @@ + $command) { + if ($i % 2) { + $command->setClient($client1); + } else { + $command->setClient($client2); + } + $queue[] = $command; + } + + $batch = new BatchCommandTransfer(2); + $this->assertEquals(array( + array($commands[0], $commands[2]), + array($commands[4]), + array($commands[1], $commands[3]) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreCommands() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchCommandTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = $this->getMockBuilder('Guzzle\Service\Client') + ->setMethods(array('send')) + ->getMock(); + $client->expects($this->once()) + ->method('send'); + $command = new Mc(); + $command->setClient($client); + $batch = new BatchCommandTransfer(2); + $batch->transfer(array($command)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchCommandTransfer(2); + $batch->transfer(array()); + } + + /** + * @expectedException Guzzle\Service\Exception\InconsistentClientTransferException + */ + public function testEnsuresAllCommandsUseTheSameClient() + { + $batch = new BatchCommandTransfer(2); + $client1 = new Client('http://www.example.com'); + $client2 = new Client('http://www.example.com'); + $command1 = new Mc(); + $command1->setClient($client1); + $command2 = new Mc(); + $command2->setClient($client2); + $batch->transfer(array($command1, $command2)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php new file mode 100644 index 0000000..dec7bd5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php @@ -0,0 +1,80 @@ +setCurlMulti(new CurlMulti()); + + $client2 = new Client('http://www.example.com'); + $client2->setCurlMulti(new CurlMulti()); + + $request1 = $client1->get(); + $request2 = $client2->get(); + $request3 = $client1->get(); + $request4 = $client2->get(); + $request5 = $client1->get(); + + $queue = new \SplQueue(); + $queue[] = $request1; + $queue[] = $request2; + $queue[] = $request3; + $queue[] = $request4; + $queue[] = $request5; + + $batch = new BatchRequestTransfer(2); + $this->assertEquals(array( + array($request1, $request3), + array($request3), + array($request2, $request4) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreRequests() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchRequestTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // For some reason... PHP unit clones the request, which emits a request.clone event. This causes the + // 'sorted' property of the event dispatcher to contain an array in the cloned request that is not present in + // the original. + $request->dispatch('request.clone'); + + $multi = $this->getMock('Guzzle\Http\Curl\CurlMultiInterface'); + $client->setCurlMulti($multi); + $multi->expects($this->once()) + ->method('add') + ->with($request); + $multi->expects($this->once()) + ->method('send'); + + $batch = new BatchRequestTransfer(2); + $batch->transfer(array($request)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchRequestTransfer(2); + $batch->transfer(array()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php new file mode 100644 index 0000000..5542228 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php @@ -0,0 +1,24 @@ +assertEquals(3, $d->getSize()); + $d->setSize(2); + $batches = $d->createBatches($queue); + $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php new file mode 100644 index 0000000..296f57a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/BatchTest.php @@ -0,0 +1,91 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + } + + public function testAddsItemsToQueue() + { + $batch = new Batch($this->getMockTransfer(), $this->getMockDivisor()); + $this->assertSame($batch, $batch->add('foo')); + $this->assertEquals(1, count($batch)); + } + + public function testFlushReturnsItems() + { + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer'); + + $divisor = $this->getMockDivisor(); + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnValue(array(array('foo', 'baz'), array('bar')))); + + $batch = new Batch($transfer, $divisor); + + $batch->add('foo')->add('baz')->add('bar'); + $items = $batch->flush(); + + $this->assertEquals(array('foo', 'baz', 'bar'), $items); + } + + public function testThrowsExceptionContainingTheFailedBatch() + { + $called = 0; + $originalException = new \Exception('Foo!'); + + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer') + ->will($this->returnCallback(function () use (&$called, $originalException) { + if (++$called == 2) { + throw $originalException; + } + })); + + $divisor = $this->getMockDivisor(); + $batch = new Batch($transfer, $divisor); + + // PHPunit clones objects before passing them to a callback. + // Horrible hack to get around this! + $queue = $this->readAttribute($batch, 'queue'); + + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnCallback(function ($batch) use ($queue) { + foreach ($queue as $item) { + $items[] = $item; + } + return array_chunk($items, 2); + })); + + $batch->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertFalse($batch->isEmpty()); + + try { + $items = $batch->flush(); + $this->fail('Expected exception'); + } catch (BatchTransferException $e) { + $this->assertEquals($originalException, $e->getPrevious()); + $this->assertEquals(array('bar', 'bee'), array_values($e->getBatch())); + $this->assertEquals(1, count($batch)); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php new file mode 100644 index 0000000..fd810b1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php @@ -0,0 +1,45 @@ +getMockBuilder('Guzzle\Batch\BatchTransferInterface') + ->setMethods(array('transfer')) + ->getMock(); + + $d = new BatchSizeDivisor(1); + $batch = new Batch($t, $d); + + $called = 0; + $t->expects($this->exactly(3)) + ->method('transfer') + ->will($this->returnCallback(function ($batch) use (&$called) { + if (++$called === 2) { + throw new \Exception('Foo'); + } + })); + + $decorator = new ExceptionBufferingBatch($batch); + $decorator->add('foo')->add('baz')->add('bar'); + $result = $decorator->flush(); + + $e = $decorator->getExceptions(); + $this->assertEquals(1, count($e)); + $this->assertEquals(array('baz'), $e[0]->getBatch()); + + $decorator->clearExceptions(); + $this->assertEquals(0, count($decorator->getExceptions())); + + $this->assertEquals(array('foo', 'bar'), $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php new file mode 100644 index 0000000..9b37a48 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php @@ -0,0 +1,40 @@ +getMock('Guzzle\Batch\BatchTransferInterface', array('transfer')); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface', array('createBatches')); + + $batch = new Batch($t, $d); + $queue = $this->readAttribute($batch, 'queue'); + + $d->expects($this->exactly(2)) + ->method('createBatches') + ->will($this->returnCallback(function () use ($queue) { + $items = array(); + foreach ($queue as $item) { + $items[] = $item; + } + return array($items); + })); + + $t->expects($this->exactly(2)) + ->method('transfer'); + + $flush = new FlushingBatch($batch, 3); + $this->assertEquals(3, $flush->getThreshold()); + $flush->setThreshold(2); + $flush->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertEquals(1, count($flush)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php new file mode 100644 index 0000000..60d6f95 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php @@ -0,0 +1,26 @@ +getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + + $history = new HistoryBatch($batch); + $history->add('foo')->add('baz'); + $this->assertEquals(array('foo', 'baz'), $history->getHistory()); + $history->clearHistory(); + $this->assertEquals(array(), $history->getHistory()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php new file mode 100644 index 0000000..69a8900 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Batch\Batch', array('flush'), array( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + )); + + $batch->expects($this->once()) + ->method('flush') + ->will($this->returnValue(array('foo', 'baz'))); + + $data = array(); + $decorator = new NotifyingBatch($batch, function ($batch) use (&$data) { + $data[] = $batch; + }); + + $decorator->add('foo')->add('baz'); + $decorator->flush(); + $this->assertEquals(array(array('foo', 'baz')), $data); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsValid() + { + $batch = new Batch( + $this->getMock('Guzzle\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Batch\BatchDivisorInterface') + ); + $decorator = new NotifyingBatch($batch, 'foo'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php new file mode 100644 index 0000000..c4140a9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php @@ -0,0 +1,64 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresConfigIsObject() + { + CacheAdapterFactory::fromCache(array()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testEnsuresKnownType() + { + CacheAdapterFactory::fromCache(new \stdClass()); + } + + public function cacheProvider() + { + return array( + array(new DoctrineCacheAdapter(new ArrayCache()), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(new ArrayCache(), 'Guzzle\Cache\DoctrineCacheAdapter'), + array(StorageFactory::factory(array('adapter' => 'memory')), 'Guzzle\Cache\Zf2CacheAdapter'), + ); + } + + /** + * @dataProvider cacheProvider + */ + public function testCreatesNullCacheAdapterByDefault($cache, $type) + { + $adapter = CacheAdapterFactory::fromCache($cache); + $this->assertInstanceOf($type, $adapter); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php new file mode 100644 index 0000000..3e30ddd --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php @@ -0,0 +1,68 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testGetCacheObject() + { + $this->assertEquals($this->cache, $this->adapter->getCacheObject()); + } + + public function testSave() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + } + + public function testFetch() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testContains() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->contains('test')); + } + + public function testDelete() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->delete('test')); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php new file mode 100644 index 0000000..12de65b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php @@ -0,0 +1,94 @@ +callables = array( + 'contains' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data); + }, + 'delete' => function($id, $options = array()) use ($that) { + unset($that->data[$id]); + return true; + }, + 'fetch' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data) ? $that->data[$id] : null; + }, + 'save' => function($id, $data, $lifeTime, $options = array()) use ($that) { + $that->data[$id] = $data; + return true; + } + ); + + $this->adapter = new ClosureCacheAdapter($this->callables); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->cache = null; + $this->callables = null; + parent::tearDown(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testEnsuresCallablesArePresent() + { + $callables = $this->callables; + unset($callables['delete']); + $cache = new ClosureCacheAdapter($callables); + } + + public function testAllCallablesMustBePresent() + { + $cache = new ClosureCacheAdapter($this->callables); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php new file mode 100644 index 0000000..e05df3f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php @@ -0,0 +1,20 @@ +assertEquals(false, $c->contains('foo')); + $this->assertEquals(true, $c->delete('foo')); + $this->assertEquals(false, $c->fetch('foo')); + $this->assertEquals(true, $c->save('foo', 'bar')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php new file mode 100644 index 0000000..9077c12 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php @@ -0,0 +1,58 @@ +cache = StorageFactory::factory(array( + 'adapter' => 'memory' + )); + $this->adapter = new Zf2CacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php new file mode 100644 index 0000000..19d12e6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php @@ -0,0 +1,63 @@ +assertEquals(array(), AbstractHasDispatcher::getAllEvents()); + } + + public function testAllowsDispatcherToBeInjected() + { + $d = new EventDispatcher(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertSame($mock, $mock->setEventDispatcher($d)); + $this->assertSame($d, $mock->getEventDispatcher()); + } + + public function testCreatesDefaultEventDispatcherIfNeeded() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher()); + } + + public function testHelperDispatchesEvents() + { + $data = array(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $mock->getEventDispatcher()->addListener('test', function(Event $e) use (&$data) { + $data = $e->getIterator()->getArrayCopy(); + }); + $mock->dispatch('test', array( + 'param' => 'abc' + )); + $this->assertEquals(array( + 'param' => 'abc', + ), $data); + } + + public function testHelperAttachesSubscribers() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $subscriber = $this->getMockForAbstractClass('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') + ->setMethods(array('addSubscriber')) + ->getMock(); + + $dispatcher->expects($this->once()) + ->method('addSubscriber'); + + $mock->setEventDispatcher($dispatcher); + $mock->addSubscriber($subscriber); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php new file mode 100644 index 0000000..0648a02 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php @@ -0,0 +1,529 @@ +coll = new Collection(); + } + + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->getAll(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor'); + $this->assertEquals($this->coll->getAll(), $this->coll->toArray()); + } + + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->getAll(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3'))); + } + + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->getAll(), $params); + + // Pass the same object to itself + $this->assertEquals($this->coll->merge($this->coll), $this->coll); + } + + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->getAll(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->getAll(), array()); + } + + public function testGetsValuesByKey() + { + $this->assertNull($this->coll->get('test')); + $this->coll->add('test', 'value'); + $this->assertEquals('value', $this->coll->get('test')); + $this->coll->set('test2', 'v2'); + $this->coll->set('test3', 'v3'); + $this->assertEquals(array( + 'test' => 'value', + 'test2' => 'v2' + ), $this->coll->getAll(array('test', 'test2'))); + } + + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + public function testCanGetAllValuesByArray() + { + $this->coll->add('foo', 'bar'); + $this->coll->add('tEsT', 'value'); + $this->coll->add('tesTing', 'v2'); + $this->coll->add('key', 'v3'); + $this->assertNull($this->coll->get('test')); + $this->assertEquals(array( + 'foo' => 'bar', + 'tEsT' => 'value', + 'tesTing' => 'v2' + ), $this->coll->getAll(array( + 'foo', 'tesTing', 'tEsT' + ))); + } + + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->getAll()); + } + + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotEquals($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->getAll()); + } + + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function($key, $value) { + return $value * $value; + }); + + $this->assertNotEquals($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->getAll()); + } + + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + public function testUsesStaticWhenCreatingNew() + { + $qs = new QueryString(array( + 'a' => 'b', + 'c' => 'd' + )); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false)); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false)); + } + + public function testCanReplaceAllData() + { + $this->assertSame($this->coll, $this->coll->replace(array( + 'a' => '123' + ))); + + $this->assertEquals(array( + 'a' => '123' + ), $this->coll->getAll()); + } + + public function dataProvider() + { + return array( + array('this_is_a_test', '{a}_is_a_{b}', array( + 'a' => 'this', + 'b' => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{0}', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', 'this_is_a_test', array( + 'abc' => 'this' + )), + array('{abc}_is_{not_found}a_{0}', '{abc}_is_{not_found}a_{0}', array()) + ); + } + + /** + * @dataProvider dataProvider + */ + public function testInjectsConfigData($output, $input, $config) + { + $collection = new Collection($config); + $this->assertEquals($output, $collection->inject($input)); + } + + public function testCanSearchByKey() + { + $collection = new Collection(array( + 'foo' => 'bar', + 'BaZ' => 'pho' + )); + + $this->assertEquals('foo', $collection->keySearch('FOO')); + $this->assertEquals('BaZ', $collection->keySearch('baz')); + $this->assertEquals(false, $collection->keySearch('Bar')); + } + + public function testPreparesFromConfig() + { + $c = Collection::fromConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('Guzzle\Common\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->getAll()); + + try { + $c = Collection::fromConfig(array(), array(), array('a')); + $this->fail('Exception not throw when missing config'); + } catch (InvalidArgumentException $e) { + } + } + + function falseyDataProvider() + { + return array( + array(false, false), + array(null, null), + array('', ''), + array(array(), array()), + array(0, 0), + ); + } + + /** + * @dataProvider falseyDataProvider + */ + public function testReturnsCorrectData($a, $b) + { + $c = new Collection(array('value' => $a)); + $this->assertSame($b, $c->get('value')); + } + + public function testRetrievesNestedKeysUsingPath() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar' + ) + ) + ); + $collection = new Collection($data); + $this->assertEquals('bar', $collection->getPath('foo')); + $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); + $this->assertNull($collection->getPath('wewewf')); + $this->assertNull($collection->getPath('baz/mesa/jar/jar')); + } + + public function testFalseyKeysStillDescend() + { + $collection = new Collection(array( + '0' => array( + 'a' => 'jar' + ), + 1 => 'other' + )); + $this->assertEquals('jar', $collection->getPath('0/a')); + $this->assertEquals('other', $collection->getPath('1')); + } + + public function getPathProvider() + { + $data = array( + 'foo' => 'bar', + 'baz' => array( + 'mesa' => array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c') + ), + 'bar' => array( + 'baz' => 'bam', + 'array' => array('d', 'e', 'f') + ) + ), + 'bam' => array( + array('foo' => 1), + array('foo' => 2), + array('array' => array('h', 'i')) + ) + ); + $c = new Collection($data); + + return array( + // Simple path selectors + array($c, 'foo', 'bar'), + array($c, 'baz', $data['baz']), + array($c, 'bam', $data['bam']), + array($c, 'baz/mesa', $data['baz']['mesa']), + array($c, 'baz/mesa/jar', 'jar'), + // Merge everything two levels under baz + array($c, 'baz/*', array( + 'jar' => 'jar', + 'array' => array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array']), + 'baz' => 'bam' + )), + // Does not barf on missing keys + array($c, 'fefwfw', null), + // Does not barf when a wildcard does not resolve correctly + array($c, '*/*/*/*/*/wefwfe', array()), + // Allows custom separator + array($c, '*|mesa', $data['baz']['mesa'], '|'), + // Merge all 'array' keys two levels under baz (the trailing * does not hurt the results) + array($c, 'baz/*/array/*', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + // Merge all 'array' keys two levels under baz + array($c, 'baz/*/array', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), + array($c, 'baz/mesa/array', $data['baz']['mesa']['array']), + // Having a trailing * does not hurt the results + array($c, 'baz/mesa/array/*', $data['baz']['mesa']['array']), + // Merge of anything one level deep + array($c, '*', array_merge(array('bar'), $data['baz'], $data['bam'])), + // Funky merge of anything two levels deep + array($c, '*/*', array( + 'jar' => 'jar', + 'array' => array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'), + 'baz' => 'bam', + 'foo' => array(1, 2) + )), + // Funky merge of all 'array' keys that are two levels deep + array($c, '*/*/array', array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i')) + ); + } + + /** + * @dataProvider getPathProvider + */ + public function testGetPath(Collection $c, $path, $expected, $separator = '/') + { + $this->assertEquals($expected, $c->getPath($path, $separator)); + } + + public function testOverridesSettings() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $c->overwriteWith(array('foo' => 10, 'bar' => 300)); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithCollection() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testOverwriteWithTraversable() + { + $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); + $b = new Collection(array('foo' => 10, 'bar' => 300)); + $c->overwriteWith($b->getIterator()); + $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); + } + + public function testCanSetNestedPathValueThatDoesNotExist() + { + $c = new Collection(array()); + $c->setPath('foo/bar/baz/123', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); + } + + public function testCanSetNestedPathValueThatExists() + { + $c = new Collection(array('foo' => array('bar' => 'test'))); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesNestedPathIsValidAtExactLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar', 'hi'); + $this->assertEquals('hi', $c['foo']['bar']); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testVerifiesThatNestedPathIsValidAtAnyLevel() + { + $c = new Collection(array('foo' => 'bar')); + $c->setPath('foo/bar/baz', 'test'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php new file mode 100644 index 0000000..5484e14 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php @@ -0,0 +1,62 @@ + '123', + 'other' => '456', + 'event' => 'test.notify' + )); + } + + public function testAllowsParameterInjection() + { + $event = new Event(array( + 'test' => '123' + )); + $this->assertEquals('123', $event['test']); + } + + public function testImplementsArrayAccess() + { + $event = $this->getEvent(); + $this->assertEquals('123', $event['test']); + $this->assertNull($event['foobar']); + + $this->assertTrue($event->offsetExists('test')); + $this->assertFalse($event->offsetExists('foobar')); + + unset($event['test']); + $this->assertFalse($event->offsetExists('test')); + + $event['test'] = 'new'; + $this->assertEquals('new', $event['test']); + } + + public function testImplementsIteratorAggregate() + { + $event = $this->getEvent(); + $this->assertInstanceOf('ArrayIterator', $event->getIterator()); + } + + public function testConvertsToArray() + { + $this->assertEquals(array( + 'test' => '123', + 'other' => '456', + 'event' => 'test.notify' + ), $this->getEvent()->toArray()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php new file mode 100644 index 0000000..c72a2a6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php @@ -0,0 +1,21 @@ +getMock('Guzzle\Batch\BatchTransferInterface'); + $d = $this->getMock('Guzzle\Batch\BatchDivisorInterface'); + $transferException = new BatchTransferException(array('foo'), array(1, 2), $e, $t, $d); + $this->assertEquals(array('foo'), $transferException->getBatch()); + $this->assertSame($t, $transferException->getTransferStrategy()); + $this->assertSame($d, $transferException->getDivisorStrategy()); + $this->assertSame($e, $transferException->getPrevious()); + $this->assertEquals(array(1, 2), $transferException->getTransferredItems()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php new file mode 100644 index 0000000..2aecf2a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php @@ -0,0 +1,66 @@ +getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $e->getMessage()); + $this->assertContains(" Test\n\n #0 ./", $e->getMessage()); + $this->assertSame($exceptions[0], $e->getFirst()); + } + + public function testCanSetExceptions() + { + $ex = new \Exception('foo'); + $e = new ExceptionCollection(); + $e->setExceptions(array($ex)); + $this->assertSame($ex, $e->getFirst()); + } + + public function testActsAsArray() + { + $e = new ExceptionCollection(); + $exceptions = $this->getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertEquals(2, count($e)); + $this->assertEquals($exceptions, $e->getIterator()->getArrayCopy()); + } + + public function testCanAddSelf() + { + $e1 = new ExceptionCollection(); + $e1->add(new \Exception("Test")); + $e2 = new ExceptionCollection('Meta description!'); + $e2->add(new \Exception("Test 2")); + $e3 = new ExceptionCollection(); + $e3->add(new \Exception('Baz')); + $e2->add($e3); + $e1->add($e2); + $message = $e1->getMessage(); + $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test\n\n #0 ", $message); + $this->assertContains("\n\n(Guzzle\\Common\\Exception\\ExceptionCollection) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n\n Meta description!\n\n", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains("\n Test 2\n\n #0 ", $message); + $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); + $this->assertContains(" Baz\n\n #0", $message); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php new file mode 100644 index 0000000..c3a81d1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/VersionTest.php @@ -0,0 +1,27 @@ +isRunning()) { + self::$server->flush(); + } else { + self::$server->start(); + } + } + + return self::$server; + } + + /** + * Set the service builder to use for tests + * + * @param ServiceBuilderInterface $builder Service builder + */ + public static function setServiceBuilder(ServiceBuilderInterface $builder) + { + self::$serviceBuilder = $builder; + } + + /** + * Get a service builder object that can be used throughout the service tests + * + * @return ServiceBuilder + */ + public static function getServiceBuilder() + { + if (!self::$serviceBuilder) { + throw new RuntimeException('No service builder has been set via setServiceBuilder()'); + } + + return self::$serviceBuilder; + } + + /** + * Check if an event dispatcher has a subscriber + * + * @param HasDispatcherInterface $dispatcher + * @param EventSubscriberInterface $subscriber + * + * @return bool + */ + protected function hasSubscriber(HasDispatcherInterface $dispatcher, EventSubscriberInterface $subscriber) + { + $class = get_class($subscriber); + $all = array_keys(call_user_func(array($class, 'getSubscribedEvents'))); + + foreach ($all as $i => $event) { + foreach ($dispatcher->getEventDispatcher()->getListeners($event) as $e) { + if ($e[0] === $subscriber) { + unset($all[$i]); + break; + } + } + } + + return count($all) == 0; + } + + /** + * Get a wildcard observer for an event dispatcher + * + * @param HasDispatcherInterface $hasDispatcher + * + * @return MockObserver + */ + public function getWildcardObserver(HasDispatcherInterface $hasDispatcher) + { + $class = get_class($hasDispatcher); + $o = new MockObserver(); + $events = call_user_func(array($class, 'getAllEvents')); + foreach ($events as $event) { + $hasDispatcher->getEventDispatcher()->addListener($event, array($o, 'update')); + } + + return $o; + } + + /** + * Set the mock response base path + * + * @param string $path Path to mock response folder + * + * @return GuzzleTestCase + */ + public static function setMockBasePath($path) + { + self::$mockBasePath = $path; + } + + /** + * Mark a request as being mocked + * + * @param RequestInterface $request + * + * @return self + */ + public function addMockedRequest(RequestInterface $request) + { + $this->requests[] = $request; + + return $this; + } + + /** + * Get all of the mocked requests + * + * @return array + */ + public function getMockedRequests() + { + return $this->requests; + } + + /** + * Get a mock response for a client by mock file name + * + * @param string $path Relative path to the mock response file + * + * @return Response + */ + public function getMockResponse($path) + { + return $path instanceof Response + ? $path + : MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path); + } + + /** + * Set a mock response from a mock file on the next client request. + * + * This method assumes that mock response files are located under the + * Command/Mock/ directory of the Service being tested + * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next + * request sent by the client. + * + * @param Client $client Client object to modify + * @param string $paths Path to files within the Mock folder of the service + * + * @return MockPlugin returns the created mock plugin + */ + public function setMockResponse(Client $client, $paths) + { + $this->requests = array(); + $that = $this; + $mock = new MockPlugin(null, true); + $client->getEventDispatcher()->removeSubscriber($mock); + $mock->getEventDispatcher()->addListener('mock.request', function(Event $event) use ($that) { + $that->addMockedRequest($event['request']); + }); + + if ($paths instanceof Response) { + // A single response instance has been specified, create an array with that instance + // as the only element for the following loop to work as expected + $paths = array($paths); + } + + foreach ((array) $paths as $path) { + $mock->addResponse($this->getMockResponse($path)); + } + + $client->getEventDispatcher()->addSubscriber($mock); + + return $mock; + } + + /** + * Compare HTTP headers and use special markup to filter values + * A header prefixed with '!' means it must not exist + * A header prefixed with '_' means it must be ignored + * A header value of '*' means anything after the * will be ignored + * + * @param array $filteredHeaders Array of special headers + * @param array $actualHeaders Array of headers to check against + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareHeaders($filteredHeaders, $actualHeaders) + { + $comparison = new HeaderComparison(); + + return $comparison->compare($filteredHeaders, $actualHeaders); + } + + /** + * Case insensitive assertContains + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertContainsIns($needle, $haystack, $message = null) + { + $this->assertContains(strtolower($needle), strtolower($haystack), $message); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php new file mode 100644 index 0000000..20feaa8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php @@ -0,0 +1,34 @@ +getMockForAbstractClass('Guzzle\Http\AbstractEntityBodyDecorator', array($e)); + + $this->assertSame($e->getStream(), $mock->getStream()); + $this->assertSame($e->getContentLength(), $mock->getContentLength()); + $this->assertSame($e->getSize(), $mock->getSize()); + $this->assertSame($e->getContentMd5(), $mock->getContentMd5()); + $this->assertSame($e->getContentType(), $mock->getContentType()); + $this->assertSame($e->__toString(), $mock->__toString()); + $this->assertSame($e->getUri(), $mock->getUri()); + $this->assertSame($e->getStreamType(), $mock->getStreamType()); + $this->assertSame($e->getWrapper(), $mock->getWrapper()); + $this->assertSame($e->getWrapperData(), $mock->getWrapperData()); + $this->assertSame($e->isReadable(), $mock->isReadable()); + $this->assertSame($e->isWritable(), $mock->isWritable()); + $this->assertSame($e->isConsumed(), $mock->isConsumed()); + $this->assertSame($e->isLocal(), $mock->isLocal()); + $this->assertSame($e->isSeekable(), $mock->isSeekable()); + $this->assertSame($e->getContentEncoding(), $mock->getContentEncoding()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php new file mode 100644 index 0000000..e6e6cdb --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php @@ -0,0 +1,249 @@ +decorated = EntityBody::factory('testing'); + $this->body = new CachingEntityBody($this->decorated); + } + + public function testUsesRemoteSizeIfPossible() + { + $body = EntityBody::factory('test'); + $caching = new CachingEntityBody($body); + $this->assertEquals(4, $caching->getSize()); + $this->assertEquals(4, $caching->getContentLength()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage does not support custom stream rewind + */ + public function testDoesNotAllowRewindFunction() + { + $this->body->setRewindFunction(true); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Cannot seek to byte 10 + */ + public function testCannotSeekPastWhatHasBeenRead() + { + $this->body->seek(10); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR + */ + public function testCannotUseSeekEnd() + { + $this->body->seek(2, SEEK_END); + } + + public function testChangingUnderlyingStreamUpdatesSizeAndStream() + { + $size = filesize(__FILE__); + $s = fopen(__FILE__, 'r'); + $this->body->setStream($s, $size); + $this->assertEquals($size, $this->body->getSize()); + $this->assertEquals($size, $this->decorated->getSize()); + $this->assertSame($s, $this->body->getStream()); + $this->assertSame($s, $this->decorated->getStream()); + } + + public function testRewindUsesSeek() + { + $a = EntityBody::factory('foo'); + $d = $this->getMockBuilder('Guzzle\Http\CachingEntityBody') + ->setMethods(array('seek')) + ->setConstructorArgs(array($a)) + ->getMock(); + $d->expects($this->once()) + ->method('seek') + ->with(0) + ->will($this->returnValue(true)); + $d->rewind(); + } + + public function testCanSeekToReadBytes() + { + $this->assertEquals('te', $this->body->read(2)); + $this->body->seek(0); + $this->assertEquals('test', $this->body->read(4)); + $this->assertEquals(4, $this->body->ftell()); + $this->body->seek(2); + $this->assertEquals(2, $this->body->ftell()); + $this->body->seek(2, SEEK_CUR); + $this->assertEquals(4, $this->body->ftell()); + $this->assertEquals('ing', $this->body->read(3)); + } + + public function testWritesToBufferStream() + { + $this->body->read(2); + $this->body->write('hi'); + $this->body->rewind(); + $this->assertEquals('tehiing', (string) $this->body); + } + + public function testReadLinesFromBothStreams() + { + $this->body->seek($this->body->ftell()); + $this->body->write("test\n123\nhello\n1234567890\n"); + $this->body->rewind(); + $this->assertEquals("test\n", $this->body->readLine(7)); + $this->assertEquals("123\n", $this->body->readLine(7)); + $this->assertEquals("hello\n", $this->body->readLine(7)); + $this->assertEquals("123456", $this->body->readLine(7)); + $this->assertEquals("7890\n", $this->body->readLine(7)); + // We overwrote the decorated stream, so no more data + $this->assertEquals('', $this->body->readLine(7)); + } + + public function testSkipsOverwrittenBytes() + { + $decorated = EntityBody::factory( + implode("\n", array_map(function ($n) { + return str_pad($n, 4, '0', STR_PAD_LEFT); + }, range(0, 25))) + ); + + $body = new CachingEntityBody($decorated); + + $this->assertEquals("0000\n", $body->readLine()); + $this->assertEquals("0001\n", $body->readLine()); + // Write over part of the body yet to be read, so skip some bytes + $this->assertEquals(5, $body->write("TEST\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + // Read, which skips bytes, then reads + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + + // Overwrite part of the cached body (so don't skip any bytes) + $body->seek(5); + $this->assertEquals(5, $body->write("ABCD\n")); + $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); + $this->assertEquals("TEST\n", $body->readLine()); + $this->assertEquals("0003\n", $body->readLine()); + $this->assertEquals("0004\n", $body->readLine()); + $this->assertEquals("0005\n", $body->readLine()); + $this->assertEquals("0006\n", $body->readLine()); + $this->assertEquals(5, $body->write("1234\n")); + $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); + + // Seek to 0 and ensure the overwritten bit is replaced + $body->rewind(); + $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); + + // Ensure that casting it to a string does not include the bit that was overwritten + $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); + } + + public function testWrapsContentType() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentType')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentType') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentType()); + } + + public function testWrapsContentEncoding() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getContentEncoding')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + $a->expects($this->once()) + ->method('getContentEncoding') + ->will($this->returnValue('foo')); + $d = new CachingEntityBody($a); + $this->assertEquals('foo', $d->getContentEncoding()); + } + + public function testWrapsMetadata() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getMetadata', 'getWrapper', 'getWrapperData', 'getStreamType', 'getUri')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->once()) + ->method('getMetadata') + ->will($this->returnValue(array())); + // Called twice for getWrapper and getWrapperData + $a->expects($this->exactly(1)) + ->method('getWrapper') + ->will($this->returnValue('wrapper')); + $a->expects($this->once()) + ->method('getWrapperData') + ->will($this->returnValue(array())); + $a->expects($this->once()) + ->method('getStreamType') + ->will($this->returnValue('baz')); + $a->expects($this->once()) + ->method('getUri') + ->will($this->returnValue('path/to/foo')); + + $d = new CachingEntityBody($a); + $this->assertEquals(array(), $d->getMetaData()); + $this->assertEquals('wrapper', $d->getWrapper()); + $this->assertEquals(array(), $d->getWrapperData()); + $this->assertEquals('baz', $d->getStreamType()); + $this->assertEquals('path/to/foo', $d->getUri()); + } + + public function testWrapsCustomData() + { + $a = $this->getMockBuilder('Guzzle\Http\EntityBody') + ->setMethods(array('getCustomData', 'setCustomData')) + ->setConstructorArgs(array(fopen(__FILE__, 'r'))) + ->getMock(); + + $a->expects($this->exactly(1)) + ->method('getCustomData') + ->with('foo') + ->will($this->returnValue('bar')); + + $a->expects($this->exactly(1)) + ->method('setCustomData') + ->with('foo', 'bar') + ->will($this->returnSelf()); + + $d = new CachingEntityBody($a); + $this->assertSame($d, $d->setCustomData('foo', 'bar')); + $this->assertEquals('bar', $d->getCustomData('foo')); + } + + public function testClosesBothStreams() + { + $s = fopen('php://temp', 'r'); + $a = EntityBody::factory($s); + $d = new CachingEntityBody($a); + $d->close(); + $this->assertFalse(is_resource($s)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php new file mode 100644 index 0000000..4a91a18 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php @@ -0,0 +1,601 @@ +assertEquals('http://www.google.com/', $client->getBaseUrl()); + $this->assertSame($client, $client->setConfig(array( + 'test' => '123' + ))); + $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll()); + $this->assertEquals('123', $client->getConfig('test')); + $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{test}')); + $this->assertEquals('http://www.test.com/123', $client->getBaseUrl()); + $this->assertEquals('http://www.test.com/{test}', $client->getBaseUrl(false)); + + try { + $client->setConfig(false); + } catch (\InvalidArgumentException $e) { + } + } + + public function testDescribesEvents() + { + $this->assertEquals(array('client.create_request'), Client::getAllEvents()); + } + + public function testConstructorCanAcceptConfig() + { + $client = new Client('http://www.test.com/', array( + 'data' => '123' + )); + $this->assertEquals('123', $client->getConfig('data')); + } + + public function testCanUseCollectionAsConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(new Collection(array( + 'api' => 'v1', + 'key' => 'value', + 'base_url' => 'http://www.google.com/' + ))); + $this->assertEquals('v1', $client->getConfig('api')); + } + + public function testExpandsUriTemplatesUsingConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(array('api' => 'v1', 'key' => 'value', 'foo' => 'bar')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $this->assertEquals('Testing...api/v1/key/value', $ref->invoke($client, 'Testing...api/{api}/key/{key}')); + } + + public function testClientAttachersObserversToRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $logPlugin = $this->getLogPlugin(); + $client->getEventDispatcher()->addSubscriber($logPlugin); + + // Get a request from the client and ensure the the observer was + // attached to the new request + $request = $client->createRequest(); + $this->assertTrue($this->hasSubscriber($request, $logPlugin)); + } + + public function testClientReturnsValidBaseUrls() + { + $client = new Client('http://www.{foo}.{data}/', array( + 'data' => '123', + 'foo' => 'bar' + )); + $this->assertEquals('http://www.bar.123/', $client->getBaseUrl()); + $client->setBaseUrl('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); + } + + public function testClientAddsCurlOptionsToRequests() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1', + // Adds the option using the curl values + 'curl.options' => array( + 'CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', + 'abc' => 'foo', + 'blacklist' => 'abc', + 'debug' => true + ) + )); + + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('foo', $options->get('abc')); + $this->assertEquals('abc', $options->get('blacklist')); + } + + public function testClientAllowsFineGrainedSslControlButIsSecureByDefault() + { + $client = new Client('https://www.secure.com/'); + + // secure by default + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertTrue($options->get(CURLOPT_SSL_VERIFYPEER)); + + // set a capath if you prefer + $client = new Client('https://www.secure.com/'); + $client->setSslVerification(__DIR__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__DIR__, $options->get(CURLOPT_CAPATH)); + } + + public function testConfigSettingsControlSslConfiguration() + { + // Use the default ca certs on the system + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => 'system')); + $this->assertNull($client->getConfig('curl.options')); + // Can set the cacert value as well + $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => false)); + $options = $client->getConfig('curl.options'); + $this->assertArrayNotHasKey(CURLOPT_CAINFO, $options); + $this->assertSame(false, $options[CURLOPT_SSL_VERIFYPEER]); + $this->assertSame(0, $options[CURLOPT_SSL_VERIFYHOST]); + } + + public function testClientAllowsUnsafeOperationIfRequested() + { + // be really unsafe if you insist + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(false); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertFalse($options->get(CURLOPT_SSL_VERIFYPEER)); + $this->assertNull($options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionForInvalidCertificate() + { + $client = new Client('https://www.secure.com/'); + $client->setSslVerification('/path/to/missing/file'); + } + + public function testClientAllowsSettingSpecificSslCaInfo() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + + $client->setSslVerification(__FILE__); + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertSame(__FILE__, $options->get(CURLOPT_CAINFO)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInadvertentInsecureVerifyHostSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, true, true); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testClientPreventsInvalidVerifyPeerSetting() + { + // set a file other than the provided cacert.pem + $client = new Client('https://www.secure.com/', array( + 'api' => 'v1' + )); + $client->setSslVerification(__FILE__, 'yes'); + } + + public function testClientAddsParamsToRequests() + { + Version::$emitWarnings = false; + $client = new Client('http://www.example.com', array( + 'api' => 'v1', + 'request.params' => array( + 'foo' => 'bar', + 'baz' => 'jar' + ) + )); + $request = $client->createRequest(); + $this->assertEquals('bar', $request->getParams()->get('foo')); + $this->assertEquals('jar', $request->getParams()->get('baz')); + Version::$emitWarnings = true; + } + + public function urlProvider() + { + $u = $this->getServer()->getUrl() . 'base/'; + $u2 = $this->getServer()->getUrl() . 'base?z=1'; + return array( + array($u, '', $u), + array($u, 'relative/path/to/resource', $u . 'relative/path/to/resource'), + array($u, 'relative/path/to/resource?a=b&c=d', $u . 'relative/path/to/resource?a=b&c=d'), + array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'), + array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'), + array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d&z=1'), + array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1'), + array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?another=query&z=1') + ); + } + + /** + * @dataProvider urlProvider + */ + public function testBuildsRelativeUrls($baseUrl, $url, $result) + { + $client = new Client($baseUrl); + $this->assertEquals($result, $client->get($url)->getUrl()); + } + + public function testAllowsConfigsToBeChangedAndInjectedInBaseUrl() + { + $client = new Client('http://{a}/{b}'); + $this->assertEquals('http:///', $client->getBaseUrl()); + $this->assertEquals('http://{a}/{b}', $client->getBaseUrl(false)); + $client->setConfig(array( + 'a' => 'test.com', + 'b' => 'index.html' + )); + $this->assertEquals('http://test.com/index.html', $client->getBaseUrl()); + } + + public function testCreatesRequestsWithDefaultValues() + { + $client = new Client($this->getServer()->getUrl() . 'base'); + + // Create a GET request + $request = $client->createRequest(); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a DELETE request + $request = $client->createRequest('DELETE'); + $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a HEAD request with custom headers + $request = $client->createRequest('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + $this->assertEquals('http://www.test.com/', $request->getUrl()); + + // Create a PUT request + $request = $client->createRequest('PUT'); + $this->assertEquals('PUT', $request->getMethod()); + + // Create a PUT request with injected config + $client->getConfig()->set('a', 1)->set('b', 2); + $request = $client->createRequest('PUT', '/path/{a}?q={b}'); + $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2'); + } + + public function testClientHasHelperMethodsForCreatingRequests() + { + $url = $this->getServer()->getUrl(); + $client = new Client($url . 'base'); + $this->assertEquals('GET', $client->get()->getMethod()); + $this->assertEquals('PUT', $client->put()->getMethod()); + $this->assertEquals('POST', $client->post()->getMethod()); + $this->assertEquals('HEAD', $client->head()->getMethod()); + $this->assertEquals('DELETE', $client->delete()->getMethod()); + $this->assertEquals('OPTIONS', $client->options()->getMethod()); + $this->assertEquals('PATCH', $client->patch()->getMethod()); + $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl()); + $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl()); + $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl()); + } + + public function testClientInjectsConfigsIntoUrls() + { + $client = new Client('http://www.test.com/api/v1', array( + 'test' => '123' + )); + $request = $client->get('relative/{test}'); + $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl()); + } + + public function testAllowsEmptyBaseUrl() + { + $client = new Client(); + $request = $client->get('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $request->setResponse(new Response(200), true); + $request->send(); + } + + public function testAllowsCustomCurlMultiObjects() + { + $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send')); + $mock->expects($this->once()) + ->method('add') + ->will($this->returnSelf()); + $mock->expects($this->once()) + ->method('send') + ->will($this->returnSelf()); + + $client = new Client(); + $client->setCurlMulti($mock); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $client->send($request); + } + + public function testClientSendsMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + + $responses = array( + new Response(200), + new Response(201), + new Response(202) + ); + + $mock->addResponse($responses[0]); + $mock->addResponse($responses[1]); + $mock->addResponse($responses[2]); + + $client->getEventDispatcher()->addSubscriber($mock); + + $requests = array( + $client->get(), + $client->head(), + $client->put('/', null, 'test') + ); + + $this->assertEquals(array( + $responses[0], + $responses[1], + $responses[2] + ), $client->send($requests)); + } + + public function testClientSendsSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(200); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $this->assertEquals($response, $client->send($client->get())); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testClientThrowsExceptionForSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(404); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send($client->get()); + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + */ + public function testClientThrowsExceptionForMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)); + $mock->addResponse(new Response(404)); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send(array($client->get(), $client->head())); + } + + public function testQueryStringsAreNotDoubleEncoded() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + 'data' => array( + 'test' => 'a&b' + ) + )); + + $request = $client->get('{/path*}{?query,data*}'); + $this->assertEquals('http://test.com/foo/bar?query=hi%20there&test=a%26b', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + $this->assertEquals('a&b', $request->getQuery()->get('test')); + } + + public function testQueryStringsAreNotDoubleEncodedUsingAbsolutePaths() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + )); + $request = $client->get('http://test.com{?query}'); + $this->assertEquals('http://test.com?query=hi%20there', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + } + + public function testAllowsUriTemplateInjection() + { + $client = new Client('http://test.com'); + $ref = new \ReflectionMethod($client, 'getUriTemplate'); + $ref->setAccessible(true); + $a = $ref->invoke($client); + $this->assertSame($a, $ref->invoke($client)); + $client->setUriTemplate(new UriTemplate()); + $this->assertNotSame($a, $ref->invoke($client)); + } + + public function testAllowsCustomVariablesWhenExpandingTemplates() + { + $client = new Client('http://test.com', array('test' => 'hi')); + $ref = new \ReflectionMethod($client, 'expandTemplate'); + $ref->setAccessible(true); + $uri = $ref->invoke($client, 'http://{test}{?query*}', array('query' => array('han' => 'solo'))); + $this->assertEquals('http://hi?han=solo', $uri); + } + + public function testUriArrayAllowsCustomTemplateVariables() + { + $client = new Client(); + $vars = array( + 'var' => 'hi' + ); + $this->assertEquals('/hi', (string) $client->createRequest('GET', array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->get(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->put(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->post(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->head(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->options(array('/{var}', $vars))->getUrl()); + } + + public function testAllowsDefaultHeaders() + { + Version::$emitWarnings = false; + $default = array('X-Test' => 'Hi!'); + $other = array('X-Other' => 'Foo'); + + $client = new Client(); + $client->setDefaultHeaders($default); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + $client->setDefaultHeaders(new Collection($default)); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + + $request = $client->createRequest('GET', null, $other); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET', null, new Collection($other)); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET'); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + Version::$emitWarnings = true; + } + + public function testDontReuseCurlMulti() + { + $client1 = new Client(); + $client2 = new Client(); + $this->assertNotSame($client1->getCurlMulti(), $client2->getCurlMulti()); + } + + public function testGetDefaultUserAgent() + { + $client = new Client(); + $agent = $this->readAttribute($client, 'userAgent'); + $version = curl_version(); + $testAgent = sprintf('Guzzle/%s curl/%s PHP/%s', Version::VERSION, $version['version'], PHP_VERSION); + $this->assertEquals($agent, $testAgent); + + $client->setUserAgent('foo'); + $this->assertEquals('foo', $this->readAttribute($client, 'userAgent')); + } + + public function testOverwritesUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com', array('User-agent' => 'foo')); + $this->assertEquals('foo', (string) $request->getHeader('User-Agent')); + } + + public function testUsesDefaultUserAgent() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com'); + $this->assertContains('Guzzle/', (string) $request->getHeader('User-Agent')); + } + + public function testCanSetDefaultRequestOptions() + { + $client = new Client(); + $client->getConfig()->set('request.options', array( + 'query' => array('test' => '123', 'other' => 'abc'), + 'headers' => array('Foo' => 'Bar', 'Baz' => 'Bam') + )); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test')); + // Explicit options on a request should overrule default options + $this->assertEquals('Test', (string) $request->getHeader('Foo')); + $this->assertEquals('hello', $request->getQuery()->get('test')); + // Default options should still be set + $this->assertEquals('abc', $request->getQuery()->get('other')); + $this->assertEquals('Bam', (string) $request->getHeader('Baz')); + } + + public function testCanSetSetOptionsOnRequests() + { + $client = new Client(); + $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'), null, array( + 'cookies' => array('michael' => 'test') + )); + $this->assertEquals('test', $request->getCookie('michael')); + } + + public function testHasDefaultOptionsHelperMethods() + { + $client = new Client(); + // With path + $client->setDefaultOption('headers/foo', 'bar'); + $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); + // With simple key + $client->setDefaultOption('allow_redirects', false); + $this->assertFalse($client->getDefaultOption('allow_redirects')); + + $this->assertEquals(array( + 'headers' => array('foo' => 'bar'), + 'allow_redirects' => false + ), $client->getConfig('request.options')); + + $request = $client->get('/'); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testHeadCanUseOptions() + { + $client = new Client(); + $head = $client->head('http://www.foo.com', array(), array('query' => array('foo' => 'bar'))); + $this->assertEquals('bar', $head->getQuery()->get('foo')); + } + + public function testCanSetRelativeUrlStartingWithHttp() + { + $client = new Client('http://www.foo.com'); + $this->assertEquals( + 'http://www.foo.com/httpfoo', + $client->createRequest('GET', 'httpfoo')->getUrl() + ); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php new file mode 100644 index 0000000..5bf28de --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php @@ -0,0 +1,947 @@ +getEventDispatcher()->addListener('request.sent', function (Event $e) use ($that) { + $that->requestHandle = $e['handle']; + }); + + return $request; + } + + public function setUp() + { + $this->requestHandle = null; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructorExpectsCurlResource() + { + $h = new CurlHandle(false, array()); + } + + public function testConstructorExpectsProperOptions() + { + $h = curl_init($this->getServer()->getUrl()); + try { + $ha = new CurlHandle($h, false); + $this->fail('Expected InvalidArgumentException'); + } catch (\InvalidArgumentException $e) { + } + + $ha = new CurlHandle($h, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + + $ha = new CurlHandle($h, new Collection(array( + CURLOPT_URL => $this->getServer()->getUrl() + ))); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + } + + public function testConstructorInitializesObject() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertSame($handle, $h->getHandle()); + $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), $h->getOptions()->get(CURLOPT_URL)); + } + + public function testStoresStdErr() + { + $request = RequestFactory::getInstance()->create('GET', 'http://test.com'); + $request->getCurlOptions()->set('debug', true); + $h = CurlHandle::factory($request); + $this->assertEquals($h->getStderr(true), $h->getOptions()->get(CURLOPT_STDERR)); + $this->assertInternalType('resource', $h->getStderr(true)); + $this->assertInternalType('string', $h->getStderr(false)); + $r = $h->getStderr(true); + fwrite($r, 'test'); + $this->assertEquals('test', $h->getStderr(false)); + } + + public function testStoresCurlErrorNumber() + { + $h = new CurlHandle(curl_init('http://test.com'), array(CURLOPT_URL => 'http://test.com')); + $this->assertEquals(CURLE_OK, $h->getErrorNo()); + $h->setErrorNo(CURLE_OPERATION_TIMEOUTED); + $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo()); + } + + public function testAccountsForMissingStdErr() + { + $handle = curl_init('http://www.test.com/'); + $h = new CurlHandle($handle, array( + CURLOPT_URL => 'http://www.test.com/' + )); + $this->assertNull($h->getStderr(false)); + } + + public function testDeterminesIfResourceIsAvailable() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array()); + $this->assertTrue($h->isAvailable()); + + // Mess it up by closing the handle + curl_close($handle); + $this->assertFalse($h->isAvailable()); + + // Mess it up by unsetting the handle + $handle = null; + $this->assertFalse($h->isAvailable()); + } + + public function testWrapsErrorsAndInfo() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + $settings = array( + CURLOPT_PORT => 123, + CURLOPT_CONNECTTIMEOUT_MS => 1, + CURLOPT_TIMEOUT_MS => 1 + ); + + $handle = curl_init($this->getServer()->getUrl()); + curl_setopt_array($handle, $settings); + $h = new CurlHandle($handle, $settings); + @curl_exec($handle); + + $errors = array( + "couldn't connect to host", + 'timeout was reached', + 'connection time-out', + 'connect() timed out!', + 'failed connect to 127.0.0.1:123; connection refused', + 'failed to connect to 127.0.0.1 port 123: connection refused' + ); + $this->assertTrue(in_array(strtolower($h->getError()), $errors), $h->getError() . ' was not the error'); + + $this->assertTrue($h->getErrorNo() > 0); + + $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL)); + $this->assertInternalType('array', $h->getInfo()); + + curl_close($handle); + $this->assertEquals(null, $h->getInfo('url')); + } + + public function testGetInfoWithoutDebugMode() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get($this->getServer()->getUrl()); + $response = $request->send(); + + $info = $response->getInfo(); + $this->assertFalse(empty($info)); + $this->assertEquals($this->getServer()->getUrl(), $info['url']); + } + + public function testWrapsCurlOptions() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_AUTOREFERER => true, + CURLOPT_BUFFERSIZE => 1024 + )); + + $this->assertEquals(true, $h->getOptions()->get(CURLOPT_AUTOREFERER)); + $this->assertEquals(1024, $h->getOptions()->get(CURLOPT_BUFFERSIZE)); + } + + /** + * Data provider for factory tests + * + * @return array + */ + public function dataProvider() + { + $testFile = __DIR__ . '/../../../../../phpunit.xml.dist'; + + $postBody = new QueryString(array('file' => '@' . $testFile)); + $qs = new QueryString(array( + 'x' => 'y', + 'z' => 'a' + )); + + $client = new Client(); + $userAgent = $client->getDefaultUserAgent(); + $auth = base64_encode('michael:123'); + $testFileSize = filesize($testFile); + + $tests = array( + // Send a regular GET + array('GET', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + // Test that custom request methods can be used + array('TRACE', 'http://www.google.com/', null, null, array( + CURLOPT_CUSTOMREQUEST => 'TRACE' + )), + // Send a GET using a port + array('GET', 'http://127.0.0.1:8080', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_PORT => 8080, + CURLOPT_HTTPHEADER => array('Accept:', 'Host: 127.0.0.1:8080', 'User-Agent: ' . $userAgent), + )), + // Send a HEAD request + array('HEAD', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + CURLOPT_NOBODY => 1 + )), + // Send a GET using basic auth + array('GET', 'https://michael:123@127.0.0.1/index.html?q=2', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1', + 'Authorization: Basic ' . $auth, + 'User-Agent: ' . $userAgent + ), + CURLOPT_PORT => 443 + )), + // Send a GET request with custom headers + array('GET', 'http://127.0.0.1:8124/', array( + 'x-test-data' => 'Guzzle' + ), null, array( + CURLOPT_PORT => 8124, + CURLOPT_HTTPHEADER => array( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'x-test-data: Guzzle', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'x-test-data' => 'Guzzle' + )), + // Send a POST using a query string + array('POST', 'http://127.0.0.1:8124/post.php', null, $qs, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POSTFIELDS => 'x=y&z=a', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a PUT using raw data + array('PUT', 'http://127.0.0.1:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_READFUNCTION => 'callback', + CURLOPT_INFILESIZE => filesize($testFile), + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + '!Expect' => null, + 'Content-Length' => $testFileSize, + '!Transfer-Encoding' => null + )), + // Send a POST request using an array of fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array( + 'x' => 'y', + 'a' => 'b' + ), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => 'x=y&a=b', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data and a custom content-type + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_INFILESIZE => 14, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Content-Length' => '14', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data, a custom content-type, and use chunked encoding + array('POST', 'http://127.0.0.1:8124/post.php', array( + 'Content-Type' => 'application/json', + 'Transfer-Encoding' => 'chunked' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_UPLOAD => true, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Transfer-Encoding: chunked', + 'Content-Type: application/json', + 'User-Agent: ' . $userAgent + ), + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + '!Expect' => null, + 'Transfer-Encoding' => 'chunked', + '!Content-Length' => '' + )), + // Send a POST request with no body + array('POST', 'http://127.0.0.1:8124/post.php', null, '', array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a POST request with empty post fields + array('POST', 'http://127.0.0.1:8124/post.php', null, array(), array( + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Transfer-Encoding' => null + )), + // Send a PATCH request + array('PATCH', 'http://127.0.0.1:8124/patch.php', null, 'body', array( + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + )), + // Send a DELETE request with a body + array('DELETE', 'http://127.0.0.1:8124/delete.php', null, 'body', array( + CURLOPT_CUSTOMREQUEST => 'DELETE', + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Accept:', + 'Host: 127.0.0.1:8124', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '4', + '!Expect' => null, + '!Transfer-Encoding' => null + )), + + /** + * Send a request with empty path and a fragment - the fragment must be + * stripped out before sending it to curl + * + * @issue 453 + * @link https://github.com/guzzle/guzzle/issues/453 + */ + array('GET', 'http://www.google.com#head', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + ); + + $postTest = array('POST', 'http://127.0.0.1:8124/post.php', null, $postBody, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => array( + 'file' => '@' . $testFile . ';filename=phpunit.xml.dist;type=application/octet-stream' + ), + CURLOPT_HTTPHEADER => array ( + 'Accept:', + 'Host: 127.0.0.1:8124', + 'Content-Type: multipart/form-data', + 'Expect: 100-Continue', + 'User-Agent: ' . $userAgent + ) + ), array( + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '*', + 'Expect' => '100-Continue', + 'Content-Type' => 'multipart/form-data; boundary=*', + '!Transfer-Encoding' => null + )); + + if (version_compare(phpversion(), '5.5.0', '>=')) { + $postTest[4][CURLOPT_POSTFIELDS] = array( + 'file' => new \CurlFile($testFile, 'application/octet-stream', 'phpunit.xml.dist') + ); + } + + $tests[] = $postTest; + + return $tests; + } + + /** + * @dataProvider dataProvider + */ + public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null) + { + $client = new Client(); + $request = $client->createRequest($method, $url, $headers, $body); + $request->getCurlOptions()->set('debug', true); + + $originalRequest = clone $request; + $curlTest = clone $request; + $handle = CurlHandle::factory($curlTest); + + $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle); + $o = $handle->getOptions()->getAll(); + + // Headers are case-insensitive + if (isset($o[CURLOPT_HTTPHEADER])) { + $o[CURLOPT_HTTPHEADER] = array_map('strtolower', $o[CURLOPT_HTTPHEADER]); + } + if (isset($options[CURLOPT_HTTPHEADER])) { + $options[CURLOPT_HTTPHEADER] = array_map('strtolower', $options[CURLOPT_HTTPHEADER]); + } + + $check = 0; + foreach ($options as $key => $value) { + $check++; + $this->assertArrayHasKey($key, $o, '-> Check number ' . $check); + if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) { + $this->assertEquals('callback', $value, '-> Check number ' . $check); + } else { + $this->assertTrue($value == $o[$key], '-> Check number ' . $check . ' - ' . var_export($value, true) . ' != ' . var_export($o[$key], true)); + } + } + + // If we are testing the actual sent headers + if ($expectedHeaders) { + + // Send the request to the test server + $client = new Client($this->getServer()->getUrl()); + $request->setClient($client); + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + // Get the request that was sent and create a request that we expected + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($method, $requests[0]->getMethod()); + + $test = $this->compareHeaders($expectedHeaders, $requests[0]->getHeaders()); + $this->assertFalse($test, $test . "\nSent: \n" . $request . "\n\n" . $requests[0]); + + // Ensure only one Content-Length header is sent + if ($request->getHeader('Content-Length')) { + $this->assertEquals((string) $request->getHeader('Content-Length'), (string) $requests[0]->getHeader('Content-Length')); + } + } + } + + public function testFactoryUsesSpecifiedProtocol() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:8124/'); + $request->setProtocolVersion('1.1'); + $handle = CurlHandle::factory($request); + $options = $handle->getOptions(); + $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]); + } + + public function testUploadsPutData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->getCurlOptions()->set('debug', true); + $request->setBody(EntityBody::factory('test'), 'text/plain', false); + $request->getCurlOptions()->set('progress', true); + + $o = $this->getWildcardObserver($request); + $request->send(); + + // Make sure that the events were dispatched + $this->assertTrue($o->has('curl.callback.progress')); + + // Ensure that the request was received exactly as intended + $r = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('put / http/1.1', $sent); + $this->assertContains('host: 127.0.0.1', $sent); + $this->assertContains('user-agent:', $sent); + $this->assertContains('content-type: text/plain', $sent); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenLengthCannotBeDetermined() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')), 'text/plain'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[1]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[1]->hasHeader('Content-Length')); + } + + public function testUploadsPutDataUsingChunkedEncodingWhenForced() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', array('Transfer-Encoding' => 'chunked'), 'hi!'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[0]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[0]->hasHeader('Content-Length')); + $this->assertEquals('hi!', $r[0]->getBody(true)); + } + + public function testSendsPostRequestsWithFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFields(array( + 'a' => 'b', + 'c' => 'ay! ~This is a test, isn\'t it?' + )); + $request->send(); + + // Make sure that the request was sent correctly + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', (string) $r[0]->getBody()); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(56, (string) $r[0]->getHeader('Content-Length')); + $sent = strtolower($r[0]); + $this->assertContains('post / http/1.1', $sent); + $this->assertContains('content-type: application/x-www-form-urlencoded; charset=utf-8', $sent); + } + + public function testSendsPostRequestsWithFiles() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFiles(array( + 'foo' => __FILE__, + )); + $request->addPostFields(array( + 'bar' => 'baz', + 'arr' => array('a' => 1, 'b' => 2), + )); + $this->updateForHandle($request); + $request->send(); + + // Ensure the CURLOPT_POSTFIELDS option was set properly + $options = $this->requestHandle->getOptions()->getAll(); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=CurlHandleTest.php;type=text/x-', $options[CURLOPT_POSTFIELDS]['foo']); + } else{ + $this->assertInstanceOf('CURLFile', $options[CURLOPT_POSTFIELDS]['foo']); + } + $this->assertEquals('baz', $options[CURLOPT_POSTFIELDS]['bar']); + $this->assertEquals('1', $options[CURLOPT_POSTFIELDS]['arr[a]']); + $this->assertEquals('2', $options[CURLOPT_POSTFIELDS]['arr[b]']); + // Ensure that a Content-Length header was sent by cURL + $this->assertTrue($request->hasHeader('Content-Length')); + } + + public function testCurlConfigurationOptionsAreSet() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->setClient(new Client('http://www.example.com')); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 99); + $request->getCurlOptions()->set('curl.fake_opt', 99); + $request->getCurlOptions()->set(CURLOPT_PORT, 8181); + $handle = CurlHandle::factory($request); + $this->assertEquals(99, $handle->getOptions()->get(CURLOPT_CONNECTTIMEOUT)); + $this->assertEquals(8181, $handle->getOptions()->get(CURLOPT_PORT)); + $this->assertNull($handle->getOptions()->get('curl.fake_opt')); + $this->assertNull($handle->getOptions()->get('fake_opt')); + } + + public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $handle = CurlHandle::factory($request); + $handle->updateRequestFromTransfer($request); + } + + public function testCanSendBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('PUT /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testCanSendPostBodyAsString() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, 'foo'); + $request->getCurlOptions()->set('body_as_string', true); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST /', $requests[0]); + $this->assertContains("\nfoo", $requests[0]); + $this->assertContains('content-length: 3', $requests[0]); + $this->assertNotContains('content-type', $requests[0]); + } + + public function testAllowsWireTransferInfoToBeEnabled() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $handle = CurlHandle::factory($request); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_STDERR)); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_VERBOSE)); + } + + public function testAddsCustomCurlOptions() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 200); + $handle = CurlHandle::factory($request); + $this->assertEquals(200, $handle->getOptions()->get(CURLOPT_TIMEOUT)); + } + + public function testSendsPostUploadsWithContentDispositionHeaders() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $fileToUpload = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post(); + $request->addPostFile('foo', $fileToUpload, 'application/json'); + $request->addPostFile('foo', __FILE__); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $body = (string) $requests[0]->getBody(); + + $this->assertContains('Content-Disposition: form-data; name="foo[0]"; filename="', $body); + $this->assertContains('Content-Type: application/json', $body); + $this->assertContains('Content-Type: text/x-', $body); + $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body); + } + + public function requestMethodProvider() + { + return array(array('POST'), array('PUT'), array('PATCH')); + } + + /** + * @dataProvider requestMethodProvider + */ + public function testSendsRequestsWithNoBodyUsingContentLengthZero($method) + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $client->createRequest($method)->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($requests[0]->hasHeader('Transfer-Encoding')); + $this->assertTrue($requests[0]->hasHeader('Content-Length')); + $this->assertEquals('0', (string) $requests[0]->getHeader('Content-Length')); + } + + /** + * @dataProvider provideCurlConfig + */ + public function testParseCurlConfigConvertsStringKeysToConstantKeys($options, $expected) + { + $actual = CurlHandle::parseCurlConfig($options); + $this->assertEquals($expected, $actual); + } + + /** + * Data provider for curl configurations + * + * @return array + */ + public function provideCurlConfig() + { + return array( + // Conversion of option name to constant value + array( + array( + 'CURLOPT_PORT' => 10, + 'CURLOPT_TIMEOUT' => 99 + ), + array( + CURLOPT_PORT => 10, + CURLOPT_TIMEOUT => 99 + ) + ), + // Keeps non constant options + array( + array('debug' => true), + array('debug' => true) + ), + // Conversion of constant names to constant values + array( + array('debug' => 'CURLPROXY_HTTP'), + array('debug' => CURLPROXY_HTTP) + ) + ); + } + + public function testSeeksToBeginningOfStreamWhenSending() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + $request->send(); + $request->send(); + + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals('test', (string) $received[0]->getBody()); + $this->assertEquals('test', (string) $received[1]->getBody()); + } + + public function testAllowsCurloptEncodingToBeSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', null); + $request->getCurlOptions()->set(CURLOPT_ENCODING, ''); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: */*', $received[0]); + $this->assertContainsIns('accept-encoding: ', $received[0]); + } + + public function testSendsExpectHeaderWhenSizeIsGreaterThanCutoff() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, 'test'); + // Start sending the expect header to 2 bytes + $this->updateForHandle($request); + $request->setExpectHeaderCutoff(2)->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertContains('Expect: 100-Continue', $options[CURLOPT_HTTPHEADER]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('expect: 100-continue', $received[0]); + } + + public function testSetsCurloptEncodingWhenAcceptEncodingHeaderIsSet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array( + 'Accept' => 'application/json', + 'Accept-Encoding' => 'gzip, deflate', + )); + $this->updateForHandle($request); + $request->send(); + $options = $this->requestHandle->getOptions()->getAll(); + $this->assertSame('gzip, deflate', $options[CURLOPT_ENCODING]); + $received = $this->getServer()->getReceivedRequests(false); + $this->assertContainsIns('accept: application/json', $received[0]); + $this->assertContainsIns('accept-encoding: gzip, deflate', $received[0]); + } + + public function testSendsPostFieldsForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => 'baz', + 'baz' => 'bar' + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertEquals( + 'application/x-www-form-urlencoded; charset=utf-8', + (string) $requests[0]->getHeader('Content-Type') + ); + $this->assertEquals(15, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo=baz&baz=bar', (string) $requests[0]->getBody()); + } + + public function testSendsPostFilesForNonPostRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client(); + $request = $client->put($this->getServer()->getUrl(), null, array( + 'foo' => '@' . __FILE__ + )); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PUT', $requests[0]->getMethod()); + $this->assertContains('multipart/form-data', (string) $requests[0]->getHeader('Content-Type')); + $this->assertContains('testSendsPostFilesForNonPostRequests', (string) $requests[0]->getBody()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php new file mode 100644 index 0000000..e04141c --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php @@ -0,0 +1,110 @@ +multi = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorSetsMaxHandles() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::MAX_HANDLES, $this->readAttribute($m, 'maxHandles')); + } + + public function testConstructorSetsSelectTimeout() + { + $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); + $this->assertEquals(self::SELECT_TIMEOUT, $this->readAttribute($m, 'selectTimeout')); + } + + public function testAddingRequestsAddsToQueue() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->assertSame($this->multi, $this->multi->add($r)); + $this->assertEquals(1, count($this->multi)); + $this->assertEquals(array($r), $this->multi->all()); + + $this->assertTrue($this->multi->remove($r)); + $this->assertFalse($this->multi->remove($r)); + $this->assertEquals(0, count($this->multi)); + } + + public function testResetClearsState() + { + $r = new Request('GET', 'http://www.foo.com'); + $this->multi->add($r); + $this->multi->reset(); + $this->assertEquals(0, count($this->multi)); + } + + public function testSendWillSendQueuedRequestsFirst() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $events = array(); + $client->getCurlMulti()->getEventDispatcher()->addListener( + CurlMultiProxy::ADD_REQUEST, + function ($e) use (&$events) { + $events[] = $e; + } + ); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.complete', function () use ($client) { + $client->get('/foo')->send(); + }); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + $this->assertEquals($this->getServer()->getUrl(), $received[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'foo', $received[1]->getUrl()); + $this->assertEquals(2, count($events)); + } + + public function testTrimsDownMaxHandleCount() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $client->setCurlMulti(new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT)); + $request = $client->get(); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $handles = $this->readAttribute($client->getCurlMulti(), 'handles'); + $this->assertEquals(2, count($handles)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php new file mode 100644 index 0000000..1272281 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php @@ -0,0 +1,455 @@ +multi = new MockMulti(); + } + + public function tearDown() + { + unset($this->multi); + } + + public function testConstructorCreateMultiHandle() + { + $this->assertInternalType('resource', $this->multi->getHandle()); + $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle())); + } + + public function testDestructorClosesMultiHandle() + { + $handle = $this->multi->getHandle(); + $this->multi->__destruct(); + $this->assertFalse(is_resource($handle)); + } + + public function testRequestsCanBeAddedAndCounted() + { + $multi = new CurlMulti(); + $request1 = new Request('GET', 'http://www.google.com/'); + $multi->add($request1); + $this->assertEquals(array($request1), $multi->all()); + $request2 = new Request('POST', 'http://www.google.com/'); + $multi->add($request2); + $this->assertEquals(array($request1, $request2), $multi->all()); + $this->assertEquals(2, count($multi)); + } + + public function testRequestsCanBeRemoved() + { + $request1 = new Request('GET', 'http://www.google.com/'); + $this->multi->add($request1); + $request2 = new Request('PUT', 'http://www.google.com/'); + $this->multi->add($request2); + $this->assertEquals(array($request1, $request2), $this->multi->all()); + $this->assertTrue($this->multi->remove($request1)); + $this->assertFalse($this->multi->remove($request1)); + $this->assertEquals(array($request2), $this->multi->all()); + } + + public function testsResetRemovesRequestsAndResetsState() + { + $this->multi->add(new Request('GET', 'http://www.google.com/')); + $this->multi->reset(); + $this->assertEquals(array(), $this->multi->all()); + } + + public function testSendsRequestsThroughCurl() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n" . + "data" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->send(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + + $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data'); + $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == ''); + $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204'); + $this->assertNotEquals((string) $response1, (string) $response2); + } + + public function testSendsThroughCurlAndAggregatesRequestExceptions() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n" . + "data", + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n", + "HTTP/1.1 404 Not Found\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('HEAD', $this->getServer()->getUrl()); + $request3 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->add($request3); + + try { + $this->multi->send(); + $this->fail('MultiTransferException not thrown when aggregating request exceptions'); + } catch (MultiTransferException $e) { + + $this->assertTrue($e->containsRequest($request1)); + $this->assertTrue($e->containsRequest($request2)); + $this->assertTrue($e->containsRequest($request3)); + $this->assertInstanceOf('ArrayIterator', $e->getIterator()); + $this->assertEquals(1, count($e)); + $exceptions = $e->getIterator(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $response3 = $request3->getResponse(); + + $this->assertNotEquals((string) $response1, (string) $response2); + $this->assertNotEquals((string) $response3, (string) $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3); + + $failed = $exceptions[0]->getResponse(); + $this->assertEquals(404, $failed->getStatusCode()); + $this->assertEquals(1, count($e)); + + // Test the IteratorAggregate functionality + foreach ($e as $except) { + $this->assertEquals($failed, $except->getResponse()); + } + + $this->assertEquals(1, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(3, count($e->getAllRequests())); + } + } + + public function testCurlErrorsAreCaught() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + try { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $request->setClient(new Client()); + $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true); + $request->getCurlOptions()->set(CURLOPT_FORBID_REUSE, true); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, 5); + $request->send(); + $this->fail('CurlException not thrown'); + } catch (CurlException $e) { + $m = $e->getMessage(); + $this->assertContains('[curl] ', $m); + $this->assertContains('[url] http://127.0.0.1:9876/', $m); + $this->assertInternalType('array', $e->getCurlInfo()); + } + } + + public function testRemovesQueuedRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $r = new Response(200); + $request->setClient(new Client()); + $request->setResponse($r, true); + $this->multi->add($request); + $this->multi->send(); + $this->assertSame($r, $request->getResponse()); + } + + public function testRemovesQueuedRequestsAddedInTransit() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.receive.status_line', function (Event $event) use ($client) { + // Create a request using a queued response + $request = $client->get()->setResponse(new Response(200), true); + $request->send(); + }); + $r->send(); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testCatchesExceptionsBeforeSendingSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $multi = new CurlMulti(); + $client->setCurlMulti($multi); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Testing!'); + }); + try { + $request->send(); + $this->fail('Did not throw'); + } catch (\RuntimeException $e) { + // Ensure it was removed + $this->assertEquals(0, count($multi)); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\ExceptionCollection + * @expectedExceptionMessage Thrown before sending! + */ + public function testCatchesExceptionsBeforeSendingMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Thrown before sending!'); + }); + $client->send(array($request)); + } + + public function testCatchesExceptionsWhenRemovingQueuedRequests() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.sent', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + public function testCatchesExceptionsWhenRemovingQueuedRequestsBeforeSending() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.before_send', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + try { + $r->send(); + $this->fail('Did not throw'); + } catch (BadResponseException $e) { + $this->assertCount(0, $client->getCurlMulti()); + } + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage test + */ + public function testDoesNotCatchRandomExceptionsThrownDuringPerform() + { + $client = new Client($this->getServer()->getUrl()); + $multi = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('perform')); + $multi->expects($this->once()) + ->method('perform') + ->will($this->throwException(new \RuntimeException('test'))); + $multi->add($client->get()); + $multi->send(); + } + + public function testDoesNotSendRequestsDecliningToBeSent() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + // Create a client that is bound to fail connecting + $client = new Client('http://127.0.0.1:123', array( + 'curl.CURLOPT_PORT' => 123, + 'curl.CURLOPT_CONNECTTIMEOUT_MS' => 1, + )); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + + // Listen for request exceptions, and when they occur, first change the + // state of the request back to transferring, and then just allow it to + // exception out + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use ($multi) { + $retries = $event['request']->getParams()->get('retries'); + // Allow the first failure to retry + if ($retries == 0) { + $event['request']->setState('transfer'); + $event['request']->getParams()->set('retries', 1); + // Remove the request to try again + $multi->remove($event['request']); + $multi->add($event['request']); + } + }); + + try { + $multi->send(); + $this->fail('Did not throw an exception at all!?!'); + } catch (\Exception $e) { + $this->assertEquals(1, $request->getParams()->get('retries')); + } + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithRetry() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function(Event $event) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + } + + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess() + { + // Attempt a port that 99.9% is not listening + $client = new Client('http://127.0.0.1:123'); + $request = $client->get(); + // Ensure it times out quickly if needed + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, 1)->set(CURLOPT_CONNECTTIMEOUT_MS, 1); + + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use (&$count) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + + // Ensure that the exception was caught, and the response was set manually + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + } + + public function testHardResetReopensMultiHandle() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $stream = fopen('php://temp', 'w+'); + $client = new Client($this->getServer()->getUrl()); + $client->getConfig()->set('curl.CURLOPT_VERBOSE', true)->set('curl.CURLOPT_STDERR', $stream); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $multi->reset(true); + $multi->add($request); + $multi->send(); + + rewind($stream); + $this->assertNotContains('Re-using existing connection', stream_get_contents($stream)); + } + + public function testThrowsMeaningfulExceptionsForCurlMultiErrors() + { + $multi = new CurlMulti(); + + // Set the state of the multi object to sending to trigger the exception + $reflector = new \ReflectionMethod('Guzzle\Http\Curl\CurlMulti', 'checkCurlResult'); + $reflector->setAccessible(true); + + // Successful + $reflector->invoke($multi, 0); + + // Known error + try { + $reflector->invoke($multi, CURLM_BAD_HANDLE); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertContains('The passed-in handle is not a valid CURLM handle.', $e->getMessage()); + $this->assertContains('CURLM_BAD_HANDLE', $e->getMessage()); + $this->assertContains(strval(CURLM_BAD_HANDLE), $e->getMessage()); + } + + // Unknown error + try { + $reflector->invoke($multi, 255); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertEquals('Unexpected cURL error: 255', $e->getMessage()); + } + } + + public function testRequestBeforeSendIncludesContentLengthHeaderIfEmptyBody() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new Request('PUT', $this->getServer()->getUrl()); + $that = $this; + $request->getEventDispatcher()->addListener('request.before_send', function ($event) use ($that) { + $that->assertEquals(0, $event['request']->getHeader('Content-Length')); + }); + $this->multi->add($request); + $this->multi->send(); + } + + public function testRemovesConflictingTransferEncodingHeader() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/', null, fopen($this->getServer()->getUrl(), 'r')); + $request->setHeader('Content-Length', 4); + $request->send(); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($received[1]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $received[1]->getHeader('Content-Length')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php new file mode 100644 index 0000000..c7b5ee6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php @@ -0,0 +1,39 @@ +getProperty('version'); + $refProperty->setAccessible(true); + $refProperty->setValue($instance, array()); + + $this->assertEquals($info, $instance->getAll()); + $this->assertEquals($info, $instance->getAll()); + + $this->assertEquals($info['version'], $instance->get('version')); + $this->assertFalse($instance->get('foo')); + } + + public function testIsSingleton() + { + $refObject = new \ReflectionClass('Guzzle\Http\Curl\CurlVersion'); + $refProperty = $refObject->getProperty('instance'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, null); + + $this->assertInstanceOf('Guzzle\Http\Curl\CurlVersion', CurlVersion::getInstance()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php new file mode 100644 index 0000000..c69e0c9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php @@ -0,0 +1,67 @@ +events[] = $event; + } + + public function testEmitsEvents() + { + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('foo'); + $request->setResponse(new Response(200)); + + // Ensure that IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + + // Attach listeners for each event type + $request->getEventDispatcher()->addListener('curl.callback.progress', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.read', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.write', array($this, 'event')); + + $mediator = new RequestMediator($request, true); + + $mediator->progress('a', 'b', 'c', 'd'); + $this->assertEquals(1, count($this->events)); + $this->assertEquals('curl.callback.progress', $this->events[0]->getName()); + + $this->assertEquals(3, $mediator->writeResponseBody('foo', 'bar')); + $this->assertEquals(2, count($this->events)); + $this->assertEquals('curl.callback.write', $this->events[1]->getName()); + $this->assertEquals('bar', $this->events[1]['write']); + $this->assertSame($request, $this->events[1]['request']); + + $this->assertEquals('foo', $mediator->readRequestBody('a', 'b', 3)); + $this->assertEquals(3, count($this->events)); + $this->assertEquals('curl.callback.read', $this->events[2]->getName()); + $this->assertEquals('foo', $this->events[2]['read']); + $this->assertSame($request, $this->events[2]['request']); + } + + public function testDoesNotUseRequestResponseBodyWhenNotCustom() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 307 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nHI", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nFI", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get()->send(); + $this->assertEquals('test', $response->getBody(true)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php new file mode 100644 index 0000000..124a44d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php @@ -0,0 +1,182 @@ +assertEquals('data', (string) $body); + $this->assertEquals(4, $body->getContentLength()); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + + $handle = fopen(__DIR__ . '/../../../../phpunit.xml.dist', 'r'); + if (!$handle) { + $this->fail('Could not open test file'); + } + $body = EntityBody::factory($handle); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertTrue($body->isLocal()); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml.dist'), $body->getContentLength()); + + // make sure that a body will return as the same object + $this->assertTrue($body === EntityBody::factory($body)); + } + + public function testFactoryCreatesTempStreamByDefault() + { + $body = EntityBody::factory(''); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + $body = EntityBody::factory(); + $this->assertEquals('PHP', $body->getWrapper()); + $this->assertEquals('TEMP', $body->getStreamType()); + } + + public function testFactoryCanCreateFromObject() + { + $body = EntityBody::factory(new QueryString(array('foo' => 'bar'))); + $this->assertEquals('foo=bar', (string) $body); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testFactoryEnsuresObjectsHaveToStringMethod() + { + EntityBody::factory(new \stdClass('a')); + } + + public function testHandlesCompression() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE'); + $size = $body->getContentLength(); + $body->compress(); + $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing'); + $this->assertEquals(gzdeflate('testing 123...testing 123'), (string) $body); + $this->assertTrue($body->getContentLength() < $size); + $this->assertTrue($body->uncompress()); + $this->assertEquals('testing 123...testing 123', (string) $body); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE'); + + if (in_array('bzip2.*', stream_get_filters())) { + $this->assertTrue($body->compress('bzip2.compress')); + $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding'); + } + + $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used'); + + // Release the body + unset($body); + + // Use gzip compression on the initial content. This will include a + // gzip header which will need to be stripped when deflating the stream + $body = EntityBody::factory(gzencode('test')); + $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate')); + $this->assertTrue($body->uncompress('zlib.inflate')); + $this->assertEquals('test', (string) $body); + unset($body); + + // Test using a very long string + $largeString = ''; + for ($i = 0; $i < 25000; $i++) { + $largeString .= chr(rand(33, 126)); + } + $body = EntityBody::factory($largeString); + $this->assertEquals($largeString, (string) $body); + $this->assertTrue($body->compress()); + $this->assertNotEquals($largeString, (string) $body); + $compressed = (string) $body; + $this->assertTrue($body->uncompress()); + $this->assertEquals($largeString, (string) $body); + $this->assertEquals($compressed, gzdeflate($largeString)); + + $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w')); + $this->assertFalse($body->compress()); + unset($body); + + unlink(__DIR__ . '/../TestData/compress_test'); + } + + public function testDeterminesContentType() + { + // Test using a string/temp stream + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertNull($body->getContentType()); + + // Use a local file + $body = EntityBody::factory(fopen(__FILE__, 'r')); + $this->assertContains('text/x-', $body->getContentType()); + } + + public function testCreatesMd5Checksum() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5()); + + $server = $this->getServer()->enqueue( + "HTTP/1.1 200 OK" . "\r\n" . + "Content-Length: 3" . "\r\n\r\n" . + "abc" + ); + + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $this->assertFalse($body->getContentMd5()); + } + + public function testSeeksToOriginalPosAfterMd5() + { + $body = EntityBody::factory('testing 123'); + $body->seek(4); + $this->assertEquals(md5('testing 123'), $body->getContentMd5()); + $this->assertEquals(4, $body->ftell()); + $this->assertEquals('ing 123', $body->read(1000)); + } + + public function testGetTypeFormBodyFactoring() + { + $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2')); + $this->assertEquals('key1=val1&key2=val2', (string) $body); + } + + public function testAllowsCustomRewind() + { + $body = EntityBody::factory('foo'); + $rewound = false; + $body->setRewindFunction(function ($body) use (&$rewound) { + $rewound = true; + return $body->seek(0); + }); + $body->seek(2); + $this->assertTrue($body->rewind()); + $this->assertTrue($rewound); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testCustomRewindFunctionMustBeCallable() + { + $body = EntityBody::factory(); + $body->setRewindFunction('foo'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php new file mode 100644 index 0000000..df3e4b7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php @@ -0,0 +1,27 @@ +assertNull($e->getError()); + $this->assertNull($e->getErrorNo()); + $this->assertSame($e, $e->setError('test', 12)); + $this->assertEquals('test', $e->getError()); + $this->assertEquals(12, $e->getErrorNo()); + + $handle = new CurlHandle(curl_init(), array()); + $e->setCurlHandle($handle); + $this->assertSame($handle, $e->getCurlHandle()); + $handle->close(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php new file mode 100644 index 0000000..12cfd36 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php @@ -0,0 +1,66 @@ +setRequest($request); + $this->assertEquals($request, $e->getRequest()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException + */ + public function testBadResponseException() + { + $e = new BadResponseException('Message'); + $response = new Response(200); + $e->setResponse($response); + $this->assertEquals($response, $e->getResponse()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesGenericErrorExceptionOnError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(307); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\BadResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesClientErrorExceptionOnClientError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(404); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ClientErrorResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesServerErrorExceptionOnServerError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(503); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ServerErrorResponseException', $e); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php new file mode 100644 index 0000000..fa4ec26 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php @@ -0,0 +1,51 @@ +addSuccessfulRequest($r1); + $e->addFailedRequest($r2); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + $this->assertEquals(array($r1, $r2), $e->getAllRequests()); + $this->assertTrue($e->containsRequest($r1)); + $this->assertTrue($e->containsRequest($r2)); + $this->assertFalse($e->containsRequest(new Request('POST', '/foo'))); + } + + public function testCanSetRequests() + { + $s = array($r1 = new Request('GET', 'http://www.foo.com')); + $f = array($r2 = new Request('GET', 'http://www.foo.com')); + $e = new MultiTransferException(); + $e->setSuccessfulRequests($s); + $e->setFailedRequests($f); + $this->assertEquals(array($r1), $e->getSuccessfulRequests()); + $this->assertEquals(array($r2), $e->getSuccessfulRequests()); + } + + public function testAssociatesExceptionsWithRequests() + { + $r1 = new Request('GET', 'http://www.foo.com'); + $re1 = new \Exception('foo'); + $re2 = new \Exception('bar'); + $e = new MultiTransferException(); + $e->add($re2); + $e->addFailedRequestWithException($r1, $re1); + $this->assertSame($re1, $e->getExceptionForFailedRequest($r1)); + $this->assertNull($e->getExceptionForFailedRequest(new Request('POST', '/foo'))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php new file mode 100644 index 0000000..cd6355f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php @@ -0,0 +1,47 @@ +decorated = EntityBody::factory('hello'); + $this->body = new IoEmittingEntityBody($this->decorated); + } + + public function testEmitsReadEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.read', function ($event) use (&$e) { + $e = $event; + }); + $this->assertEquals('hel', $this->body->read(3)); + $this->assertEquals('hel', $e['read']); + $this->assertEquals(3, $e['length']); + $this->assertSame($this->body, $e['body']); + } + + public function testEmitsWriteEvents() + { + $e = null; + $this->body->getEventDispatcher()->addListener('body.write', function ($event) use (&$e) { + $e = $event; + }); + $this->body->seek(0, SEEK_END); + $this->assertEquals(5, $this->body->write('there')); + $this->assertEquals('there', $e['write']); + $this->assertEquals(5, $e['result']); + $this->assertSame($this->body, $e['body']); + $this->assertEquals('hellothere', (string) $this->body); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php new file mode 100644 index 0000000..9447d8c --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php @@ -0,0 +1,136 @@ +mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage'); + } + + public function tearDown() + { + $this->mock = $this->request = null; + } + + public function testGetParams() + { + $request = new Request('GET', 'http://example.com'); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams()); + } + + public function testAddHeaders() + { + $this->mock->setHeader('A', 'B'); + + $this->assertEquals($this->mock, $this->mock->addHeaders(array( + 'X-Data' => '123' + ))); + + $this->assertTrue($this->mock->hasHeader('X-Data') !== false); + $this->assertTrue($this->mock->hasHeader('A') !== false); + } + + public function testAllowsHeaderToSetAsHeader() + { + $h = new Header('A', 'B'); + $this->mock->setHeader('A', $h); + $this->assertSame($h, $this->mock->getHeader('A')); + } + + public function testGetHeader() + { + $this->mock->setHeader('Test', '123'); + $this->assertEquals('123', $this->mock->getHeader('Test')); + } + + public function testGetHeaders() + { + $this->assertSame($this->mock, $this->mock->setHeaders(array('a' => 'b', 'c' => 'd'))); + $h = $this->mock->getHeaders(); + $this->assertArrayHasKey('a', $h->toArray()); + $this->assertArrayHasKey('c', $h->toArray()); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('a')); + $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('c')); + } + + public function testGetHeaderLinesUsesGlue() + { + $this->mock->setHeaders(array('a' => 'b', 'c' => 'd')); + $this->mock->addHeader('a', 'e'); + $this->mock->getHeader('a')->setGlue('!'); + $this->assertEquals(array( + 'a: b! e', + 'c: d' + ), $this->mock->getHeaderLines()); + } + + public function testHasHeader() + { + $this->assertFalse($this->mock->hasHeader('Foo')); + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->setHeader('foo', 'yoo'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->assertEquals(true, $this->mock->hasHeader('foo')); + $this->assertEquals(false, $this->mock->hasHeader('bar')); + } + + public function testRemoveHeader() + { + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->removeHeader('Foo'); + $this->assertFalse($this->mock->hasHeader('Foo')); + } + + public function testReturnsNullWhenHeaderIsNotFound() + { + $this->assertNull($this->mock->getHeader('foo')); + } + + public function testAddingHeadersPreservesOriginalHeaderCase() + { + $this->mock->addHeaders(array( + 'test' => '123', + 'Test' => 'abc' + )); + $this->mock->addHeader('test', '456'); + $this->mock->addHeader('test', '789'); + + $header = $this->mock->getHeader('test'); + $this->assertContains('123', $header->toArray()); + $this->assertContains('456', $header->toArray()); + $this->assertContains('789', $header->toArray()); + $this->assertContains('abc', $header->toArray()); + } + + public function testCanStoreEmptyHeaders() + { + $this->mock->setHeader('Content-Length', 0); + $this->assertTrue($this->mock->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $this->mock->getHeader('Content-Length')); + } + + public function testCanSetCustomHeaderFactory() + { + $f = new Header\HeaderFactory(); + $this->mock->setHeaderFactory($f); + $this->assertSame($f, $this->readAttribute($this->mock, 'headerFactory')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php new file mode 100644 index 0000000..191b022 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php @@ -0,0 +1,434 @@ +client = new Client(); + } + + public function tearDown() + { + $this->client = null; + } + + public function testConstructorConfiguresRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array( + 'X-Test' => '123' + )); + $request->setBody('Test'); + $this->assertEquals('123', $request->getHeader('X-Test')); + $this->assertNull($request->getHeader('Expect')); + } + + public function testCanSetBodyWithoutOverridingContentType() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array('Content-Type' => 'foooooo')); + $request->setBody('{"a":"b"}'); + $this->assertEquals('foooooo', $request->getHeader('Content-Type')); + } + + public function testRequestIncludesBodyInMessage() + { + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $this->assertEquals("PUT / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Length: 4\r\n\r\n" + . "data", (string) $request); + } + + public function testRequestIncludesPostBodyInMessageOnlyWhenNoPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => 'bar' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "foo=bar", (string) $request); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => '@' . __FILE__ + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: multipart/form-data\r\n" + . "Expect: 100-Continue\r\n\r\n", (string) $request); + } + + public function testAddsPostFieldsAndSetsContentLength() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'data' => '123' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" + . "data=123", (string) $request); + } + + public function testAddsPostFilesAndSetsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/') + ->addPostFiles(array( + 'file' => __FILE__ + ))->addPostFields(array( + 'a' => 'b' + )); + $message = (string) $request; + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + $this->assertEquals('100-Continue', $request->getHeader('Expect')); + } + + public function testRequestBodyContainsPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/'); + $request->addPostFields(array( + 'test' => '123' + )); + $this->assertContains("\r\n\r\ntest=123", (string) $request); + } + + public function testRequestBodyAddsContentLength() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/'); + $request->setBody(EntityBody::factory('test')); + $this->assertEquals(4, (string) $request->getHeader('Content-Length')); + $this->assertFalse($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestBodyDoesNotUseContentLengthWhenChunked() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'test'); + $this->assertNull($request->getHeader('Content-Length')); + $this->assertTrue($request->hasHeader('Transfer-Encoding')); + } + + public function testRequestHasMutableBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $body = $request->getBody(); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body); + $this->assertSame($body, $request->getBody()); + + $newBody = EntityBody::factory('foobar'); + $request->setBody($newBody); + $this->assertEquals('foobar', (string) $request->getBody()); + $this->assertSame($newBody, $request->getBody()); + } + + public function testSetPostFields() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields()); + + $fields = new QueryString(array( + 'a' => 'b' + )); + $request->addPostFields($fields); + $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll()); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testSetPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()) + ->setClient(new Client()) + ->addPostFiles(array(__FILE__)) + ->addPostFields(array( + 'test' => 'abc' + )); + + $request->getCurlOptions()->set('debug', true); + + $this->assertEquals(array( + 'test' => 'abc' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $post = $files['file'][0]; + $this->assertEquals('file', $post->getFieldName()); + $this->assertContains('text/x-', $post->getContentType()); + $this->assertEquals(__FILE__, $post->getFilename()); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + $this->assertNotNull($request->getHeader('Content-Length')); + $this->assertContains('multipart/form-data; boundary=', (string) $request->getHeader('Content-Type'), '-> cURL must add the boundary'); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array( + 'file' => 'filenotfound.ini' + )); + } + + /** + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenNonStringsAreAddedToPost() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', new \stdClass()); + } + + public function testAllowsContentTypeInPostUploads() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__, 'text/plain'); + + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/plain') + ), $request->getPostFile('foo')); + } + + public function testGuessesContentTypeOfPostUpload() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__); + $file = $request->getPostFile('foo'); + $this->assertContains('text/x-', $file[0]->getContentType()); + } + + public function testAllowsContentDispositionFieldsInPostUploadsWhenSettingInBulk() + { + $postFile = new PostFile('foo', __FILE__, 'text/x-php'); + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array('foo' => $postFile)); + + $this->assertEquals(array($postFile), $request->getPostFile('foo')); + } + + public function testPostRequestsUseApplicationXwwwForUrlEncodedForArrays() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertContains("\r\n\r\na=b", (string) $request); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testProcessMethodAddsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); + } + + public function testPostRequestsUseMultipartFormDataWithFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->addPostFiles(array('file' => __FILE__)); + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + } + + public function testCanSendMultipleRequestsUsingASingleRequestObject() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n", + )); + + // Send the first request + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()) + ->setBody('test') + ->setClient(new Client()); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + + // Send the second request + $request->setBody('abcdefg', 'application/json', false); + $request->send(); + $this->assertEquals(201, $request->getResponse()->getStatusCode()); + + // Ensure that the same request was sent twice with different bodies + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($requests)); + $this->assertEquals(4, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals(7, (string) $requests[1]->getHeader('Content-Length')); + } + + public function testRemovingPostFieldRebuildsPostFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com'); + $request->setPostField('test', 'value'); + $request->removePostField('test'); + $this->assertNull($request->getPostField('test')); + } + + public function testUsesChunkedTransferWhenBodyLengthCannotBeDetermined() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenContentLengthCannotBeDeterminedAndUsingHttp1() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->setProtocolVersion('1.0'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + } + + public function testAllowsNestedPostData() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => array('b', 'c') + )); + $this->assertEquals(array( + 'a' => array('b', 'c') + ), $request->getPostFields()->getAll()); + } + + public function testAllowsEmptyFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '' + )); + $this->assertEquals(array( + 'a' => '' + ), $request->getPostFields()->getAll()); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + */ + public function testFailsOnInvalidFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'a' => new \stdClass() + )); + } + + public function testHandlesEmptyStrings() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + )); + $this->assertEquals(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + ), $request->getPostFields()->getAll()); + } + + public function testHoldsPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFile('foo', __FILE__); + $request->addPostFile(new PostFile('foo', __FILE__)); + + $this->assertArrayHasKey('foo', $request->getPostFiles()); + $foo = $request->getPostFile('foo'); + $this->assertEquals(2, count($foo)); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + $this->assertEquals(__FILE__, $foo[1]->getFilename()); + + $request->removePostFile('foo'); + $this->assertEquals(array(), $request->getPostFiles()); + } + + public function testAllowsAtPrefixWhenAddingPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'foo' => '@' . __FILE__ + )); + $foo = $request->getPostFile('foo'); + $this->assertEquals(__FILE__, $foo[0]->getFilename()); + } + + public function testSetStateToTransferWithEmptyBodySetsContentLengthToZero() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->setState($request::STATE_TRANSFER); + $this->assertEquals('0', (string) $request->getHeader('Content-Length')); + } + + public function testSettingExpectHeaderCutoffChangesRequest() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(false); + $this->assertNull($request->getHeader('Expect')); + // There is not body, so remove the expect header + $request->setHeader('Expect', '100-Continue'); + $request->setExpectHeaderCutoff(10); + $this->assertNull($request->getHeader('Expect')); + // The size is less than the cutoff + $request->setBody('foo'); + $this->assertNull($request->getHeader('Expect')); + // The size is greater than the cutoff + $request->setBody('foobazbarbamboo'); + $this->assertNotNull($request->getHeader('Expect')); + } + + public function testStrictRedirectsCanBeSpecifiedOnEntityEnclosingRequests() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(true); + $this->assertTrue($request->getParams()->get(RedirectPlugin::STRICT_REDIRECTS)); + } + + public function testCanDisableRedirects() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->configureRedirects(false, false); + $this->assertTrue($request->getParams()->get(RedirectPlugin::DISABLE)); + } + + public function testSetsContentTypeWhenSettingBodyByGuessingFromEntityBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody(EntityBody::factory(fopen(__FILE__, 'r'))); + $this->assertEquals('text/x-php', (string) $request->getHeader('Content-Type')); + } + + public function testDoesNotCloneBody() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); + $request->setBody('test'); + $newRequest = clone $request; + $newRequest->setBody('foo'); + $this->assertInternalType('string', (string) $request->getBody()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php new file mode 100644 index 0000000..62ca555 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php @@ -0,0 +1,29 @@ +createHeader('Foo', 'Bar'); + $this->assertInstanceOf('Guzzle\Http\Message\Header', $h); + $this->assertEquals('Foo', $h->getName()); + $this->assertEquals('Bar', (string) $h); + } + + public function testCreatesSpecificHeaders() + { + $f = new HeaderFactory(); + $h = $f->createHeader('Link', '; rel="test"'); + $this->assertInstanceOf('Guzzle\Http\Message\Header\Link', $h); + $this->assertEquals('Link', $h->getName()); + $this->assertEquals('; rel="test"', (string) $h); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php new file mode 100644 index 0000000..c834d10 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php @@ -0,0 +1,63 @@ +; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg", ; rel=side; type="image/jpeg"'); + $links = $link->getLinks(); + $this->assertEquals(array( + array( + 'rel' => 'front', + 'type' => 'image/jpeg', + 'url' => 'http:/.../front.jpeg', + ), + array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), + array( + 'rel' => 'side', + 'type' => 'image/jpeg', + 'url' => 'http://.../side.jpeg?test=1' + ) + ), $links); + + $this->assertEquals(array( + 'rel' => 'back', + 'type' => 'image/jpeg', + 'url' => 'http://.../back.jpeg', + ), $link->getLink('back')); + + $this->assertTrue($link->hasLink('front')); + $this->assertFalse($link->hasLink('foo')); + } + + public function testCanAddLink() + { + $link = new Link('Link', '; rel=a; type="image/jpeg"'); + $link->addLink('http://test.com', 'test', array('foo' => 'bar')); + $this->assertEquals( + '; rel=a; type="image/jpeg", ; rel="test"; foo="bar"', + (string) $link + ); + } + + public function testCanParseLinksWithCommas() + { + $link = new Link('Link', '; rel="previous"; title="start, index"'); + $this->assertEquals(array( + array( + 'rel' => 'previous', + 'title' => 'start, index', + 'url' => 'http://example.com/TheBook/chapter1', + ) + ), $link->getLinks()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php new file mode 100644 index 0000000..a3f511b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php @@ -0,0 +1,135 @@ +toArray(); + } + + foreach ($filteredHeaders as $k => $v) { + if ($k[0] == '_') { + // This header should be ignored + $ignore[] = str_replace('_', '', $k); + } elseif ($k[0] == '!') { + // This header must not be present + $absent[] = str_replace('!', '', $k); + } else { + $expected[$k] = $v; + } + } + + return $this->compareArray($expected, $actualHeaders, $ignore, $absent); + } + + /** + * Check if an array of HTTP headers matches another array of HTTP headers while taking * into account as a wildcard + * + * @param array $expected Expected HTTP headers (allows wildcard values) + * @param array|Collection $actual Actual HTTP header array + * @param array $ignore Headers to ignore from the comparison + * @param array $absent Array of headers that must not be present + * + * @return array|bool Returns an array of the differences or FALSE if none + */ + public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array()) + { + $differences = array(); + + // Add information about headers that were present but weren't supposed to be + foreach ($absent as $header) { + if ($this->hasKey($header, $actual)) { + $differences["++ {$header}"] = $actual[$header]; + unset($actual[$header]); + } + } + + // Check if expected headers are missing + foreach ($expected as $header => $value) { + if (!$this->hasKey($header, $actual)) { + $differences["- {$header}"] = $value; + } + } + + // Flip the ignore array so it works with the case insensitive helper + $ignore = array_flip($ignore); + // Allow case-insensitive comparisons in wildcards + $expected = array_change_key_case($expected); + + // Compare the expected and actual HTTP headers in no particular order + foreach ($actual as $key => $value) { + + // If this is to be ignored, the skip it + if ($this->hasKey($key, $ignore)) { + continue; + } + + // If the header was not expected + if (!$this->hasKey($key, $expected)) { + $differences["+ {$key}"] = $value; + continue; + } + + // Check values and take wildcards into account + $lkey = strtolower($key); + $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false; + + foreach ((array) $actual[$key] as $v) { + if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) { + $differences[$key] = "{$value} != {$expected[$lkey]}"; + } + } + } + + return empty($differences) ? false : $differences; + } + + /** + * Case insensitive check if an array have a key + * + * @param string $key Key to check + * @param array $array Array to check + * + * @return bool + */ + protected function hasKey($key, $array) + { + if ($array instanceof Collection) { + $keys = $array->getKeys(); + } else { + $keys = array_keys($array); + } + + foreach ($keys as $k) { + if (!strcasecmp($k, $key)) { + return true; + } + } + + return false; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php new file mode 100644 index 0000000..86c4fe8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php @@ -0,0 +1,115 @@ + 'Foo' + ), array( + 'Content-Length' => 'Foo' + ), false), + + // Missing header + array(array( + 'X-Foo' => 'Bar' + ), array(), array( + '- X-Foo' => 'Bar' + )), + + // Extra headers is present + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Bar', + 'X-Baz' => 'Jar' + ), array( + '+ X-Baz' => 'Jar' + )), + + // Header is present but must be absent + array(array( + '!X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), array( + '++ X-Foo' => 'Bar' + )), + + // Different values + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Baz' + ), array( + 'X-Foo' => 'Baz != Bar' + )), + + // Wildcard search passes + array(array( + 'X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), false), + + // Wildcard search fails + array(array( + 'X-Foo' => '*' + ), array(), array( + '- X-Foo' => '*' + )), + + // Ignore extra header if present + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz', + 'X-Bar' => 'Jar' + ), false), + + // Ignore extra header if present and is not + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz' + ), false), + + // Case insensitive + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + ), false), + + // Case insensitive with collection + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), new Collection(array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + )), false), + ); + } + + /** + * @dataProvider filterProvider + */ + public function testComparesHeaders($filters, $headers, $result) + { + $compare = new HeaderComparison(); + $this->assertEquals($result, $compare->compare($filters, $headers)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php new file mode 100644 index 0000000..c750234 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php @@ -0,0 +1,162 @@ + array('foo', 'Foo'), + 'Zoo' => 'bar', + ); + + public function testStoresHeaderName() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('Zoo', $i->getName()); + } + + public function testConvertsToString() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('foo, Foo, bar', (string) $i); + $i->setGlue(';'); + $this->assertEquals('foo; Foo; bar', (string) $i); + } + + public function testNormalizesGluedHeaders() + { + $h = new Header('Zoo', array('foo, Faz', 'bar')); + $result = $h->normalize(true)->toArray(); + natsort($result); + $this->assertEquals(array('bar', 'foo', 'Faz'), $result); + } + + public function testCanSearchForValues() + { + $h = new Header('Zoo', $this->test); + $this->assertTrue($h->hasValue('foo')); + $this->assertTrue($h->hasValue('Foo')); + $this->assertTrue($h->hasValue('bar')); + $this->assertFalse($h->hasValue('moo')); + $this->assertFalse($h->hasValue('FoO')); + } + + public function testIsCountable() + { + $h = new Header('Zoo', $this->test); + $this->assertEquals(3, count($h)); + } + + public function testCanBeIterated() + { + $h = new Header('Zoo', $this->test); + $results = array(); + foreach ($h as $key => $value) { + $results[$key] = $value; + } + $this->assertEquals(array( + 'foo', 'Foo', 'bar' + ), $results); + } + + public function testAllowsFalseyValues() + { + // Allows 0 + $h = new Header('Foo', 0, ';'); + $this->assertEquals('0', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(';', $h->getGlue()); + + // Does not add a null header by default + $h = new Header('Foo'); + $this->assertEquals('', (string) $h); + $this->assertEquals(0, count($h)); + + // Allows null array for a single null header + $h = new Header('Foo', array(null)); + $this->assertEquals('', (string) $h); + + // Allows empty string + $h = new Header('Foo', ''); + $this->assertEquals('', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(1, count($h->normalize()->toArray())); + } + + public function testCanRemoveValues() + { + $h = new Header('Foo', array('Foo', 'baz', 'bar')); + $h->removeValue('bar'); + $this->assertTrue($h->hasValue('Foo')); + $this->assertFalse($h->hasValue('bar')); + $this->assertTrue($h->hasValue('baz')); + } + + public function testAllowsArrayInConstructor() + { + $h = new Header('Foo', array('Testing', '123', 'Foo=baz')); + $this->assertEquals(array('Testing', '123', 'Foo=baz'), $h->toArray()); + } + + public function parseParamsProvider() + { + $res1 = array( + array( + '' => '', + 'rel' => 'front', + 'type' => 'image/jpeg', + ), + array( + '' => '', + 'rel' => 'back', + 'type' => 'image/jpeg', + ), + ); + + return array( + array( + '; rel="front"; type="image/jpeg", ; rel=back; type="image/jpeg"', + $res1 + ), + array( + '; rel="front"; type="image/jpeg",; rel=back; type="image/jpeg"', + $res1 + ), + array( + 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', + array( + array('foo' => 'baz', 'bar' => '123'), + array('boo' => ''), + array('test' => '123'), + array('foobar' => 'foo;bar') + ) + ), + array( + '; rel="side"; type="image/jpeg",; rel=side; type="image/jpeg"', + array( + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg'), + array('' => '', 'rel' => 'side', 'type' => 'image/jpeg') + ) + ), + array( + '', + array() + ) + ); + } + + /** + * @dataProvider parseParamsProvider + */ + public function testParseParams($header, $result) + { + $response = new Response(200, array('Link' => $header)); + $this->assertEquals($result, $response->getHeader('Link')->parseParams()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php new file mode 100644 index 0000000..be048cb --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php @@ -0,0 +1,88 @@ +assertEquals('foo', $file->getFieldName()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertEquals('boo', $file->getPostName()); + $this->assertEquals('x-foo', $file->getContentType()); + } + + public function testRemovesLeadingAtSymbolFromPath() + { + $file = new PostFile('foo', '@' . __FILE__); + $this->assertEquals(__FILE__, $file->getFilename()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresFileIsReadable() + { + $file = new PostFile('foo', '/foo/baz/bar'); + } + + public function testCanChangeContentType() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setContentType('Boo'); + $this->assertEquals('Boo', $file->getContentType()); + } + + public function testCanChangeFieldName() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setFieldName('Boo'); + $this->assertEquals('Boo', $file->getFieldName()); + } + + public function testReturnsCurlValueString() + { + $file = new PostFile('foo', __FILE__); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=PostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('PostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testReturnsCurlValueStringAndPostname() + { + $file = new PostFile('foo', __FILE__, null, 'NewPostFileTest.php'); + if (version_compare(phpversion(), '5.5.0', '<')) { + $this->assertContains('@' . __FILE__ . ';filename=NewPostFileTest.php;type=text/x-', $file->getCurlValue()); + } else { + $c = $file->getCurlValue(); + $this->assertEquals(__FILE__, $c->getFilename()); + $this->assertEquals('NewPostFileTest.php', $c->getPostFilename()); + $this->assertContains('text/x-', $c->getMimeType()); + } + } + + public function testContentDispositionFilePathIsStripped() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $client->post()->addPostFile('file', __FILE__); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(false); + $this->assertContains('POST / HTTP/1.1', $requests[0]); + $this->assertContains('Content-Disposition: form-data; name="file"; filename="PostFileTest.php"', $requests[0]); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php new file mode 100644 index 0000000..80b8d54 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php @@ -0,0 +1,616 @@ +assertSame($factory, RequestFactory::getInstance()); + } + + public function testCreatesNewGetRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://www.google.com/'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/', $request->getPath()); + $this->assertEquals('/', $request->getResource()); + + // Create a GET request with a custom receiving body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $b = EntityBody::factory(); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl(), null, $b); + $request->setClient(new Client()); + $response = $request->send(); + $this->assertSame($b, $response->getBody()); + } + + public function testCreatesPutRequests() + { + // Test using a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + unset($request); + + // Test using an EntityBody + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using a resource + $resource = fopen('php://temp', 'w+'); + fwrite($resource, 'Data'); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, $resource); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using an object that can be cast as a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('http://www.example.com/', (string) $request->getBody()); + } + + public function testCreatesHeadAndDeleteRequests() + { + $request = RequestFactory::getInstance()->create('DELETE', 'http://www.test.com/'); + $this->assertEquals('DELETE', $request->getMethod()); + $request = RequestFactory::getInstance()->create('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + } + + public function testCreatesOptionsRequests() + { + $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/'); + $this->assertEquals('OPTIONS', $request->getMethod()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + } + + public function testCreatesNewPutRequestWithBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertEquals('Data', (string) $request->getBody()); + } + + public function testCreatesNewPostRequestWithFields() + { + // Use an array + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, array( + 'a' => 'b' + )); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + unset($request); + + // Use a collection + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + // Use a QueryString + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/', null, array( + 'a' => 'b', + 'file' => '@' . __FILE__ + )); + + $this->assertEquals(array( + 'a' => 'b' + ), $request->getPostFields()->getAll()); + + $files = $request->getPostFiles(); + $this->assertInstanceOf('Guzzle\Http\Message\PostFile', $files['file'][0]); + } + + public function testCreatesFromParts() + { + $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2'); + + $request = RequestFactory::getInstance()->fromParts('PUT', $parts, null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals('8080', $request->getPort()); + $this->assertEquals(array( + 'scheme' => 'http', + 'host' => 'www.google.com', + 'port' => 8080, + 'path' => '/path', + 'query' => 'q=1&v=2', + ), parse_url($request->getUrl())); + } + + public function testCreatesFromMessage() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals('8080', $request->getPort()); + + // Test passing a blank message returns false + $this->assertFalse($request = RequestFactory::getInstance()->fromMessage('')); + + // Test passing a url with no port + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals("Basic {$auth}", (string) $request->getHeader('Authorization')); + $this->assertEquals(80, $request->getPort()); + } + + public function testCreatesNewTraceRequest() + { + $request = RequestFactory::getInstance()->create('TRACE', 'http://www.google.com/'); + $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest); + $this->assertEquals('TRACE', $request->getMethod()); + } + + public function testCreatesProperTransferEncodingRequests() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'hello'); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + public function testProperlyDealsWithDuplicateHeaders() + { + $parser = new MessageParser(); + + $message = "POST / http/1.1\r\n" + . "DATE:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "host:host.foo.com\r\n" + . "ZOO:abc\r\n" + . "ZOO:123\r\n" + . "ZOO:HI\r\n" + . "zoo:456\r\n\r\n"; + + $parts = $parser->parseRequest($message); + $this->assertEquals(array ( + 'DATE' => 'Mon, 09 Sep 2011 23:36:00 GMT', + 'host' => 'host.foo.com', + 'ZOO' => array('abc', '123', 'HI'), + 'zoo' => '456', + ), $parts['headers']); + + $request = RequestFactory::getInstance()->fromMessage($message); + + $this->assertEquals(array( + 'abc', '123', 'HI', '456' + ), $request->getHeader('zoo')->toArray()); + } + + public function testCreatesHttpMessagesWithBodiesAndNormalizesLineEndings() + { + $message = "POST / http/1.1\r\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\r\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "Host:host.foo.com\r\n\r\n" + . "foo=bar"; + + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf8', (string) $request->getHeader('Content-Type')); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "POST / http/1.1\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\n" + . "Host:host.foo.com\n\n" + . "foo=bar"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertTrue($request->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $request->getHeader('Content-Length')); + } + + public function testBugPathIncorrectlyHandled() + { + $message = "POST /foo\r\n\r\nBODY"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertSame('POST', $request->getMethod()); + $this->assertSame('/foo', $request->getPath()); + $this->assertSame('BODY', (string) $request->getBody()); + } + + public function testHandlesChunkedTransferEncoding() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.foo.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'Test'); + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.foo.com/', array( + 'transfer-encoding' => 'chunked' + ), array( + 'foo' => 'bar' + )); + + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + } + + public function testClonesRequestsWithMethodWithoutClient() + { + $f = RequestFactory::getInstance(); + $request = $f->create('GET', 'http://www.test.com', array('X-Foo' => 'Bar')); + $request->getParams()->replace(array('test' => '123')); + $request->getCurlOptions()->set('foo', 'bar'); + $cloned = $f->cloneRequestWithMethod($request, 'PUT'); + $this->assertEquals('PUT', $cloned->getMethod()); + $this->assertEquals('Bar', (string) $cloned->getHeader('X-Foo')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + // Ensure params are cloned and cleaned up + $this->assertEquals(1, count($cloned->getParams()->getAll())); + $this->assertEquals('123', $cloned->getParams()->get('test')); + // Ensure curl options are cloned + $this->assertEquals('bar', $cloned->getCurlOptions()->get('foo')); + // Ensure event dispatcher is cloned + $this->assertNotSame($request->getEventDispatcher(), $cloned->getEventDispatcher()); + } + + public function testClonesRequestsWithMethodWithClient() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'GET'); + $this->assertEquals('GET', $cloned->getMethod()); + $this->assertNull($cloned->getHeader('Content-Length')); + $this->assertEquals('http://www.test.com', $cloned->getUrl()); + $this->assertSame($request->getClient(), $cloned->getClient()); + } + + public function testClonesRequestsWithMethodWithClientWithEntityEnclosingChange() + { + $f = RequestFactory::getInstance(); + $client = new Client(); + $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); + $cloned = $f->cloneRequestWithMethod($request, 'POST'); + $this->assertEquals('POST', $cloned->getMethod()); + $this->assertEquals('test', (string) $cloned->getBody()); + } + + public function testCanDisableRedirects() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 307\r\nLocation: " . $this->getServer()->getUrl() . "\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $response = $client->get('/', array(), array('allow_redirects' => false))->send(); + $this->assertEquals(307, $response->getStatusCode()); + } + + public function testCanAddCookies() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/', array(), array('cookies' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', $request->getCookie('Foo')); + } + + public function testCanAddQueryString() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'query' => array('Foo' => 'Bar') + )); + $this->assertEquals('Bar', $request->getQuery()->get('Foo')); + } + + public function testCanSetDefaultQueryString() + { + $request = new Request('GET', 'http://www.foo.com?test=abc'); + RequestFactory::getInstance()->applyOptions($request, array( + 'query' => array('test' => '123', 'other' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('abc', $request->getQuery()->get('test')); + $this->assertEquals('t123', $request->getQuery()->get('other')); + } + + public function testCanAddBasicAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test') + )); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddDigestAuth() + { + $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( + 'auth' => array('michael', 'test', 'digest') + )); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testCanAddEvents() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => function () use (&$foo) { $foo = true; } + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddEventsWithPriority() + { + $foo = null; + $client = new Client(); + $client->addSubscriber(new MockPlugin(array(new Response(200)))); + $request = $client->get($this->getServer()->getUrl(), array(), array( + 'events' => array( + 'request.before_send' => array(function () use (&$foo) { $foo = true; }, 100) + ) + )); + $request->send(); + $this->assertTrue($foo); + } + + public function testCanAddPlugins() + { + $mock = new MockPlugin(array( + new Response(200), + new Response(200) + )); + $client = new Client(); + $client->addSubscriber($mock); + $request = $client->get('/', array(), array( + 'plugins' => array($mock) + )); + $request->send(); + } + + public function testCanDisableExceptions() + { + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanDisableExceptionsWithErrorListener() + { + $client = new Client(); + $client->getEventDispatcher()->addListener('request.error', function () {}); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(500)))), + 'exceptions' => false + )); + $this->assertEquals(500, $request->send()->getStatusCode()); + } + + public function testCanChangeSaveToLocation() + { + $r = EntityBody::factory(); + $client = new Client(); + $request = $client->get('/', array(), array( + 'plugins' => array(new MockPlugin(array(new Response(200, array(), 'testing')))), + 'save_to' => $r + )); + $request->send(); + $this->assertEquals('testing', (string) $r); + } + + public function testCanSetProxy() + { + $client = new Client(); + $request = $client->get('/', array(), array('proxy' => '192.168.16.121')); + $this->assertEquals('192.168.16.121', $request->getCurlOptions()->get(CURLOPT_PROXY)); + } + + public function testCanSetHeadersOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('headers' => array('Foo' => 'Bar'))); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + } + + public function testCanSetDefaultHeadersOptions() + { + $request = new Request('GET', 'http://www.foo.com', array('Foo' => 'Bar')); + RequestFactory::getInstance()->applyOptions($request, array( + 'headers' => array('Foo' => 'Baz', 'Bam' => 't123') + ), RequestFactory::OPTIONS_AS_DEFAULTS); + $this->assertEquals('Bar', (string) $request->getHeader('Foo')); + $this->assertEquals('t123', (string) $request->getHeader('Bam')); + } + + public function testCanSetBodyOption() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('body' => 'test')); + $this->assertEquals('test', (string) $request->getBody()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesBodyOption() + { + $client = new Client(); + $client->get('/', array(), array('body' => 'test')); + } + + public function testCanSetTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_TIMEOUT_MS)); + } + + public function testCanSetConnectTimeoutOption() + { + $client = new Client(); + $request = $client->get('/', array(), array('connect_timeout' => 1.5)); + $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_CONNECTTIMEOUT_MS)); + } + + public function testCanSetDebug() + { + $client = new Client(); + $request = $client->get('/', array(), array('debug' => true)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_VERBOSE)); + } + + public function testCanSetVerifyToOff() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => false)); + $this->assertNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(0, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertFalse($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToOn() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => true)); + $this->assertNotNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function testCanSetVerifyToPath() + { + $client = new Client(); + $request = $client->get('/', array(), array('verify' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_CAINFO)); + $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); + $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); + } + + public function inputValidation() + { + return array_map(function ($option) { return array($option); }, array( + 'headers', 'query', 'cookies', 'auth', 'events', 'plugins', 'params' + )); + } + + /** + * @dataProvider inputValidation + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesInput($option) + { + $client = new Client(); + $client->get('/', array(), array($option => 'foo')); + } + + public function testCanAddRequestParams() + { + $client = new Client(); + $request = $client->put('/', array(), null, array('params' => array('foo' => 'test'))); + $this->assertEquals('test', $request->getParams()->get('foo')); + } + + public function testCanAddSslKey() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + } + + public function testCanAddSslKeyPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('ssl_key' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLKEYPASSWD)); + } + + public function testCanAddSslCert() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => '/foo.pem')); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + } + + public function testCanAddSslCertPassword() + { + $client = new Client(); + $request = $client->get('/', array(), array('cert' => array('/foo.pem', 'bar'))); + $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); + $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLCERTPASSWD)); + } + + public function testCreatesBodyWithoutZeroString() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://test.com', array(), '0'); + $this->assertSame('0', (string) $request->getBody()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php new file mode 100644 index 0000000..5bf6248 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php @@ -0,0 +1,639 @@ +client = new Client($this->getServer()->getUrl()); + $this->request = $this->client->get(); + } + + public function tearDown() + { + unset($this->request); + unset($this->client); + } + + public function testConstructorBuildsRequestWithArrayHeaders() + { + // Test passing an array of headers + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'foo' => 'bar' + )); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Request::getAllEvents()); + } + + public function testConstructorBuildsRequestWithCollectionHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( + 'foo' => 'bar' + ))); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + public function testConstructorBuildsRequestWithNoHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', null); + $this->assertFalse($request->hasHeader('foo')); + } + + public function testConstructorHandlesNonBasicAuth() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'Authorization' => 'Foo bar' + )); + $this->assertNull($request->getUserName()); + $this->assertNull($request->getPassword()); + $this->assertEquals('Foo bar', (string) $request->getHeader('Authorization')); + } + + public function testRequestsCanBeConvertedToRawMessageStrings() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\n" + . "Host: www.google.com\r\n" + . "Authorization: Basic {$auth}\r\n" + . "Content-Length: 4\r\n\r\nData"; + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', array( + 'Authorization' => 'Basic ' . $auth + ), 'Data'); + + $this->assertEquals($message, $request->__toString()); + } + + /** + * Add authorization after the fact and see that it was put in the message + */ + public function testRequestStringsIncludeAuth() + { + $auth = base64_encode('michael:123'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl(), null, 'Data') + ->setClient($this->client) + ->setAuth('michael', '123', CURLAUTH_BASIC); + $request->send(); + + $this->assertContains('Authorization: Basic ' . $auth, (string) $request); + } + + public function testGetEventDispatcher() + { + $d = $this->request->getEventDispatcher(); + $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d); + $this->assertEquals($d, $this->request->getEventDispatcher()); + } + + public function testRequestsManageClients() + { + $request = new Request('GET', 'http://test.com'); + $this->assertNull($request->getClient()); + $request->setClient($this->client); + $this->assertSame($this->client, $request->getClient()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage A client must be set on the request + */ + public function testRequestsRequireClients() + { + $request = new Request('GET', 'http://test.com'); + $request->send(); + } + + public function testSend() + { + $response = new Response(200, array( + 'Content-Length' => 3 + ), 'abc'); + $this->request->setResponse($response, true); + $r = $this->request->send(); + + $this->assertSame($response, $r); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse()); + $this->assertSame($r, $this->request->getResponse()); + $this->assertEquals('complete', $this->request->getState()); + } + + public function testGetResponse() + { + $this->assertNull($this->request->getResponse()); + $response = new Response(200, array('Content-Length' => 3), 'abc'); + + $this->request->setResponse($response); + $this->assertEquals($response, $this->request->getResponse()); + + $client = new Client('http://www.google.com'); + $request = $client->get('http://www.google.com/'); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + + // Try again, making sure it's still the same response + $this->assertSame($requestResponse, $request->getResponse()); + + $response = new Response(204); + $request = $client->get(); + $request->setResponse($response, true); + $request->send(); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + } + + public function testRequestThrowsExceptionOnBadResponse() + { + try { + $this->request->setResponse(new Response(404, array('Content-Length' => 3), 'abc'), true); + $this->request->send(); + $this->fail('Expected exception not thrown'); + } catch (BadResponseException $e) { + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse()); + $this->assertContains('Client error response', $e->getMessage()); + } + } + + public function testManagesQuery() + { + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery()); + $this->request->getQuery()->set('test', '123'); + $this->assertEquals('test=123', $this->request->getQuery(true)); + } + + public function testRequestHasMethod() + { + $this->assertEquals('GET', $this->request->getMethod()); + } + + public function testRequestHasScheme() + { + $this->assertEquals('http', $this->request->getScheme()); + $this->assertEquals($this->request, $this->request->setScheme('https')); + $this->assertEquals('https', $this->request->getScheme()); + } + + public function testRequestHasHost() + { + $this->assertEquals('127.0.0.1', $this->request->getHost()); + $this->assertEquals('127.0.0.1:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www2.google.com')); + $this->assertEquals('www2.google.com', $this->request->getHost()); + $this->assertEquals('www2.google.com:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www.test.com:8081')); + $this->assertEquals('www.test.com', $this->request->getHost()); + $this->assertEquals(8081, $this->request->getPort()); + } + + public function testRequestHasProtocol() + { + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1')); + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0')); + $this->assertEquals('1.0', $this->request->getProtocolVersion()); + } + + public function testRequestHasPath() + { + $this->assertEquals('/', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('/index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + } + + public function testPermitsFalsyComponents() + { + $request = new Request('GET', 'http://0/0?0'); + $this->assertSame('0', $request->getHost()); + $this->assertSame('/0', $request->getPath()); + $this->assertSame('0', $request->getQuery(true)); + + $request = new Request('GET', '0'); + $this->assertEquals('/0', $request->getPath()); + } + + public function testRequestHasPort() + { + $this->assertEquals(8124, $this->request->getPort()); + $this->assertEquals('127.0.0.1:8124', $this->request->getHeader('Host')); + + $this->assertEquals($this->request, $this->request->setPort('8080')); + $this->assertEquals('8080', $this->request->getPort()); + $this->assertEquals('127.0.0.1:8080', $this->request->getHeader('Host')); + + $this->request->setPort(80); + $this->assertEquals('127.0.0.1', $this->request->getHeader('Host')); + } + + public function testRequestHandlesAuthorization() + { + // Uninitialized auth + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Set an auth + $this->assertSame($this->request, $this->request->setAuth('michael', '123')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('123', $this->request->getPassword()); + + // Set an auth with blank password + $this->assertSame($this->request, $this->request->setAuth('michael', '')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('', $this->request->getPassword()); + + // Remove the auth + $this->request->setAuth(false); + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Make sure that the cURL based auth works too + $request = new Request('GET', $this->getServer()->getUrl()); + $request->setAuth('michael', 'password', CURLAUTH_DIGEST); + $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesAuth() + { + $this->request->setAuth('foo', 'bar', 'bam'); + } + + public function testGetResourceUri() + { + $this->assertEquals('/', $this->request->getResource()); + $this->request->setPath('/index.html'); + $this->assertEquals('/index.html', $this->request->getResource()); + $this->request->getQuery()->add('v', '1'); + $this->assertEquals('/index.html?v=1', $this->request->getResource()); + } + + public function testRequestHasMutableUrl() + { + $url = 'http://www.test.com:8081/path?q=123#fragment'; + $u = Url::factory($url); + $this->assertSame($this->request, $this->request->setUrl($url)); + $this->assertEquals($url, $this->request->getUrl()); + + $this->assertSame($this->request, $this->request->setUrl($u)); + $this->assertEquals($url, $this->request->getUrl()); + } + + public function testRequestHasState() + { + $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState()); + $this->request->setState(RequestInterface::STATE_TRANSFER); + $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState()); + } + + public function testSetManualResponse() + { + $response = new Response(200, array( + 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT', + 'Expires' => '-1', + 'Cache-Control' => 'private, max-age=0', + 'Content-Type' => 'text/html; charset=ISO-8859-1', + ), 'response body'); + + $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface'); + $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete'); + $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it'); + } + + public function testRequestCanHaveManuallySetResponseBody() + { + $file = __DIR__ . '/../../TestData/temp.out'; + if (file_exists($file)) { + unlink($file); + } + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $entityBody = EntityBody::factory(fopen($file, 'w+')); + $request->setResponseBody($entityBody); + $response = $request->send(); + $this->assertSame($entityBody, $response->getBody()); + + $this->assertTrue(file_exists($file)); + $this->assertEquals('data', file_get_contents($file)); + unlink($file); + + $this->assertEquals('data', $response->getBody(true)); + } + + public function testHoldsCookies() + { + $this->assertNull($this->request->getCookie('test')); + + // Set a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + + // Multiple cookies by setting the Cookie header + $this->request->setHeader('Cookie', '__utma=1.638370270.1344367610.1374365610.1944450276.2; __utmz=1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hl=de; PHPSESSID=ak93pqashi5uubuoq8fjv60897'); + $this->assertEquals('1.638370270.1344367610.1374365610.1944450276.2', $this->request->getCookie('__utma')); + $this->assertEquals('1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', $this->request->getCookie('__utmz')); + $this->assertEquals('de', $this->request->getCookie('hl')); + $this->assertEquals('ak93pqashi5uubuoq8fjv60897', $this->request->getCookie('PHPSESSID')); + + // Unset the cookies by setting the Cookie header to null + $this->request->setHeader('Cookie', null); + $this->assertNull($this->request->getCookie('test')); + $this->request->removeHeader('Cookie'); + + // Set and remove a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + $this->assertSame($this->request, $this->request->removeCookie('test')); + $this->assertNull($this->request->getCookie('test')); + + // Remove the cookie header + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->request->removeHeader('Cookie'); + $this->assertEquals('', (string) $this->request->getHeader('Cookie')); + + // Remove a cookie value + $this->request->addCookie('foo', 'bar')->addCookie('baz', 'boo'); + $this->request->removeCookie('foo'); + $this->assertEquals(array( + 'baz' => 'boo' + ), $this->request->getCookies()); + + $this->request->addCookie('foo', 'bar'); + $this->assertEquals('baz=boo; foo=bar', (string) $this->request->getHeader('Cookie')); + } + + /** + * @expectedException \Guzzle\Http\Exception\RequestException + * @expectedExceptionMessage Error completing request + */ + public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() + { + $this->request->setState(RequestInterface::STATE_COMPLETE); + } + + public function testClonedRequestsUseNewInternalState() + { + $p = new AsyncPlugin(); + $this->request->getEventDispatcher()->addSubscriber($p); + $h = $this->request->getHeader('Host'); + + $r = clone $this->request; + $this->assertEquals(RequestInterface::STATE_NEW, $r->getState()); + $this->assertNotSame($r->getQuery(), $this->request->getQuery()); + $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions()); + $this->assertNotSame($r->getEventDispatcher(), $this->request->getEventDispatcher()); + $this->assertEquals($r->getHeaders(), $this->request->getHeaders()); + $this->assertNotSame($h, $r->getHeader('Host')); + $this->assertNotSame($r->getParams(), $this->request->getParams()); + $this->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testRecognizesBasicAuthCredentialsInUrls() + { + $this->request->setUrl('http://michael:test@test.com/'); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('test', $this->request->getPassword()); + } + + public function testRequestCanBeSentUsingCurl() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\nFile not found" + )); + + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $response = $request->send(); + + $this->assertEquals('data', $response->getBody(true)); + $this->assertEquals(200, (int) $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires()); + + // Test that the same handle can be sent twice without setting state to new + $response2 = $request->send(); + $this->assertNotSame($response, $response2); + + try { + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl() . 'index.html'); + $request->setClient($this->client); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + } + + $requests = $this->getServer()->getReceivedRequests(true); + $messages = $this->getServer()->getReceivedRequests(false); + $port = $this->getServer()->getPort(); + + $userAgent = $this->client->getDefaultUserAgent(); + + $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host')); + + $this->assertEquals('/', $requests[0]->getPath()); + $this->assertEquals('/', $requests[1]->getPath()); + $this->assertEquals('/index.html', $requests[2]->getPath()); + + $parts = explode("\r\n", $messages[0]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[1]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[2]); + $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]); + } + + public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"); + + try { + $request = $this->client->get('/index.html'); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + $this->assertContains('Client error response', $e->getMessage()); + $this->assertContains('[status code] 404', $e->getMessage()); + $this->assertContains('[reason phrase] Not found', $e->getMessage()); + } + } + + public function testCanShortCircuitErrorHandling() + { + $request = $this->request; + $response = new Response(404); + $request->setResponse($response, true); + $out = ''; + $that = $this; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$out, $that) { + $out .= $event['request'] . "\n" . $event['response'] . "\n"; + $event->stopPropagation(); + }); + $request->send(); + $this->assertContains((string) $request, $out); + $this->assertContains((string) $request->getResponse(), $out); + $this->assertSame($response, $request->getResponse()); + } + + public function testCanOverrideUnsuccessfulResponses() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 404 NOT FOUND\r\n" . + "Content-Length: 0\r\n" . + "\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $newResponse = null; + + $request = $this->request; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$newResponse) { + if ($event['response']->getStatusCode() == 404) { + $newRequest = clone $event['request']; + $newResponse = $newRequest->send(); + // Override the original response and bypass additional response processing + $event['response'] = $newResponse; + // Call $event['request']->setResponse($newResponse); to re-apply events + $event->stopPropagation(); + } + }); + + $request->send(); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertSame($newResponse, $request->getResponse()); + $this->assertEquals(2, count($this->getServer()->getReceivedRequests())); + } + + public function testCanRetrieveUrlObject() + { + $request = new Request('GET', 'http://www.example.com/foo?abc=d'); + $this->assertInstanceOf('Guzzle\Http\Url', $request->getUrl(true)); + $this->assertEquals('http://www.example.com/foo?abc=d', $request->getUrl()); + $this->assertEquals('http://www.example.com/foo?abc=d', (string) $request->getUrl(true)); + } + + public function testUnresolvedRedirectsReturnResponse() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 SEE OTHER\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n" + )); + $request = $this->request; + $this->assertEquals(303, $request->send()->getStatusCode()); + $request->getParams()->set(RedirectPlugin::DISABLE, true); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanSendCustomRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = $this->client->createRequest('PROPFIND', $this->getServer()->getUrl(), array( + 'Content-Type' => 'text/plain' + ), 'foo'); + $response = $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('PROPFIND', $requests[0]->getMethod()); + $this->assertEquals(3, (string) $requests[0]->getHeader('Content-Length')); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + */ + public function testEnsuresFileCanBeCreated() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->client->get('/')->setResponseBody('/wefwefefefefwewefwe/wefwefwefefwe/wefwefewfw.txt')->send(); + } + + public function testAllowsFilenameForDownloadingContent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $name = sys_get_temp_dir() . '/foo.txt'; + $this->client->get('/')->setResponseBody($name)->send(); + $this->assertEquals('test', file_get_contents($name)); + unlink($name); + } + + public function testUsesCustomResponseBodyWhenItIsCustom() + { + $en = EntityBody::factory(); + $request = $this->client->get(); + $request->setResponseBody($en); + $request->setResponse(new Response(200, array(), 'foo')); + $this->assertEquals('foo', (string) $en); + } + + public function testCanChangePortThroughScheme() + { + $request = new Request('GET', 'http://foo.com'); + $request->setScheme('https'); + $this->assertEquals('https://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setScheme('http'); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + $request->setPort(null); + $this->assertEquals('http://foo.com', (string) $request->getUrl()); + $this->assertEquals('foo.com', $request->getHost()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php new file mode 100644 index 0000000..08b4df8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php @@ -0,0 +1,677 @@ +response = new Response(200, new Collection(array( + 'Accept-Ranges' => 'bytes', + 'Age' => '12', + 'Allow' => 'GET, HEAD', + 'Cache-Control' => 'no-cache', + 'Content-Encoding' => 'gzip', + 'Content-Language' => 'da', + 'Content-Length' => '348', + 'Content-Location' => '/index.htm', + 'Content-Disposition' => 'attachment; filename=fname.ext', + 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', + 'Content-Range' => 'bytes 21010-47021/47022', + 'Content-Type' => 'text/html; charset=utf-8', + 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT', + 'ETag' => '737060cd8c284d8af7ad3082f209582d', + 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT', + 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Location' => 'http://www.w3.org/pub/WWW/People.html', + 'Pragma' => 'no-cache', + 'Proxy-Authenticate' => 'Basic', + 'Retry-After' => '120', + 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)', + 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'Trailer' => 'Max-Forwards', + 'Transfer-Encoding' => 'chunked', + 'Vary' => '*', + 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)', + 'Warning' => '199 Miscellaneous warning', + 'WWW-Authenticate' => 'Basic' + )), 'body'); + } + + public function tearDown() + { + unset($this->response); + } + + public function testConstructor() + { + $params = new Collection(); + $body = EntityBody::factory(''); + $response = new Response(200, $params, $body); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals($body, $response->getBody()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Make sure Content-Length is set automatically + $response = new Response(200, $params); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Pass bodies to the response + $response = new Response(200, null, 'data'); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $response = new Response(200, null, EntityBody::factory('data')); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $this->assertEquals('data', $response->getBody(true)); + $response = new Response(200, null, '0'); + $this->assertSame('0', $response->getBody(true), 'getBody(true) should return "0" if response body is "0".'); + + // Make sure the proper exception is thrown + try { + //$response = new Response(200, null, array('foo' => 'bar')); + //$this->fail('Response did not throw exception when passing invalid body'); + } catch (HttpException $e) { + } + + // Ensure custom codes can be set + $response = new Response(2); + $this->assertEquals(2, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + // Make sure the proper exception is thrown when sending invalid headers + try { + $response = new Response(200, 'adidas'); + $this->fail('Response did not throw exception when passing invalid $headers'); + } catch (BadResponseException $e) { + } + } + + public function test__toString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + + // Add another header + $response = new Response(200, array( + 'X-Test' => 'Guzzle' + )); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + + $response = new Response(200, array( + 'Content-Length' => 4 + ), 'test'); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + public function testFactory() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + + // Make sure that automatic Content-Length works + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + } + + public function testFactoryCanCreateHeadResponses() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, (string) $response->getContentLength()); + $this->assertEquals('', $response->getBody(true)); + } + + public function testFactoryRequiresMessage() + { + $this->assertFalse(Response::fromMessage('')); + } + + public function testGetBody() + { + $body = EntityBody::factory(''); + $response = new Response(403, new Collection(), $body); + $this->assertEquals($body, $response->getBody()); + $response->setBody('foo'); + $this->assertEquals('foo', $response->getBody(true)); + } + + public function testManagesStatusCode() + { + $response = new Response(403); + $this->assertEquals(403, $response->getStatusCode()); + } + + public function testGetMessage() + { + $response = new Response(200, new Collection(array( + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage()); + } + + public function testGetRawHeaders() + { + $response = new Response(200, new Collection(array( + 'Keep-Alive' => 155, + 'User-Agent' => 'Guzzle', + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders()); + } + + public function testHandlesStatusAndStatusCodes() + { + $response = new Response(200, new Collection(), 'body'); + $this->assertEquals('OK', $response->getReasonPhrase()); + + $this->assertSame($response, $response->setStatus(204)); + $this->assertEquals('No Content', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $this->assertSame($response, $response->setStatus(204, 'Testing!')); + $this->assertEquals('Testing!', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $response->setStatus(2000); + $this->assertEquals(2000, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + $response->setStatus(200, 'Foo'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } + + public function testIsClientError() + { + $response = new Response(403); + $this->assertTrue($response->isClientError()); + $response = new Response(200); + $this->assertFalse($response->isClientError()); + } + + public function testIsError() + { + $response = new Response(403); + $this->assertTrue($response->isError()); + $response = new Response(200); + $this->assertFalse($response->isError()); + $response = new Response(500); + $this->assertTrue($response->isError()); + } + + public function testIsInformational() + { + $response = new Response(100); + $this->assertTrue($response->isInformational()); + $response = new Response(200); + $this->assertFalse($response->isInformational()); + } + + public function testIsRedirect() + { + $response = new Response(301); + $this->assertTrue($response->isRedirect()); + $response = new Response(200); + $this->assertFalse($response->isRedirect()); + } + + public function testIsServerError() + { + $response = new Response(500); + $this->assertTrue($response->isServerError()); + $response = new Response(400); + $this->assertFalse($response->isServerError()); + } + + public function testIsSuccessful() + { + $response = new Response(200); + $this->assertTrue($response->isSuccessful()); + $response = new Response(403); + $this->assertFalse($response->isSuccessful()); + } + + public function testGetAcceptRanges() + { + $this->assertEquals('bytes', $this->response->getAcceptRanges()); + } + + public function testCalculatesAge() + { + $this->assertEquals(12, $this->response->calculateAge()); + + $this->response->removeHeader('Age'); + $this->response->removeHeader('Date'); + $this->assertNull($this->response->calculateAge()); + + $this->response->setHeader('Date', gmdate(ClientInterface::HTTP_DATE, strtotime('-1 minute'))); + // If the test runs slowly, still pass with a +5 second allowance + $this->assertTrue($this->response->getAge() - 60 <= 5); + } + + public function testGetAllow() + { + $this->assertEquals('GET, HEAD', $this->response->getAllow()); + } + + public function testGetCacheControl() + { + $this->assertEquals('no-cache', $this->response->getCacheControl()); + } + + public function testGetContentEncoding() + { + $this->assertEquals('gzip', $this->response->getContentEncoding()); + } + + public function testGetContentLanguage() + { + $this->assertEquals('da', $this->response->getContentLanguage()); + } + + public function testGetContentLength() + { + $this->assertEquals('348', $this->response->getContentLength()); + } + + public function testGetContentLocation() + { + $this->assertEquals('/index.htm', $this->response->getContentLocation()); + } + + public function testGetContentDisposition() + { + $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); + } + + public function testGetContentMd5() + { + $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); + } + + public function testGetContentRange() + { + $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); + } + + public function testGetContentType() + { + $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); + } + + public function testGetDate() + { + $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); + } + + public function testGetEtag() + { + $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); + } + + public function testGetExpires() + { + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); + } + + public function testGetLastModified() + { + $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); + } + + public function testGetLocation() + { + $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); + } + + public function testGetPragma() + { + $this->assertEquals('no-cache', $this->response->getPragma()); + } + + public function testGetProxyAuthenticate() + { + $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); + } + + public function testGetServer() + { + $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); + } + + public function testGetSetCookie() + { + $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); + } + + public function testGetMultipleSetCookie() + { + $this->response->addHeader('Set-Cookie', 'UserID=Mike; Max-Age=200'); + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'UserID=Mike; Max-Age=200', + ), $this->response->getHeader('Set-Cookie')->toArray()); + } + + public function testGetSetCookieNormalizesHeaders() + { + $this->response->addHeaders(array( + 'Set-Cooke' => 'boo', + 'set-cookie' => 'foo' + )); + + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'foo' + ), $this->response->getHeader('Set-Cookie')->toArray()); + + $this->response->addHeaders(array( + 'set-cookie' => 'fubu' + )); + $this->assertEquals( + array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'), + $this->response->getHeader('Set-Cookie')->toArray() + ); + } + + public function testGetTrailer() + { + $this->assertEquals('Max-Forwards', $this->response->getTrailer()); + } + + public function testGetTransferEncoding() + { + $this->assertEquals('chunked', $this->response->getTransferEncoding()); + } + + public function testGetVary() + { + $this->assertEquals('*', $this->response->getVary()); + } + + public function testReturnsViaHeader() + { + $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); + } + public function testGetWarning() + { + $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); + } + + public function testReturnsWwwAuthenticateHeader() + { + $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); + } + + public function testReturnsConnectionHeader() + { + $this->assertEquals(null, $this->response->getConnection()); + $this->response->setHeader('Connection', 'close'); + $this->assertEquals('close', $this->response->getConnection()); + } + + public function testReturnsHeaders() + { + $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); + $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); + } + + public function testHasTransferInfo() + { + $stats = array ( + 'url' => 'http://www.google.com/', + 'content_type' => 'text/html; charset=ISO-8859-1', + 'http_code' => 200, + 'header_size' => 606, + 'request_size' => 53, + 'filetime' => -1, + 'ssl_verify_result' => 0, + 'redirect_count' => 0, + 'total_time' => 0.093284, + 'namelookup_time' => 0.001349, + 'connect_time' => 0.01635, + 'pretransfer_time' => 0.016358, + 'size_upload' => 0, + 'size_download' => 10330, + 'speed_download' => 110737, + 'speed_upload' => 0, + 'download_content_length' => -1, + 'upload_content_length' => 0, + 'starttransfer_time' => 0.07066, + 'redirect_time' => 0, + ); + + // Uninitialized state + $this->assertNull($this->response->getInfo('url')); + $this->assertEquals(array(), $this->response->getInfo()); + + // Set the stats + $this->response->setInfo($stats); + $this->assertEquals($stats, $this->response->getInfo()); + $this->assertEquals(606, $this->response->getInfo('header_size')); + $this->assertNull($this->response->getInfo('does_not_exist')); + } + + /** + * @return Response + */ + private function getResponse($code, array $headers = null, EntityBody $body = null) + { + return new Response($code, $headers, $body); + } + + public function testDeterminesIfItCanBeCached() + { + $this->assertTrue($this->getResponse(200)->canCache()); + $this->assertTrue($this->getResponse(410)->canCache()); + $this->assertFalse($this->getResponse(404)->canCache()); + $this->assertTrue($this->getResponse(200, array( + 'Cache-Control' => 'public' + ))->canCache()); + + // This has the no-store directive + $this->assertFalse($this->getResponse(200, array( + 'Cache-Control' => 'private, no-store' + ))->canCache()); + + // The body cannot be read, so it cannot be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertFalse($this->getResponse(200, array( + 'Transfer-Encoding' => 'chunked' + ), EntityBody::factory($resource, 10))->canCache()); + unlink($tmp); + + // The body is 0 length, cannot be read, so it can be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertTrue($this->getResponse(200, array(array( + 'Content-Length' => 0 + )), EntityBody::factory($resource, 0))->canCache()); + unlink($tmp); + } + + public function testDeterminesResponseMaxAge() + { + $this->assertEquals(null, $this->getResponse(200)->getMaxAge()); + + // Uses the response's s-maxage + $this->assertEquals(140, $this->getResponse(200, array( + 'Cache-Control' => 's-maxage=140' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120', + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) + ))->getMaxAge()); + } + + public function testDeterminesIfItCanValidate() + { + $response = new Response(200); + $this->assertFalse($response->canValidate()); + $response->setHeader('ETag', '123'); + $this->assertTrue($response->canValidate()); + $response->removeHeader('ETag'); + $this->assertFalse($response->canValidate()); + $response->setHeader('Last-Modified', '123'); + $this->assertTrue($response->canValidate()); + } + + public function testCalculatesFreshness() + { + $response = new Response(200); + $this->assertNull($response->isFresh()); + $this->assertNull($response->getFreshness()); + + $response->setHeader('Cache-Control', 'max-age=120'); + $response->setHeader('Age', 100); + $this->assertEquals(20, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 120); + $this->assertEquals(0, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 150); + $this->assertEquals(-30, $response->getFreshness()); + $this->assertFalse($response->isFresh()); + } + + public function testHandlesProtocols() + { + $this->assertSame($this->response, $this->response->setProtocol('HTTP', '1.0')); + $this->assertEquals('HTTP', $this->response->getProtocol()); + $this->assertEquals('1.0', $this->response->getProtocolVersion()); + } + + public function testComparesContentType() + { + $response = new Response(200, array( + 'Content-Type' => 'text/html; charset=ISO-8859-4' + )); + + $this->assertTrue($response->isContentType('text/html')); + $this->assertTrue($response->isContentType('TExT/html')); + $this->assertTrue($response->isContentType('charset=ISO-8859-4')); + $this->assertFalse($response->isContentType('application/xml')); + } + + public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader() + { + $response = new Response(200, array( + 'Allow' => 'OPTIONS, POST, deletE,GET' + )); + + $this->assertTrue($response->isMethodAllowed('get')); + $this->assertTrue($response->isMethodAllowed('GET')); + $this->assertTrue($response->isMethodAllowed('options')); + $this->assertTrue($response->isMethodAllowed('post')); + $this->assertTrue($response->isMethodAllowed('Delete')); + $this->assertFalse($response->isMethodAllowed('put')); + $this->assertFalse($response->isMethodAllowed('PUT')); + + $response = new Response(200); + $this->assertFalse($response->isMethodAllowed('get')); + } + + public function testParsesJsonResponses() + { + $response = new Response(200, array(), '{"foo": "bar"}'); + $this->assertEquals(array('foo' => 'bar'), $response->json()); + // Return array when null is a service response + $response = new Response(200); + $this->assertEquals(array(), $response->json()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into JSON: 4 + */ + public function testThrowsExceptionWhenFailsToParseJsonResponse() + { + $response = new Response(200, array(), '{"foo": "'); + $response->json(); + } + + public function testParsesXmlResponses() + { + $response = new Response(200, array(), 'bar'); + $this->assertEquals('bar', (string) $response->xml()->foo); + // Always return a SimpleXMLElement from the xml method + $response = new Response(200); + $this->assertEmpty((string) $response->xml()->foo); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML + */ + public function testThrowsExceptionWhenFailsToParseXmlResponse() + { + $response = new Response(200, array(), 'xml(); + } + + public function testResponseIsSerializable() + { + $response = new Response(200, array('Foo' => 'bar'), 'test'); + $r = unserialize(serialize($response)); + $this->assertEquals(200, $r->getStatusCode()); + $this->assertEquals('bar', (string) $r->getHeader('Foo')); + $this->assertEquals('test', (string) $r->getBody()); + } + + public function testPreventsComplexExternalEntities() + { + $xml = ']>&test;'; + $response = new Response(200, array(), $xml); + + $oldCwd = getcwd(); + chdir(__DIR__); + try { + $xml = $response->xml(); + chdir($oldCwd); + $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); + } catch (\Exception $e) { + chdir($oldCwd); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php new file mode 100644 index 0000000..7228453 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php @@ -0,0 +1,31 @@ +assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); + } + + public function testGetsFromFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); + } + + public function testGetsFromCaseInsensitiveFilename() + { + $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); + } + + public function testReturnsNullWhenNoMatchFound() + { + $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php new file mode 100644 index 0000000..549d3ed --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('test%20123' => 'foo%20123,baz,bar'), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'test 123'; + $value = array('foo 123', 'baz', 'bar'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('test 123' => 'foo 123,baz,bar'), $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php new file mode 100644 index 0000000..6a4d9d9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php @@ -0,0 +1,30 @@ +aggregate($key, $value, $query); + $this->assertEquals(array('facet%201' => array('size%20a', 'width%20b')), $result); + } + + public function testEncodes() + { + $query = new QueryString(); + $query->useUrlEncoding(false); + $a = new Ag(); + $key = 'facet 1'; + $value = array('size a', 'width b'); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array('facet 1' => array('size a', 'width b')), $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php new file mode 100644 index 0000000..1e7f0c2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php @@ -0,0 +1,32 @@ +useUrlEncoding(false); + $a = new Ag(); + $key = 't'; + $value = array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ); + $result = $a->aggregate($key, $value, $query); + $this->assertEquals(array( + 't[v1]' => 'a', + 't[v2]' => 'b', + 't[v3][v4]' => 'c', + 't[v3][v5]' => 'd', + ), $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php new file mode 100644 index 0000000..948db44 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php @@ -0,0 +1,233 @@ +q = new QueryString(); + } + + public function testGetFieldSeparator() + { + $this->assertEquals('&', $this->q->getFieldSeparator()); + } + + public function testGetValueSeparator() + { + $this->assertEquals('=', $this->q->getValueSeparator()); + } + + public function testIsUrlEncoding() + { + $this->assertEquals('RFC 3986', $this->q->getUrlEncoding()); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals('foo%20bar', $this->q->encodeValue('foo bar')); + + $this->q->useUrlEncoding(QueryString::FORM_URLENCODED); + $this->assertTrue($this->q->isUrlEncoding()); + $this->assertEquals(QueryString::FORM_URLENCODED, $this->q->getUrlEncoding()); + $this->assertEquals('foo+bar', $this->q->encodeValue('foo bar')); + + $this->assertSame($this->q, $this->q->useUrlEncoding(false)); + $this->assertFalse($this->q->isUrlEncoding()); + $this->assertFalse($this->q->isUrlEncoding()); + } + + public function testSetFieldSeparator() + { + $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); + $this->assertEquals('/', $this->q->getFieldSeparator()); + } + + public function testSetValueSeparator() + { + $this->assertEquals($this->q, $this->q->setValueSeparator('/')); + $this->assertEquals('/', $this->q->getValueSeparator()); + } + + public function testUrlEncode() + { + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'ሴ' => 'bar' + ); + $encoded = array( + 'test' => 'value', + 'test%202' => rawurlencode('this is a test?'), + 'test3%5B0%5D' => 'v1', + 'test3%5B1%5D' => 'v2', + 'test3%5B2%5D' => 'v3', + '%E1%88%B4' => 'bar' + ); + $this->q->replace($params); + $this->assertEquals($encoded, $this->q->urlEncode()); + + // Disable encoding + $testData = array('test 2' => 'this is a test'); + $this->q->replace($testData); + $this->q->useUrlEncoding(false); + $this->assertEquals($testData, $this->q->urlEncode()); + } + + public function testToString() + { + // Check with no parameters + $this->assertEquals('', $this->q->__toString()); + + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'test4' => null, + ); + $this->q->replace($params); + $this->assertEquals('test=value&test%202=this%20is%20a%20test%3F&test3%5B0%5D=v1&test3%5B1%5D=v2&test3%5B2%5D=v3&test4', $this->q->__toString()); + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3&test4', $this->q->__toString()); + + // Use an alternative aggregator + $this->q->setAggregator(new CommaAggregator()); + $this->assertEquals('test=value&test 2=this is a test?&test3=v1,v2,v3&test4', $this->q->__toString()); + } + + public function testAllowsMultipleValuesPerKey() + { + $q = new QueryString(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregator(new DuplicateAggregator()); + $this->assertEquals('facet=size&facet=width&facet.field=foo', $q->__toString()); + } + + public function testAllowsNestedQueryData() + { + $this->q->replace(array( + 'test' => 'value', + 't' => array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ) + )); + + $this->q->useUrlEncoding(false); + $this->assertEquals('test=value&t[v1]=a&t[v2]=b&t[v3][v4]=c&t[v3][v5]=d', $this->q->__toString()); + } + + public function parseQueryProvider() + { + return array( + // Ensure that multiple query string values are allowed per value + array('q=a&q=b', array('q' => array('a', 'b'))), + // Ensure that PHP array style query string values are parsed + array('q[]=a&q[]=b', array('q' => array('a', 'b'))), + // Ensure that a single PHP array style query string value is parsed into an array + array('q[]=a', array('q' => array('a'))), + // Ensure that decimals are allowed in query strings + array('q.a=a&q.b=b', array( + 'q.a' => 'a', + 'q.b' => 'b' + )), + // Ensure that query string values are percent decoded + array('q%20a=a%20b', array('q a' => 'a b')), + // Ensure null values can be added + array('q&a', array('q' => false, 'a' => false)), + ); + } + + /** + * @dataProvider parseQueryProvider + */ + public function testParsesQueryStrings($query, $data) + { + $query = QueryString::fromString($query); + $this->assertEquals($data, $query->getAll()); + } + + public function testProperlyDealsWithDuplicateQueryStringValues() + { + $query = QueryString::fromString('foo=a&foo=b&?µ=c'); + $this->assertEquals(array('a', 'b'), $query->get('foo')); + $this->assertEquals('c', $query->get('?µ')); + } + + public function testAllowsBlankQueryStringValues() + { + $query = QueryString::fromString('foo'); + $this->assertEquals('foo', (string) $query); + $query->set('foo', QueryString::BLANK); + $this->assertEquals('foo', (string) $query); + } + + public function testAllowsFalsyQueryStringValues() + { + $query = QueryString::fromString('0'); + $this->assertEquals('0', (string) $query); + $query->set('0', QueryString::BLANK); + $this->assertSame('0', (string) $query); + } + + public function testFromStringIgnoresQuestionMark() + { + $query = QueryString::fromString('foo=baz&bar=boo'); + $this->assertEquals('foo=baz&bar=boo', (string) $query); + } + + public function testConvertsPlusSymbolsToSpaces() + { + $query = QueryString::fromString('var=foo+bar'); + $this->assertEquals('foo bar', $query->get('var')); + } + + public function testFromStringDoesntMangleZeroes() + { + $query = QueryString::fromString('var=0'); + $this->assertSame('0', $query->get('var')); + } + + public function testAllowsZeroValues() + { + $query = new QueryString(array( + 'foo' => 0, + 'baz' => '0', + 'bar' => null, + 'boo' => false, + 'bam' => '' + )); + $this->assertEquals('foo=0&baz=0&bar&boo&bam=', (string) $query); + } + + public function testFromStringDoesntStripTrailingEquals() + { + $query = QueryString::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0='); + $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data')); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsed() + { + $query = QueryString::fromString('test=a&test=b'); + $this->assertEquals('test=a&test=b', (string) $query); + } + + public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle() + { + $query = QueryString::fromString('test[]=a&test[]=b'); + $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php new file mode 100644 index 0000000..6bb3fed --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php @@ -0,0 +1,81 @@ +decorated = EntityBody::factory(fopen(__FILE__, 'r')); + $this->body = new ReadLimitEntityBody($this->decorated, 10, 3); + } + + public function testReturnsSubsetWhenCastToString() + { + $body = EntityBody::factory('foo_baz_bar'); + $limited = new ReadLimitEntityBody($body, 3, 4); + $this->assertEquals('baz', (string) $limited); + } + + public function testReturnsSubsetOfEmptyBodyWhenCastToString() + { + $body = EntityBody::factory(''); + $limited = new ReadLimitEntityBody($body, 0, 10); + $this->assertEquals('', (string) $limited); + } + + public function testSeeksWhenConstructed() + { + $this->assertEquals(3, $this->body->ftell()); + } + + public function testAllowsBoundedSeek() + { + $this->body->seek(100); + $this->assertEquals(13, $this->body->ftell()); + $this->body->seek(0); + $this->assertEquals(3, $this->body->ftell()); + $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); + } + + public function testReadsOnlySubsetOfData() + { + $data = $this->body->read(100); + $this->assertEquals(10, strlen($data)); + $this->assertFalse($this->body->read(1000)); + + $this->body->setOffset(10); + $newData = $this->body->read(100); + $this->assertEquals(10, strlen($newData)); + $this->assertNotSame($data, $newData); + } + + public function testClaimsConsumedWhenReadLimitIsReached() + { + $this->assertFalse($this->body->isConsumed()); + $this->body->read(1000); + $this->assertTrue($this->body->isConsumed()); + } + + public function testContentLengthIsBounded() + { + $this->assertEquals(10, $this->body->getContentLength()); + } + + public function testContentMd5IsBasedOnSubsection() + { + $this->assertNotSame($this->body->getContentMd5(), $this->decorated->getContentMd5()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php new file mode 100644 index 0000000..886236d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php @@ -0,0 +1,277 @@ +getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + // Ensure that two requests were sent + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/foo', $requests[0]->getResource()); + $this->assertEquals('GET', $requests[0]->getMethod()); + $this->assertEquals('/redirect1', $requests[1]->getResource()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('/redirect2', $requests[2]->getResource()); + $this->assertEquals('GET', $requests[2]->getMethod()); + + // Ensure that the redirect count was incremented + $this->assertEquals(2, $request->getParams()->get(RedirectPlugin::REDIRECT_COUNT)); + $this->assertCount(3, $history); + $requestHistory = $history->getAll(); + + $this->assertEquals(301, $requestHistory[0]['response']->getStatusCode()); + $this->assertEquals('/redirect1', (string) $requestHistory[0]['response']->getHeader('Location')); + $this->assertEquals(301, $requestHistory[1]['response']->getStatusCode()); + $this->assertEquals('/redirect2', (string) $requestHistory[1]['response']->getHeader('Location')); + $this->assertEquals(200, $requestHistory[2]['response']->getStatusCode()); + } + + public function testCanLimitNumberOfRedirects() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" + )); + + try { + $client = new Client($this->getServer()->getUrl()); + $client->get('/foo')->send(); + $this->fail('Did not throw expected exception'); + } catch (TooManyRedirectsException $e) { + $this->assertContains( + "5 redirects were issued for this request:\nGET /foo HTTP/1.1\r\n", + $e->getMessage() + ); + } + } + + public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $client->post('/foo', array('X-Baz' => 'bar'), 'testing')->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('GET', $requests[2]->getMethod()); + } + + public function testCanRedirectWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo', array('X-Baz' => 'bar'), 'testing'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('POST', $requests[1]->getMethod()); + $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); + $this->assertEquals('POST', $requests[2]->getMethod()); + } + + public function testRedirect303WithGet() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRedirect303WithGetWithStrictRfcCompliance() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/foo'); + $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); + $request->send(); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertEquals('GET', $requests[1]->getMethod()); + } + + public function testRewindsStreamWhenRedirectingIfNeeded() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory('foo'); + $body->read(1); + $request->setBody($body); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('foo', (string) $requests[0]->getBody()); + } + + /** + * @expectedException \Guzzle\Http\Exception\CouldNotRewindStreamException + */ + public function testThrowsExceptionWhenStreamCannotBeRewound() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(true); + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $body->read(1); + $request->setBody($body)->send(); + } + + public function testRedirectsCanBeDisabledPerRequest() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put(); + $request->configureRedirects(false, 0); + $this->assertEquals(301, $request->send()->getStatusCode()); + } + + public function testCanRedirectWithNoLeadingSlashAndQuery() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect?foo=bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('?foo=bar'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $requests[0]->getUrl()); + $this->assertEquals($this->getServer()->getUrl() . 'redirect?foo=bar', $requests[1]->getUrl()); + // Ensure that the history on the actual request is correct + $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $request->getUrl()); + } + + public function testRedirectWithStrictRfc386Compliance() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect', $requests[1]->getResource()); + } + + public function testResetsHistoryEachSend() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + // Create a client that uses the default redirect behavior + $client = new Client($this->getServer()->getUrl()); + $history = new HistoryPlugin(); + $client->addSubscriber($history); + + $request = $client->get('/foo'); + $response = $request->send(); + $this->assertEquals(3, count($history)); + $this->assertTrue($request->getParams()->hasKey('redirect.count')); + $this->assertContains('/redirect2', $response->getEffectiveUrl()); + + $request->send(); + $this->assertFalse($request->getParams()->hasKey('redirect.count')); + } + + public function testHandlesRedirectsWithSpacesProperly() + { + // Flush the server and queue up a redirect followed by a successful response + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get('/foo'); + $request->send(); + $reqs = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('/redirect%201', $reqs[1]->getResource()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php new file mode 100644 index 0000000..94eb59a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php @@ -0,0 +1,191 @@ +port = $port ?: self::DEFAULT_PORT; + $this->client = new Client($this->getUrl()); + register_shutdown_function(array($this, 'stop')); + } + + /** + * Flush the received requests from the server + * @throws RuntimeException + */ + public function flush() + { + $this->client->delete('guzzle-server/requests')->send(); + } + + /** + * Queue an array of responses or a single response on the server. + * + * Any currently queued responses will be overwritten. Subsequent requests + * on the server will return queued responses in FIFO order. + * + * @param array|Response $responses A single or array of Responses to queue + * @throws BadResponseException + */ + public function enqueue($responses) + { + $data = array(); + foreach ((array) $responses as $response) { + + // Create the response object from a string + if (is_string($response)) { + $response = Response::fromMessage($response); + } elseif (!($response instanceof Response)) { + throw new BadResponseException('Responses must be strings or implement Response'); + } + + $data[] = array( + 'statusCode' => $response->getStatusCode(), + 'reasonPhrase' => $response->getReasonPhrase(), + 'headers' => $response->getHeaders()->toArray(), + 'body' => $response->getBody(true) + ); + } + + $request = $this->client->put('guzzle-server/responses', null, json_encode($data)); + $request->send(); + } + + /** + * Check if the server is running + * + * @return bool + */ + public function isRunning() + { + if ($this->running) { + return true; + } + + try { + $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send(); + $this->running = true; + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * Get the URL to the server + * + * @return string + */ + public function getUrl() + { + return 'http://127.0.0.1:' . $this->getPort() . '/'; + } + + /** + * Get the port that the server is listening on + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws RuntimeException + */ + public function getReceivedRequests($hydrate = false) + { + $response = $this->client->get('guzzle-server/requests')->send(); + $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true))); + if ($hydrate) { + $data = array_map(function($message) { + return RequestFactory::getInstance()->fromMessage($message); + }, $data); + } + + return $data; + } + + /** + * Start running the node.js server in the background + */ + public function start() + { + if (!$this->isRunning()) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR + . 'server.js ' . $this->port + . ' >> /tmp/server.log 2>&1 &'); + // Wait at most 5 seconds for the server the setup before + // proceeding. + $start = time(); + while (!$this->isRunning() && time() - $start < 5); + if (!$this->running) { + throw new RuntimeException( + 'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.' + ); + } + } + } + + /** + * Stop running the node.js server + */ + public function stop() + { + if (!$this->isRunning()) { + return false; + } + + $this->running = false; + $this->client->delete('guzzle-server')->send(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php new file mode 100644 index 0000000..091314b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php @@ -0,0 +1,67 @@ +assertTrue(class_exists('FooBazBar')); + $this->assertSame($client, $this->readAttribute('Guzzle\Http\StaticClient', 'client')); + } + + public function requestProvider() + { + return array_map( + function ($m) { return array($m); }, + array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS') + ); + } + + /** + * @dataProvider requestProvider + */ + public function testSendsRequests($method) + { + $mock = new MockPlugin(array(new Response(200))); + call_user_func('Guzzle\Http\StaticClient::' . $method, 'http://foo.com', array( + 'plugins' => array($mock) + )); + $requests = $mock->getReceivedRequests(); + $this->assertCount(1, $requests); + $this->assertEquals($method, $requests[0]->getMethod()); + } + + public function testCanCreateStreamsUsingDefaultFactory() + { + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $stream = StaticClient::get($this->getServer()->getUrl(), array('stream' => true)); + $this->assertInstanceOf('Guzzle\Stream\StreamInterface', $stream); + $this->assertEquals('test', (string) $stream); + } + + public function testCanCreateStreamsUsingCustomFactory() + { + $stream = $this->getMockBuilder('Guzzle\Stream\StreamRequestFactoryInterface') + ->setMethods(array('fromRequest')) + ->getMockForAbstractClass(); + $resource = new Stream(fopen('php://temp', 'r+')); + $stream->expects($this->once()) + ->method('fromRequest') + ->will($this->returnValue($resource)); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); + $result = StaticClient::get($this->getServer()->getUrl(), array('stream' => $stream)); + $this->assertSame($resource, $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php new file mode 100644 index 0000000..28f2671 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php @@ -0,0 +1,303 @@ +assertEquals('', (string) $url); + } + + public function testPortIsDeterminedFromScheme() + { + $this->assertEquals(80, Url::factory('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort()); + $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort()); + } + + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::factory('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + public function testValidatesUrlPartsInFactory() + { + $url = Url::factory('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::factory($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + public function testAllowsFalsyUrlParts() + { + $url = Url::factory('http://0:50/0?0#0'); + $this->assertSame('0', $url->getHost()); + $this->assertEquals(50, $url->getPort()); + $this->assertSame('/0', $url->getPath()); + $this->assertEquals('0', (string) $url->getQuery()); + $this->assertSame('0', $url->getFragment()); + $this->assertEquals('http://0:50/0?0#0', (string) $url); + + $url = Url::factory(''); + $this->assertSame('', (string) $url); + + $url = Url::factory('0'); + $this->assertSame('0', (string) $url); + } + + public function testBuildsRelativeUrlsWithFalsyParts() + { + $url = Url::buildUrl(array( + 'host' => '0', + 'path' => '0', + )); + + $this->assertSame('//0/0', $url); + + $url = Url::buildUrl(array( + 'path' => '0', + )); + $this->assertSame('0', $url); + } + + public function testUrlStoresParts() + { + $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => 'a=1&b=2', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + public function testHandlesPathsCorrectly() + { + $url = Url::factory('http://www.test.com'); + $this->assertEquals('', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + public function testAddsQueryStringIfPresent() + { + $this->assertEquals('?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + public function testAddsToPath() + { + // Does nothing here + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(null)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(array())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(new \stdClass())); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('')); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/')); + $this->assertEquals('http://e.com/baz/foo', (string) Url::factory('http://e.com/baz/')->addPath('foo')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative')); + $this->assertEquals('http://e.com/base/0', (string) Url::factory('http://e.com/base')->addPath('0')); + $this->assertEquals('http://e.com/base/0/1', (string) Url::factory('http://e.com/base')->addPath('0')->addPath('1')); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return array( + array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'), + array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'), + array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'), + array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'), + array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'), + array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'), + array('http://www.example.com/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'), + array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'), + array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'), + array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'), + array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), + array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'), + array('http://www.example.com/?foo=bar', 'some/path', 'http://www.example.com/some/path?foo=bar'), + array('http://www.example.com/?foo=bar', 'some/path?boo=moo', 'http://www.example.com/some/path?boo=moo&foo=bar'), + array('http://www.example.com/some/', 'path?foo=bar&foo=baz', 'http://www.example.com/some/path?foo=bar&foo=baz'), + ); + } + + /** + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::factory($a)->combine($b)); + } + + public function testHasGettersAndSetters() + { + $url = Url::factory('http://www.test.com/'); + $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); + $this->assertEquals('8080', $url->setPort(8080)->getPort()); + $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath()); + $this->assertEquals('a', $url->setPassword('a')->getPassword()); + $this->assertEquals('b', $url->setUsername('b')->getUsername()); + $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); + $this->assertEquals('https', $url->setScheme('https')->getScheme()); + $this->assertEquals('a=123', (string) $url->setQuery('a=123')->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string) $url); + $this->assertEquals('b=boo', (string) $url->setQuery(new QueryString(array( + 'b' => 'boo' + )))->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string) $url); + } + + public function testSetQueryAcceptsArray() + { + $url = Url::factory('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com?a=b', (string) $url); + } + + public function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '/'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('/./foo/bar/baz/pho/../..', '/foo/bar'), + array('*', '*'), + array('/foo', '/foo'), + array('/abc/123/../foo/', '/abc/foo/'), + array('/a/b/c/./../../g', '/a/g'), + array('/b/c/./../../g', '/g'), + array('/b/c/./../../g', '/g'), + array('/c/./../../g', '/g'), + array('/./../../g', '/g'), + ); + } + + /** + * @dataProvider urlProvider + */ + public function testNormalizesPaths($path, $result) + { + $url = Url::factory('http://www.example.com/'); + $url->setPath($path)->normalizePath(); + $this->assertEquals($result, $url->getPath()); + } + + public function testSettingHostWithPortModifiesPort() + { + $url = Url::factory('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesUrlCanBeParsed() + { + Url::factory('foo:////'); + } + + public function testConvertsSpecialCharsInPathWhenCastingToString() + { + $url = Url::factory('http://foo.com/baz bar?a=b'); + $url->addPath('?'); + $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); + } + + /** + * @link http://tools.ietf.org/html/rfc3986#section-5.4.1 + */ + public function rfc3986UrlProvider() + { + $result = array( + array('g', 'http://a/b/c/g'), + array('./g', 'http://a/b/c/g'), + array('g/', 'http://a/b/c/g/'), + array('/g', 'http://a/g'), + array('?y', 'http://a/b/c/d;p?y'), + array('g?y', 'http://a/b/c/g?y'), + array('#s', 'http://a/b/c/d;p?q#s'), + array('g#s', 'http://a/b/c/g#s'), + array('g?y#s', 'http://a/b/c/g?y#s'), + array(';x', 'http://a/b/c/;x'), + array('g;x', 'http://a/b/c/g;x'), + array('g;x?y#s', 'http://a/b/c/g;x?y#s'), + array('', 'http://a/b/c/d;p?q'), + array('.', 'http://a/b/c'), + array('./', 'http://a/b/c/'), + array('..', 'http://a/b'), + array('../', 'http://a/b/'), + array('../g', 'http://a/b/g'), + array('../..', 'http://a/'), + array('../../', 'http://a/'), + array('../../g', 'http://a/g') + ); + + // This support was added in PHP 5.4.7: https://bugs.php.net/bug.php?id=62844 + if (version_compare(PHP_VERSION, '5.4.7', '>=')) { + $result[] = array('//g', 'http://g'); + } + + return $result; + } + + /** + * @dataProvider rfc3986UrlProvider + */ + public function testCombinesUrlsUsingRfc3986($relative, $result) + { + $a = Url::factory('http://a/b/c/d;p?q'); + $b = Url::factory($relative); + $this->assertEquals($result, trim((string) $a->combine($b, true), '=')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js new file mode 100644 index 0000000..4156f1a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js @@ -0,0 +1,146 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * DELETE /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Enqueue responses + * PUT /guzzle-server/responses + * Host: 127.0.0.1:8124 + * + * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] + * + * - Get the received requests + * GET /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Shutdown the server + * DELETE /guzzle-server + * Host: 127.0.0.1:8124 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require("http"); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port, log) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + var controlRequest = function(request, req, res) { + if (req.url == '/guzzle-server/perf') { + res.writeHead(200, "OK", {"Content-Length": 16}); + res.end("Body of response"); + } else if (req.method == "DELETE") { + if (req.url == "/guzzle-server/requests") { + // Clear the received requests + that.requests = []; + res.writeHead(200, "OK", { "Content-Length": 0 }); + res.end(); + if (this.log) { + console.log("Flushing requests"); + } + } else if (req.url == "/guzzle-server") { + // Shutdown the server + res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); + res.end(); + if (this.log) { + console.log("Shutting down"); + } + that.server.close(); + } + } else if (req.method == "GET") { + if (req.url === "/guzzle-server/requests") { + // Get received requests + var data = that.requests.join("\n----[request]\n"); + res.writeHead(200, "OK", { "Content-Length": data.length }); + res.end(data); + if (that.log) { + console.log("Sending receiving requests"); + } + } + } else if (req.method == "PUT") { + if (req.url == "/guzzle-server/responses") { + if (that.log) { + console.log("Adding responses..."); + } + // Received response to queue + var data = request.split("\r\n\r\n")[1]; + if (!data) { + if (that.log) { + console.log("No response data was provided"); + } + res.writeHead(400, "NO RESPONSES IN REQUEST", { "Content-Length": 0 }); + } else { + that.responses = eval("(" + data + ")"); + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, "OK", { "Content-Length": 0 }); + } + res.end(); + } + } + }; + + var receivedRequest = function(request, req, res) { + if (req.url.indexOf("/guzzle-server") === 0) { + controlRequest(request, req, res); + } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { + res.writeHead(500); + res.end("No responses in queue"); + } else { + var response = that.responses.shift(); + res.writeHead(response.statusCode, response.reasonPhrase, response.headers); + res.end(response.body); + that.requests.push(request); + } + }; + + this.start = function() { + + that.server = http.createServer(function(req, res) { + + var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; + for (var i in req.headers) { + request += i + ": " + req.headers[i] + "\r\n"; + } + request += "\r\n"; + + // Receive each chunk of the request body + req.addListener("data", function(chunk) { + request += chunk; + }); + + // Called when the request completes + req.addListener("end", function() { + receivedRequest(request, req, res); + }); + }); + that.server.listen(port, "127.0.0.1"); + + if (this.log) { + console.log("Server running at http://127.0.0.1:8124/"); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8124; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php new file mode 100644 index 0000000..990c0af --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php @@ -0,0 +1,37 @@ +assertSame(Inflector::getDefault(), Inflector::getDefault()); + } + + public function testSnake() + { + $this->assertEquals('camel_case', Inflector::getDefault()->snake('camelCase')); + $this->assertEquals('camel_case', Inflector::getDefault()->snake('CamelCase')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCaseWords')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCase_words')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('expect100_continue', Inflector::getDefault()->snake('Expect100Continue')); + } + + public function testCamel() + { + $this->assertEquals('CamelCase', Inflector::getDefault()->camel('camel_case')); + $this->assertEquals('CamelCaseWords', Inflector::getDefault()->camel('camel_case_words')); + $this->assertEquals('Test', Inflector::getDefault()->camel('test')); + $this->assertEquals('Expect100Continue', ucfirst(Inflector::getDefault()->camel('expect100_continue'))); + // Get from cache + $this->assertEquals('Test', Inflector::getDefault()->camel('test', false)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php new file mode 100644 index 0000000..f00b7fa --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php @@ -0,0 +1,46 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->will($this->returnValue('foo_bar')); + $mock->expects($this->once())->method('camel')->will($this->returnValue('FooBar')); + + $inflector = new MemoizingInflector($mock); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + } + + public function testProtectsAgainstCacheOverflow() + { + $inflector = new MemoizingInflector(new Inflector(), 10); + for ($i = 1; $i < 11; $i++) { + $inflector->camel('foo_' . $i); + $inflector->snake('Foo' . $i); + } + + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(10, count($cache['snake'])); + $this->assertEquals(10, count($cache['camel'])); + + $inflector->camel('baz!'); + $inflector->snake('baz!'); + + // Now ensure that 20% of the cache was removed (2), then the item was added + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(9, count($cache['snake'])); + $this->assertEquals(9, count($cache['camel'])); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php new file mode 100644 index 0000000..ff2654c --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->with('Test')->will($this->returnValue('test')); + $mock->expects($this->once())->method('camel')->with('Test')->will($this->returnValue('Test')); + $inflector = new PreComputedInflector($mock, array('FooBar' => 'foo_bar'), array('foo_bar' => 'FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('Test', $inflector->camel('Test')); + $this->assertEquals('test', $inflector->snake('Test')); + } + + public function testMirrorsPrecomputedValues() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array(), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + } + + public function testMirrorsPrecomputedValuesByMerging() + { + $mock = $this->getMock('Guzzle\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array('foo' => 'Foo'), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + $this->assertEquals('Foo', $inflector->camel('foo')); + $this->assertEquals('foo', $inflector->snake('Foo')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php new file mode 100644 index 0000000..8d6ae84 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php @@ -0,0 +1,29 @@ + 1, + 'b' => 2 + )); + $b = new \ArrayIterator(array()); + $c = new \ArrayIterator(array( + 'c' => 3, + 'd' => 4 + )); + $i = new AppendIterator(); + $i->append($a); + $i->append($b); + $i->append($c); + $this->assertEquals(array(1, 2, 3, 4), iterator_to_array($i, false)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php new file mode 100644 index 0000000..ec4c129 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php @@ -0,0 +1,52 @@ +assertEquals(11, count($chunks)); + foreach ($chunks as $j => $chunk) { + $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk); + } + } + + public function testChunksIteratorWithOddValues() + { + $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2); + $chunks = iterator_to_array($chunked, false); + $this->assertEquals(3, count($chunks)); + $this->assertEquals(array(1, 2), $chunks[0]); + $this->assertEquals(array(3, 4), $chunks[1]); + $this->assertEquals(array(5), $chunks[2]); + } + + public function testMustNotTerminateWithTraversable() + { + $traversable = simplexml_load_string('')->foo; + $chunked = new ChunkedIterator($traversable, 2); + $actual = iterator_to_array($chunked, false); + $this->assertCount(2, $actual); + } + + public function testSizeOfZeroMakesIteratorInvalid() { + $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), 0); + $chunked->rewind(); + $this->assertFalse($chunked->valid()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSizeLowerZeroThrowsException() { + new ChunkedIterator(new \ArrayIterator(range(1, 5)), -1); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php new file mode 100644 index 0000000..73b4f69 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(1, 99, 2), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new FilterIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php new file mode 100644 index 0000000..4de4a6b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(0, 1000, 10), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new MapIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php new file mode 100644 index 0000000..5bcf06f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php @@ -0,0 +1,28 @@ +append('a'); + $proxy->append('b'); + $this->assertEquals(array('a', 'b'), $i->getArrayCopy()); + $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy()); + } + + public function testUsesInnerIterator() + { + $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2)); + $this->assertEquals(3, count(iterator_to_array($i, false))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php new file mode 100644 index 0000000..a66882f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php @@ -0,0 +1,23 @@ +log('test', \LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => '127.0.0.1')), $adapter->getLogs()); + } + + public function testClearLog() + { + $adapter = new ArrayLogAdapter(); + $adapter->log('test', \LOG_NOTICE, '127.0.0.1'); + $adapter->clearLogs(); + $this->assertEquals(array(), $adapter->getLogs()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php new file mode 100644 index 0000000..0177dc0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php @@ -0,0 +1,30 @@ +adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) { + $modified = array($message, $priority, $extras); + }); + $this->adapter->log('test', LOG_NOTICE, '127.0.0.1'); + $this->assertEquals(array('test', LOG_NOTICE, '127.0.0.1'), $modified); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenNotCallable() + { + $this->adapter = new ClosureLogAdapter(123); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php new file mode 100644 index 0000000..3ff4b07 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php @@ -0,0 +1,143 @@ +request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array( + 'X-Foo' => 'bar', + 'Authorization' => 'Baz' + )); + $this->request->setBody(EntityBody::factory('Hello')); + + $this->response = new Response(200, array( + 'X-Test' => 'Abc' + ), 'Foo'); + + $this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo')) + ->getMock(); + + $this->handle->expects($this->any()) + ->method('getError') + ->will($this->returnValue('e')); + + $this->handle->expects($this->any()) + ->method('getErrorNo') + ->will($this->returnValue('123')); + + $this->handle->expects($this->any()) + ->method('getStderr') + ->will($this->returnValue('testing')); + + $this->handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValueMap(array( + array(CURLINFO_CONNECT_TIME, '123'), + array(CURLINFO_TOTAL_TIME, '456') + ))); + } + + public function logProvider() + { + return array( + // Uses the cache for the second time + array('{method} - {method}', 'POST - POST'), + array('{url}', 'http://foo.com?q=test'), + array('{port}', '80'), + array('{resource}', '/?q=test'), + array('{host}', 'foo.com'), + array('{hostname}', gethostname()), + array('{protocol}/{version}', 'HTTP/1.1'), + array('{code} {phrase}', '200 OK'), + array('{req_header_Foo}', ''), + array('{req_header_X-Foo}', 'bar'), + array('{req_header_Authorization}', 'Baz'), + array('{res_header_foo}', ''), + array('{res_header_X-Test}', 'Abc'), + array('{req_body}', 'Hello'), + array('{res_body}', 'Foo'), + array('{curl_stderr}', 'testing'), + array('{curl_error}', 'e'), + array('{curl_code}', '123'), + array('{connect_time}', '123'), + array('{total_time}', '456') + ); + } + + /** + * @dataProvider logProvider + */ + public function testFormatsMessages($template, $output) + { + $formatter = new MessageFormatter($template); + $this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle)); + } + + public function testFormatsRequestsAndResponses() + { + $formatter = new MessageFormatter(); + $formatter->setTemplate('{request}{response}'); + $this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response)); + } + + public function testAddsTimestamp() + { + $formatter = new MessageFormatter('{ts}'); + $this->assertNotEmpty($formatter->format($this->request, $this->response)); + } + + public function testUsesResponseWhenNoHandleAndGettingCurlInformation() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(200)) + ->setMethods(array('getInfo')) + ->getMock(); + $response->expects($this->exactly(2)) + ->method('getInfo') + ->will($this->returnValueMap(array( + array('connect_time', '1'), + array('total_time', '2'), + ))); + $this->assertEquals('1/2', $formatter->format($this->request, $response)); + } + + public function testUsesEmptyStringWhenNoHandleAndNoResponse() + { + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $this->assertEquals('/', $formatter->format($this->request)); + } + + public function testInjectsTotalTime() + { + $out = ''; + $formatter = new MessageFormatter('{connect_time}/{total_time}'); + $adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; }); + $log = new LogPlugin($adapter, $formatter); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($log); + $client->get('/')->send(); + $this->assertNotEquals('/', $out); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php new file mode 100644 index 0000000..7b72dd6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php @@ -0,0 +1,25 @@ +pushHandler($handler); + $adapter = new PsrLogAdapter($log); + $adapter->log('test!', LOG_INFO); + $this->assertTrue($handler->hasInfoRecords()); + $this->assertSame($log, $adapter->getLogObject()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php new file mode 100644 index 0000000..1b61283 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php @@ -0,0 +1,51 @@ +stream = fopen('php://temp', 'r+'); + $this->log = new Logger(); + $this->log->addWriter(new Stream($this->stream)); + $this->adapter = new Zf2LogAdapter($this->log); + + } + + public function testLogsMessagesToAdaptedObject() + { + // Test without a priority + $this->adapter->log('Zend_Test!', \LOG_NOTICE); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(1, substr_count($contents, 'Zend_Test!')); + + // Test with a priority + $this->adapter->log('Zend_Test!', \LOG_ALERT); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(2, substr_count($contents, 'Zend_Test!')); + } + + public function testExposesAdaptedLogObject() + { + $this->assertEquals($this->log, $this->adapter->getLogObject()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php new file mode 100644 index 0000000..3fb6527 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php @@ -0,0 +1,21 @@ +command = $command; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php new file mode 100644 index 0000000..aabb15f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php @@ -0,0 +1,25 @@ +command = $command; + $this->response = $response; + $this->message = 'Error from ' . $response; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php new file mode 100644 index 0000000..97a1974 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php @@ -0,0 +1,11 @@ +multiHandle; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php new file mode 100644 index 0000000..11e22eb --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php @@ -0,0 +1,65 @@ +events as $event) { + if ($event->getName() == $eventName) { + return true; + } + } + + return false; + } + + public function getLastEvent() + { + return end($this->events); + } + + public function count() + { + return count($this->events); + } + + public function getGrouped() + { + $events = array(); + foreach ($this->events as $event) { + if (!isset($events[$event->getName()])) { + $events[$event->getName()] = array(); + } + $events[$event->getName()][] = $event; + } + + return $events; + } + + public function getData($event, $key, $occurrence = 0) + { + $grouped = $this->getGrouped(); + if (isset($grouped[$event])) { + return $grouped[$event][$occurrence][$key]; + } + + return null; + } + + public function update(Event $event) + { + $this->events[] = $event; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php new file mode 100644 index 0000000..e011959 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php @@ -0,0 +1,7 @@ + 'allseeing-i.com', + 'path' => '/', + 'data' => array( + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c' + ), + 'max_age' => NULL, + 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'version' => NULL, + 'secure' => NULL, + 'discard' => NULL, + 'port' => NULL, + 'cookies' => array( + 'ASIHTTPRequestTestCookie' => 'This+is+the+value' + ), + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array('', false), + array('foo', false), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'cookies' => array( + 'foo' => '' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'cookies' => array( + 'foo' => '1' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting multiple values + array(array( + 'foo=1; bar=2;', 'foo =1; bar = "2"', 'foo=1; bar=2'), + array( + 'cookies' => array( + 'foo' => '1', + 'bar' => '2', + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Tests getting the domain and path from a reference request + array(array( + 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path; port="80,8081"; HttpOnly;'), + array( + 'cookies' => array( + 'foo' => 1 + ), + 'data' => array(), + 'discard' => null, + 'domain' => 'www.test.com', + 'expires' => null, + 'max_age' => null, + 'path' => '/path', + 'port' => array('80', '8081'), + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => true + ), + 'http://www.test.com/path/' + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'cookies' => array( + 'justacookie' => 'foo' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'cookies' => array( + 'expires' => 'tomorrow' + ), + 'domain' => '.example.com', + 'path' => '/Space Out/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'data' => array(), + 'discard' => null, + 'port' => null, + 'secure' => true, + 'version' => null, + 'max_age' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'cookies' => array( + 'domain' => 'unittests' + ), + 'domain' => 'example.com', + 'path' => '/some value/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'cookies' => array( + 'path' => 'indexAction' + ), + 'domain' => '.foo.com', + 'path' => '/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'cookies' => array( + 'secure' => 'sha1' + ), + 'domain' => 'some.really.deep.domain.com', + 'path' => '/', + 'secure' => true, + 'data' => array(), + 'discard' => null, + 'expires' => time() + 86400, + 'max_age' => 86400, + 'port' => null, + 'version' => 1, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'cookies' => array( + 'PHPSESSID' => '123456789+abcd%2Cef' + ), + 'domain' => '.localdomain', + 'path' => '/foo/baz', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => true, + 'data' => array(), + 'discard' => true, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // rfc6265#section-5.1.4 + array( + 'cookie=value', + array( + 'cookies' => array( + 'cookie' => 'value' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/some/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/some/path/test.html' + ), + array( + 'empty=path', + array( + 'cookies' => array( + 'empty' => 'path' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/test.html' + ), + array( + 'baz=qux', + array( + 'cookies' => array( + 'baz' => 'qux' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com?query=here' + ), + array( + 'test=noSlashPath; path=someString', + array( + 'cookies' => array( + 'test' => 'noSlashPath' + ), + 'domain' => 'example.com', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/real/path', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ), + 'http://example.com/real/path/' + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + */ + public function testParseCookie($cookie, $parsed, $url = null) + { + $c = $this->cookieParserClass; + $parser = new $c(); + + $request = null; + if ($url) { + $url = Url::factory($url); + $host = $url->getHost(); + $path = $url->getPath(); + } else { + $host = ''; + $path = ''; + } + + foreach ((array) $cookie as $c) { + $p = $parser->parseCookie($c, $host, $path); + + // Remove expires values from the assertion if they are relatively equal by allowing a 5 minute difference + if ($p['expires'] != $parsed['expires']) { + if (abs($p['expires'] - $parsed['expires']) < 300) { + unset($p['expires']); + unset($parsed['expires']); + } + } + + if (is_array($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals($parsed, $p); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php new file mode 100644 index 0000000..75d336f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php @@ -0,0 +1,22 @@ +parseCookie('foo=baz+bar', null, null, true); + $this->assertEquals(array( + 'foo' => 'baz bar' + ), $result['cookies']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php new file mode 100644 index 0000000..da58bb4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php @@ -0,0 +1,225 @@ + 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php new file mode 100644 index 0000000..2f52228 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php @@ -0,0 +1,58 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } + + public function testParsesRequestsWithMissingProtocol() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesRequestsWithMissingVersion() + { + $parser = new MessageParser(); + $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); + $this->assertEquals('GET', $parts['method']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } + + public function testParsesResponsesWithMissingReasonPhrase() + { + $parser = new MessageParser(); + $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); + $this->assertEquals('200', $parts['code']); + $this->assertEquals('', $parts['reason_phrase']); + $this->assertEquals('HTTP', $parts['protocol']); + $this->assertEquals('1.1', $parts['version']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php new file mode 100644 index 0000000..6706e20 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php @@ -0,0 +1,36 @@ +markTestSkipped('pecl_http is not available.'); + } + } + + /** + * @dataProvider requestProvider + */ + public function testParsesRequests($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php new file mode 100644 index 0000000..7675efb --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php @@ -0,0 +1,33 @@ +registerParser('foo', $c); + $this->assertSame($c, $r->getParser('foo')); + } + + public function testReturnsNullWhenNotFound() + { + $r = new ParserRegistry(); + $this->assertNull($r->getParser('FOO')); + } + + public function testReturnsLazyLoadedDefault() + { + $r = new ParserRegistry(); + $c = $r->getParser('cookie'); + $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c); + $this->assertSame($c, $r->getParser('cookie')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php new file mode 100644 index 0000000..a05fc2e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php @@ -0,0 +1,113 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'null' => null, + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ), + 'empty_keys' => array(), + ); + + return array_map(function($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + array('{.null}', ''), + array('{.null,var}', '.value'), + array('X{.empty_keys*}', 'X'), + array('X{.empty_keys}', 'X'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php new file mode 100644 index 0000000..633c5d5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php @@ -0,0 +1,27 @@ +markTestSkipped('uri_template PECL extension must be installed to test PeclUriTemplate'); + } + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new PeclUriTemplate($template); + $this->assertEquals($expansion, $uri->expand($template, $params)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php new file mode 100644 index 0000000..5130d6f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php @@ -0,0 +1,106 @@ +assertEquals($expansion, $uri->expand($template, $params)); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/426 + */ + public function testSetRegex() + { + $template = new UriTemplate(); + $template->setRegex('/\<\$(.+)\>/'); + $this->assertSame('/foo', $template->expand('/<$a>', array('a' => 'foo'))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php new file mode 100644 index 0000000..16990a5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php @@ -0,0 +1,93 @@ +assertArrayHasKey('request.before_send', $events); + $this->assertArrayHasKey('request.exception', $events); + $this->assertArrayHasKey('curl.callback.progress', $events); + } + + public function testEnablesProgressCallbacks() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $event = new Event(array( + 'request' => $request + )); + $p->onBeforeSend($event); + $this->assertEquals(true, $request->getCurlOptions()->get('progress')); + } + + public function testAddsTimesOutAfterSending() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $handle = CurlHandle::factory($request); + $event = new Event(array( + 'request' => $request, + 'handle' => $handle->getHandle(), + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testEnsuresRequestIsSet() + { + $p = new AsyncPlugin(); + $event = new Event(array( + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + public function testMasksCurlExceptions() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $e = new CurlException('Error'); + $event = new Event(array( + 'request' => $request, + 'exception' => $e + )); + $p->onRequestTimeout($event); + $this->assertEquals(RequestInterface::STATE_COMPLETE, $request->getState()); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + } + + public function testEnsuresIntegration() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 204 FOO\r\nContent-Length: 4\r\n\r\ntest"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, array( + 'foo' => 'bar' + )); + $request->getEventDispatcher()->addSubscriber(new AsyncPlugin()); + $request->send(); + $this->assertEquals('', $request->getResponse()->getBody(true)); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('POST', $received[0]->getMethod()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php new file mode 100644 index 0000000..72af263 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php @@ -0,0 +1,86 @@ +getMockBuilder('Guzzle\Plugin\Backoff\AbstractBackoffStrategy') + ->setMethods(array('getDelay', 'makesDecision')) + ->getMockForAbstractClass(); + } + + public function testReturnsZeroWhenNoNextAndGotNull() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $this->assertEquals(0, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalse() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(false)); + $this->assertEquals(false, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsNextValueWhenNullOrTrue() + { + $request = new Request('GET', 'http://www.foo.com'); + $mock = $this->getMockStrategy(); + $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); + $mock->expects($this->any())->method('makesDecision')->will($this->returnValue(false)); + + $mock2 = $this->getMockStrategy(); + $mock2->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(10)); + $mock2->expects($this->atLeastOnce())->method('makesDecision')->will($this->returnValue(true)); + $mock->setNext($mock2); + + $this->assertEquals(10, $mock->getBackoffPeriod(0, $request)); + } + + public function testReturnsFalseWhenNullAndNoNext() + { + $request = new Request('GET', 'http://www.foo.com'); + $s = new TruncatedBackoffStrategy(2); + $this->assertFalse($s->getBackoffPeriod(0, $request)); + } + + public function testHasNext() + { + $a = new TruncatedBackoffStrategy(2); + $b = new TruncatedBackoffStrategy(2); + $a->setNext($b); + $this->assertSame($b, $a->getNext()); + } + + public function testSkipsOtherDecisionsInChainWhenOneReturnsTrue() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $c = new CallbackBackoffStrategy(function () { return null; }, true); + $d = new CallbackBackoffStrategy(function () { return 10; }, false); + $a->setNext($b); + $b->setNext($c); + $c->setNext($d); + $this->assertEquals(10, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } + + public function testReturnsZeroWhenDecisionMakerReturnsTrueButNoFurtherStrategiesAreInTheChain() + { + $a = new CallbackBackoffStrategy(function () { return null; }, true); + $b = new CallbackBackoffStrategy(function () { return true; }, true); + $a->setNext($b); + $this->assertSame(0, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php new file mode 100644 index 0000000..a64dd82 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php @@ -0,0 +1,110 @@ +message = ''; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffLogger::getSubscribedEvents())); + } + + public function testLogsEvents() + { + list($logPlugin, $request, $response) = $this->getMocks(); + + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setConstructorArgs(array(503)) + ->setMethods(array('getInfo')) + ->getMock(); + + $response->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + $handle = $this->getMockHandle(); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'retries' => 1, + 'delay' => 3, + 'handle' => $handle + )); + + $logPlugin->onRequestRetry($event); + $this->assertContains( + '] PUT http://www.example.com - 503 Service Unavailable - Retries: 1, Delay: 3, Time: 2, 2, cURL: 30 Foo', + $this->message + ); + } + + public function testCanSetTemplate() + { + $l = new BackoffLogger(new ClosureLogAdapter(function () {})); + $l->setTemplate('foo'); + $t = $this->readAttribute($l, 'formatter'); + $this->assertEquals('foo', $this->readAttribute($t, 'template')); + } + + /** + * @return array + */ + protected function getMocks() + { + $that = $this; + $logger = new ClosureLogAdapter(function ($message) use ($that) { + $that->message .= $message . "\n"; + }); + $logPlugin = new BackoffLogger($logger); + $response = new Response(503); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com', array( + 'Content-Length' => 3, + 'Foo' => 'Bar' + )); + + return array($logPlugin, $request, $response); + } + + /** + * @return CurlHandle + */ + protected function getMockHandle() + { + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') + ->disableOriginalConstructor() + ->setMethods(array('getError', 'getErrorNo', 'getInfo')) + ->getMock(); + + $handle->expects($this->once()) + ->method('getError') + ->will($this->returnValue('Foo')); + + $handle->expects($this->once()) + ->method('getErrorNo') + ->will($this->returnValue(30)); + + $handle->expects($this->any()) + ->method('getInfo') + ->will($this->returnValue(2)); + + return $handle; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php new file mode 100644 index 0000000..496e49e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php @@ -0,0 +1,297 @@ +retried = false; + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + public function onRequestRetry(Event $event) + { + $this->retried = $event; + } + + public function testHasEventList() + { + $this->assertEquals(1, count(BackoffPlugin::getAllEvents())); + } + + public function testCreatesDefaultExponentialBackoffPlugin() + { + $plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10)); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin); + $strategy = $this->readAttribute($plugin, 'strategy'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy); + $this->assertEquals(3, $this->readAttribute($strategy, 'max')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy); + $this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy); + $this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes')); + $strategy = $this->readAttribute($strategy, 'next'); + $this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy); + } + + public function testDoesNotRetryUnlessStrategyReturnsNumber() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface') + ->setMethods(array('getBackoffPeriod')) + ->getMockForAbstractClass(); + + $mock->expects($this->once()) + ->method('getBackoffPeriod') + ->will($this->returnValue(false)); + + $plugin = new BackoffPlugin($mock); + $plugin->addSubscriber($this); + $plugin->onRequestSent(new Event(array('request' => $request))); + $this->assertFalse($this->retried); + } + + public function testUpdatesRequestForRetry() + { + $request = new Request('GET', 'http://www.example.com'); + $request->setState('transfer'); + $response = new Response(500); + $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock(); + $e = new CurlException(); + $e->setCurlHandle($handle); + + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->addSubscriber($this); + + $event = new Event(array( + 'request' => $request, + 'response' => $response, + 'exception' => $e + )); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 1, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + + $plugin->onRequestSent($event); + $this->assertEquals(array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle, + 'retries' => 2, + 'delay' => 10 + ), $this->readAttribute($this->retried, 'context')); + } + + public function testDoesNothingWhenNotRetryingAndPollingRequest() + { + $request = new Request('GET', 'http://www.foo.com'); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); + $plugin->onRequestPoll(new Event(array('request' => $request))); + } + + public function testRetriesRequests() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(3, + new HttpBackoffStrategy(null, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that three requests were made to retry this request + $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); + $this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM)); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testFailsOnTruncation() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" + )); + + $plugin = new BackoffPlugin( + new TruncatedBackoffStrategy(2, + new HttpBackoffStrategy(null, + new ConstantBackoffStrategy(0.05) + ) + ) + ); + + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber($plugin); + $client->get()->send(); + } + + public function testRetriesRequestsWhenInParallel() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new BackoffPlugin( + new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(3, + new CurlBackoffStrategy(null, + new ConstantBackoffStrategy(0.1) + ) + ) + ) + ); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $requests = array(); + for ($i = 0; $i < 5; $i++) { + $requests[] = $client->get(); + } + $client->send($requests); + + $this->assertEquals(15, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Plugin\Backoff\BackoffPlugin + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRetriesPooledRequestsUsingDelayAndPollingEvent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + // Need to sleep for some time ensure that the polling works correctly in the observer + $plugin = new BackoffPlugin(new HttpBackoffStrategy(null, + new TruncatedBackoffStrategy(1, + new ConstantBackoffStrategy(0.5)))); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + // Make sure it eventually completed successfully + $this->assertEquals('data', $request->getResponse()->getBody(true)); + // Check that two requests were made to retry this request + $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); + } + + public function testSeeksToBeginningOfRequestBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('abc'); + // Set the retry time to be something that will be retried always + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + // Seek to the end of the stream + $request->getBody()->seek(3); + $this->assertEquals('', $request->getBody()->read(1)); + // Create a plugin that does not delay when retrying + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + // Ensure that the stream was seeked to 0 + $this->assertEquals('a', $request->getBody()->read(1)); + } + + public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying() + { + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); + $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); + $plugin->onRequestPoll($this->getMockEvent($request)); + } + + protected function getMockEvent(RequestInterface $request) + { + // Create a mock curl multi object + $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti') + ->setMethods(array('remove', 'add')) + ->getMock(); + + // Create an event that is expected for the Poll event + $event = new Event(array( + 'request' => $request, + 'curl_multi' => $multi + )); + $event->setName(CurlMultiInterface::POLLING_REQUEST); + + return $event; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php new file mode 100644 index 0000000..c0ce10d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php @@ -0,0 +1,31 @@ +getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $strategy = new CallbackBackoffStrategy(function () { return 10; }, true); + $this->assertTrue($strategy->makesDecision()); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request)); + // Ensure it chains correctly when null is returned + $strategy = new CallbackBackoffStrategy(function () { return null; }, false); + $this->assertFalse($strategy->makesDecision()); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php new file mode 100644 index 0000000..703eb4a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php @@ -0,0 +1,20 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(3.5, $strategy->getBackoffPeriod(1, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php new file mode 100644 index 0000000..0a5c3e2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php @@ -0,0 +1,36 @@ +assertNotEmpty(CurlBackoffStrategy::getDefaultFailureCodes()); + $strategy = new CurlBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $e = new CurlException(); + $e->setError('foo', CURLE_BAD_CALLING_ORDER); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, null, $e)); + + foreach (CurlBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, null, $e->setError('foo', $code))); + } + } + + public function testIgnoresNonErrors() + { + $strategy = new CurlBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, new Response(200))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php new file mode 100644 index 0000000..09965bc --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php @@ -0,0 +1,23 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(1, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(2, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(4, $strategy->getBackoffPeriod(2, $request)); + $this->assertEquals(8, $strategy->getBackoffPeriod(3, $request)); + $this->assertEquals(16, $strategy->getBackoffPeriod(4, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php new file mode 100644 index 0000000..ae68a4e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php @@ -0,0 +1,47 @@ +assertNotEmpty(HttpBackoffStrategy::getDefaultFailureCodes()); + $strategy = new HttpBackoffStrategy(); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(400); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + + foreach (HttpBackoffStrategy::getDefaultFailureCodes() as $code) { + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response->setStatus($code))); + } + } + + public function testAllowsCustomCodes() + { + $strategy = new HttpBackoffStrategy(array(204)); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(204); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(500); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new HttpBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php new file mode 100644 index 0000000..b4ce8e4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php @@ -0,0 +1,21 @@ +assertFalse($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request)); + $this->assertEquals(5, $strategy->getBackoffPeriod(1, $request)); + $this->assertEquals(10, $strategy->getBackoffPeriod(2, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php new file mode 100644 index 0000000..dea5a68 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php @@ -0,0 +1,32 @@ +assertEmpty(ReasonPhraseBackoffStrategy::getDefaultFailureCodes()); + $strategy = new ReasonPhraseBackoffStrategy(array('Foo', 'Internal Server Error')); + $this->assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $response = new Response(200); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); + $response->setStatus(200, 'Foo'); + $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); + } + + public function testIgnoresNonErrors() + { + $strategy = new ReasonPhraseBackoffStrategy(); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php new file mode 100644 index 0000000..5590dfb --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php @@ -0,0 +1,30 @@ +assertTrue($strategy->makesDecision()); + $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); + $this->assertFalse($strategy->getBackoffPeriod(0, $request)); + $this->assertFalse($strategy->getBackoffPeriod(1, $request)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request)); + + $response = new Response(500); + $strategy->setNext(new HttpBackoffStrategy(null, new ConstantBackoffStrategy(10))); + $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request, $response)); + $this->assertEquals(10, $strategy->getBackoffPeriod(1, $request, $response)); + $this->assertFalse($strategy->getBackoffPeriod(2, $request, $response)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php new file mode 100644 index 0000000..69da60a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php @@ -0,0 +1,441 @@ +assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testAddsDefaultCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CanCacheStrategyInterface', + $this->readAttribute($plugin, 'canCache') + ); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\RevalidationInterface', + $this->readAttribute($plugin, 'revalidation') + ); + } + + public function testAddsCallbackCollaborators() + { + $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); + $plugin = new CachePlugin(array('can_cache' => function () {})); + $this->assertInstanceOf( + 'Guzzle\Plugin\Cache\CallbackCanCacheStrategy', + $this->readAttribute($plugin, 'canCache') + ); + } + + public function testCanPassCacheAsOnlyArgumentToConstructor() + { + $p = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $p = new CachePlugin(new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()))); + } + + public function testUsesCreatedCacheStorage() + { + $plugin = new CachePlugin(array( + 'adapter' => $this->getMockBuilder('Guzzle\Cache\CacheAdapterInterface')->getMockForAbstractClass() + )); + $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); + } + + public function testUsesProvidedOptions() + { + $can = $this->getMockBuilder('Guzzle\Plugin\Cache\CanCacheStrategyInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\RevalidationInterface')->getMockForAbstractClass(); + $plugin = new CachePlugin(array( + 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(), + 'can_cache' => $can, + 'revalidation' => $revalidate + )); + $this->assertSame($can, $this->readAttribute($plugin, 'canCache')); + $this->assertSame($revalidate, $this->readAttribute($plugin, 'revalidation')); + } + + public function satisfyProvider() + { + $req1 = new Request('GET', 'http://foo.com', array('Cache-Control' => 'no-cache')); + + return array( + // The response is too old to satisfy the request + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-age=20')), new Response(200, array('Age' => 100)), false, false), + // The response cannot satisfy the request because it is stale + array(new Request('GET', 'http://foo.com'), new Response(200, array('Cache-Control' => 'max-age=10', 'Age' => 100)), false, false), + // Allows the expired response to satisfy the request because of the max-stale + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=15')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), true, false), + // Max stale is > than the allowed staleness + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=5')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), false, false), + // Performs cache revalidation + array($req1, new Response(200), true, true), + // Performs revalidation due to ETag on the response and no cache-control on the request + array(new Request('GET', 'http://foo.com'), new Response(200, array( + 'ETag' => 'ABC', + 'Expires' => date('c', strtotime('+1 year')) + )), true, true), + ); + } + + /** + * @dataProvider satisfyProvider + */ + public function testChecksIfResponseCanSatisfyRequest($request, $response, $can, $revalidates) + { + $didRevalidate = false; + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(); + $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setMethods(array('revalidate')) + ->setConstructorArgs(array($storage)) + ->getMockForAbstractClass(); + + $revalidate->expects($this->any()) + ->method('revalidate') + ->will($this->returnCallback(function () use (&$didRevalidate) { + $didRevalidate = true; + return true; + })); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'revalidation' => $revalidate + )); + + $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response)); + $this->assertEquals($didRevalidate, $revalidates); + } + + public function satisfyFailedProvider() + { + return array( + // Neither has stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100)), false), + // Request has stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has valid stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), + // Request has expired stale-if-error + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), false), + // Response has permanent stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error', )), true), + // Response has valid stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), true), + // Response has expired stale-if-error + array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Request has valid stale-if-error but response does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), + // Response has valid stale-if-error but request does not + array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), false), + ); + } + + /** + * @dataProvider satisfyFailedProvider + */ + public function testChecksIfResponseCanSatisfyFailedRequest($request, $response, $can) + { + $plugin = new CachePlugin(); + + $this->assertEquals($can, $plugin->canResponseSatisfyFailedRequest($request, $response)); + } + + public function testDoesNothingWhenRequestIsNotCacheable() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->never())->method('fetch'); + + $plugin = new CachePlugin(array( + 'storage' => $storage, + 'can_cache' => new CallbackCanCacheStrategy(function () { return false; }) + )); + + $plugin->onRequestBeforeSend(new Event(array( + 'request' => new Request('GET', 'http://foo.com') + ))); + } + + public function satisfiableProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // Fresh response + array(new Response(200, array(), 'foo')), + // Stale response + array(new Response(200, array('Date' => $date->format('c'), 'Cache-Control' => 'max-age=5'), 'foo')) + ); + } + + /** + * @dataProvider satisfiableProvider + */ + public function testInjectsSatisfiableResponses($response) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $storage->expects($this->once())->method('fetch')->will($this->returnValue($response)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestSent(new Event(array('request' => $request, 'response' => $request->getResponse()))); + $this->assertEquals($response->getStatusCode(), $request->getResponse()->getStatusCode()); + $this->assertEquals((string) $response->getBody(), (string) $request->getResponse()->getBody()); + $this->assertTrue($request->getResponse()->hasHeader('Age')); + if ($request->getResponse()->isFresh() === false) { + $this->assertContains('110', (string) $request->getResponse()->getHeader('Warning')); + } + $this->assertSame( + sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), + (string) $request->getHeader('Via') + ); + $this->assertSame( + sprintf('%s GuzzleCache/%s',$request->getProtocolVersion(), Version::VERSION), + (string) $request->getResponse()->getHeader('Via') + ); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertTrue($request->getParams()->get('cache.hit')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache-Lookup')); + $this->assertTrue($request->getResponse()->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache-Lookup')); + } + + public function satisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + return array( + array( + new Response(200, array( + 'Date' => $date->format('c'), + 'Cache-Control' => 'max-age=5, stale-if-error' + ), 'foo'), + ) + ); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnError($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + )) + ); + $response = $event['response']; + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @dataProvider satisfiableOnErrorProvider + */ + public function testInjectsSatisfiableResponsesOnException($cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + new Event(array( + 'request' => $request, + 'response' => $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + $plugin->onRequestSent( + new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); + $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); + $this->assertTrue($response->hasHeader('Age')); + if ($response->isFresh() === false) { + $this->assertContains('110', (string) $response->getHeader('Warning')); + } + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); + $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); + $this->assertTrue($request->getParams()->get('cache.lookup')); + $this->assertSame('error', $request->getParams()->get('cache.hit')); + $this->assertTrue($response->hasHeader('X-Cache-Lookup')); + $this->assertTrue($response->hasHeader('X-Cache')); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + public function unsatisfiableOnErrorProvider() + { + $date = new \DateTime('-10 seconds'); + + return array( + // no-store on request + array( + false, + array('Cache-Control' => 'no-store'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // request expired + array( + true, + array('Cache-Control' => 'stale-if-error=4'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), + ), + // response expired + array( + true, + array('Cache-Control' => 'stale-if-error'), + new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error=4'), 'foo'), + ), + ); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnError($requestCanCache, $requestHeaders, $cacheResponse) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($cacheResponse)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestError( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + )) + ); + + $this->assertSame($response, $event['response']); + } + + /** + * @dataProvider unsatisfiableOnErrorProvider + */ + public function testDoesNotInjectUnsatisfiableResponsesOnException($requestCanCache, $requestHeaders, $responseParts) + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($responseParts)); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', $requestHeaders); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestException( + $event = new Event(array( + 'request' => $request, + 'response' => $response = $request->getResponse(), + 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), + )) + ); + + $this->assertSame($response, $request->getResponse()); + } + + public function testCachesResponsesWhenCacheable() + { + $cache = new ArrayCache(); + $plugin = new CachePlugin($cache); + + $request = new Request('GET', 'http://foo.com'); + $response = new Response(200, array(), 'Foo'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + $plugin->onRequestSent(new Event(array( + 'request' => $request, + 'response' => $response + ))); + $data = $this->readAttribute($cache, 'data'); + $this->assertNotEmpty($data); + } + + public function testPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage)); + $request = new Request('GET', 'http://foo.com', array('X-Foo' => 'Bar')); + $plugin->purge($request); + } + + public function testAutoPurgesRequests() + { + $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') + ->setMethods(array('purge')) + ->getMockForAbstractClass(); + $storage->expects($this->atLeastOnce())->method('purge'); + $plugin = new CachePlugin(array('storage' => $storage, 'auto_purge' => true)); + $client = new Client(); + $request = $client->put('http://foo.com', array('X-Foo' => 'Bar')); + $request->addSubscriber($plugin); + $request->setResponse(new Response(200), true); + $request->send(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php new file mode 100644 index 0000000..f3d9baf --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php @@ -0,0 +1,72 @@ +assertTrue($c->canCacheRequest(new Request('DELETE', 'http://www.foo.com'))); + } + + /** + * The following is a bit of an integration test to ensure that the CachePlugin honors a + * custom can cache strategy. + */ + public function testIntegrationWithCachePlugin() + { + $c = new CallbackCanCacheStrategy( + function ($request) { return true; }, + function ($response) { return true; } + ); + + // Make a request and response that have no business being cached + $request = new Request('DELETE', 'http://www.foo.com'); + $response = Response::fromMessage( + "HTTP/1.1 200 OK\r\n" + . "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" + . "Last-Modified: Wed, 09 Jan 2013 08:48:53 GMT\r\n" + . "Content-Length: 2\r\n" + . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n\r\n" + . "hi" + ); + + $this->assertTrue($c->canCacheRequest($request)); + $this->assertTrue($c->canCacheResponse($response)); + + $s = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultCacheStorage') + ->setConstructorArgs(array(new DoctrineCacheAdapter(new ArrayCache()))) + ->setMethods(array('fetch')) + ->getMockForAbstractClass(); + + $s->expects($this->once()) + ->method('fetch') + ->will($this->returnValue($response)); + + $plugin = new CachePlugin(array('can_cache' => $c, 'storage' => $s)); + $plugin->onRequestBeforeSend(new Event(array('request' => $request))); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('hi', $request->getResponse()->getBody(true)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php new file mode 100644 index 0000000..701a015 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php @@ -0,0 +1,193 @@ + 'application/json')); + $response = new Response(200, array( + 'Content-Type' => 'application/json', + 'Connection' => 'close', + 'X-Foo' => 'Bar', + 'Vary' => 'Accept' + ), 'test'); + $s->cache($request, $response); + $data = $this->readAttribute($a, 'data'); + + return array( + 'cache' => $a, + 'adapter' => $c, + 'storage' => $s, + 'request' => $request, + 'response' => $response, + 'serialized' => end($data) + ); + } + + public function testReturnsNullForCacheMiss() + { + $cache = $this->getCache(); + $this->assertNull($cache['storage']->fetch(new Request('GET', 'http://test.com'))); + } + + public function testCachesRequests() + { + $cache = $this->getCache(); + $foundRequest = $foundBody = $bodyKey = false; + foreach ($this->readAttribute($cache['cache'], 'data') as $key => $v) { + if (strpos($v, 'foo.com')) { + $foundRequest = true; + $data = unserialize($v); + $bodyKey = $data[0][3]; + $this->assertInternalType('integer', $data[0][4]); + $this->assertFalse(isset($data[0][0]['connection'])); + $this->assertEquals('foo.com', $data[0][0]['host']); + } elseif ($v == 'test') { + $foundBody = $key; + } + } + $this->assertContains($bodyKey, $foundBody); + $this->assertTrue($foundRequest); + } + + public function testFetchesResponse() + { + $cache = $this->getCache(); + $response = $cache['storage']->fetch($cache['request']); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertFalse($response->hasHeader('Connection')); + $this->assertEquals('Bar', (string) $response->getHeader('X-Foo')); + $this->assertEquals('test', (string) $response->getBody()); + $this->assertTrue(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testDeletesRequestItemsAndBody() + { + $cache = $this->getCache(); + $cache['storage']->delete($cache['request']); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + } + + public function testCachesMultipleRequestsWithVary() + { + $cache = $this->getCache(); + $cache['request']->setHeader('Accept', 'application/xml'); + $response = $cache['response']->setHeader('Content-Type', 'application/xml'); + $response->setBody('123'); + $cache['storage']->cache($cache['request'], $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertEquals(2, count($u)); + $this->assertEquals($u[0][0]['accept'], 'application/xml'); + $this->assertEquals($u[0][1]['content-type'], 'application/xml'); + $this->assertEquals($u[1][0]['accept'], 'application/json'); + $this->assertEquals($u[1][1]['content-type'], 'application/json'); + $this->assertNotSame($u[0][3], $u[1][3]); + break; + } + } + } + + public function testPurgeRemovesAllMethodCaches() + { + $cache = $this->getCache(); + foreach (array('HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $request = RequestFactory::getInstance()->cloneRequestWithMethod($cache['request'], $method); + $cache['storage']->cache($request, $cache['response']); + } + $cache['storage']->purge('http://foo.com'); + $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); + $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); + $this->assertEquals( + array('DoctrineNamespaceCacheKey[]'), + array_keys($this->readAttribute($cache['cache'], 'data')) + ); + } + + public function testRemovesExpiredResponses() + { + $cache = $this->getCache(); + $request = new Request('GET', 'http://xyz.com'); + $response = new Response(200, array('Age' => 1000, 'Cache-Control' => 'max-age=-10000')); + $cache['storage']->cache($request, $response); + $this->assertNull($cache['storage']->fetch($request)); + $data = $this->readAttribute($cache['cache'], 'data'); + $this->assertFalse(in_array('xyz.com', $data)); + $this->assertTrue(in_array($cache['serialized'], $data)); + } + + public function testUsesVaryToDetermineResult() + { + $cache = $this->getCache(); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $cache['storage']->fetch($cache['request'])); + $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/xml')); + $this->assertNull($cache['storage']->fetch($request)); + } + + public function testEnsuresResponseIsStillPresent() + { + $cache = $this->getCache(); + $data = $this->readAttribute($cache['cache'], 'data'); + $key = array_search('test', $data); + $cache['cache']->delete(substr($key, 1, -4)); + $this->assertNull($cache['storage']->fetch($cache['request'])); + } + + public function staleProvider() + { + return array( + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error=100', 'Vary' => 'Accept')) + ), + array( + new Request('GET', 'http://foo.com', array('Accept' => 'foo')), + new Response(200, array('Cache-Control' => 'stale-if-error', 'Vary' => 'Accept')) + ) + ); + } + + /** + * @dataProvider staleProvider + */ + public function testUsesStaleTimeDirectiveForTtd($request, $response) + { + $cache = $this->getCache(); + $cache['storage']->cache($request, $response); + $data = $this->readAttribute($cache['cache'], 'data'); + foreach ($data as $v) { + if (strpos($v, 'foo.com')) { + $u = unserialize($v); + $this->assertGreaterThan($u[1][4], $u[0][4]); + break; + } + } + } + + public function testCanFilterCacheKeys() + { + $cache = $this->getCache(); + $cache['request']->getQuery()->set('auth', 'foo'); + $this->assertNull($cache['storage']->fetch($cache['request'])); + $cache['request']->getParams()->set('cache.key_filter', 'auth'); + $this->assertNotNull($cache['storage']->fetch($cache['request'])); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php new file mode 100644 index 0000000..de4d182 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php @@ -0,0 +1,40 @@ +assertTrue($strategy->canCacheRequest($request)); + } + + public function testDoesNotCacheNoStore() + { + $strategy = new DefaultCanCacheStrategy(); + $request = new Request('GET', 'http://foo.com', array('cache-control' => 'no-store')); + $this->assertFalse($strategy->canCacheRequest($request)); + } + + public function testCanCacheResponse() + { + $response = $this->getMockBuilder('Guzzle\Http\Message\Response') + ->setMethods(array('canCache')) + ->setConstructorArgs(array(200)) + ->getMock(); + $response->expects($this->once()) + ->method('canCache') + ->will($this->returnValue(true)); + $strategy = new DefaultCanCacheStrategy(); + $this->assertTrue($strategy->canCacheResponse($response)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php new file mode 100644 index 0000000..0699cb2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php @@ -0,0 +1,248 @@ +getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\nContent-Length: 0\r\n\r\n", + ), + // Forces revalidation that overwrites what is in cache + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . $this->getHttpDate('now') . "\r\n\r\nDatas" + ), + // Throws an exception during revalidation + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . $this->getHttpDate('-3 hours') . "\r\n\r\nData", + "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n" + ), + // ETag mismatch + array( + false, + "\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", + ), + ); + } + + /** + * @dataProvider cacheRevalidationDataProvider + */ + public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null) + { + // Send some responses to the test server for cache validation + $server = $this->getServer(); + $server->flush(); + + if ($validate) { + $server->enqueue($validate); + } + + $request = RequestFactory::getInstance()->fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request); + $response = Response::fromMessage($response); + $request->setClient(new Client()); + + $plugin = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); + $this->assertEquals( + $can, + $plugin->canResponseSatisfyRequest($request, $response), + '-> ' . $request . "\n" . $response + ); + + if ($result) { + $result = Response::fromMessage($result); + $result->removeHeader('Date'); + $request->getResponse()->removeHeader('Date'); + $request->getResponse()->removeHeader('Connection'); + // Get rid of dates + $this->assertEquals((string) $result, (string) $request->getResponse()); + } + + if ($validate) { + $this->assertEquals(1, count($server->getReceivedRequests())); + } + } + + public function testHandles404RevalidationResponses() + { + $request = new Request('GET', 'http://foo.com'); + $request->setClient(new Client()); + $badResponse = new Response(404, array(), 'Oh no!'); + $badRequest = clone $request; + $badRequest->setResponse($badResponse, true); + $response = new Response(200, array(), 'foo'); + + // Seed the cache + $s = new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + $s->cache($request, $response); + $this->assertNotNull($s->fetch($request)); + + $rev = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') + ->setConstructorArgs(array($s)) + ->setMethods(array('createRevalidationRequest')) + ->getMock(); + + $rev->expects($this->once()) + ->method('createRevalidationRequest') + ->will($this->returnValue($badRequest)); + + try { + $rev->revalidate($request, $response); + $this->fail('Should have thrown an exception'); + } catch (BadResponseException $e) { + $this->assertSame($badResponse, $e->getResponse()); + $this->assertNull($s->fetch($request)); + } + } + + public function testCanRevalidateWithPlugin() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(3, count($this->getServer()->getReceivedRequests())); + } + + public function testCanHandleRevalidationFailures() + { + $client = new Client($this->getServer()->getUrl()); + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'max-age=100, must-revalidate, stale-if-error=9999', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Bleh'), + new CurlException('Bleh') + )); + $client->addSubscriber(new CachePlugin()); + $client->addSubscriber($mock); + $client->get()->send(); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('hi', $response->getBody(true)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + $this->assertEquals(0, count($mock->getQueue())); + } + + public function testCanHandleStaleIfErrorWhenRevalidating() + { + $lm = gmdate('c', time() - 60); + $mock = new MockPlugin(array( + new Response(200, array( + 'Date' => $lm, + 'Cache-Control' => 'must-revalidate, max-age=0, stale-if-error=1200', + 'Last-Modified' => $lm, + 'Content-Length' => 2 + ), 'hi'), + new CurlException('Oh no!'), + new CurlException('Oh no!') + )); + $cache = new CachePlugin(); + $client = new Client('http://www.example.com'); + $client->addSubscriber($cache); + $client->addSubscriber($mock); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $response = $client->get()->send(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertCount(0, $mock); + $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); + $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); + } + + /** + * @group issue-437 + */ + public function testDoesNotTouchClosureListeners() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . + "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Content-Length: 2\r\n\r\nhi", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + "HTTP/1.0 304 Not Modified\r\n" . + "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . + "Content-Type: text/html; charset=UTF-8\r\n" . + "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . + "Age: 6302\r\n\r\n", + )); + $client = new Client($this->getServer()->getUrl()); + $client->addSubscriber(new CachePlugin()); + $client->getEventDispatcher()->addListener('command.after_send', function(){}); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + $this->assertEquals(200, $client->get()->send()->getStatusCode()); + } + +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php new file mode 100644 index 0000000..9af80f2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php @@ -0,0 +1,19 @@ +assertFalse($deny->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php new file mode 100644 index 0000000..4bcc04b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php @@ -0,0 +1,19 @@ +assertTrue($skip->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php new file mode 100644 index 0000000..5d0f668 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php @@ -0,0 +1,385 @@ +jar = new ArrayCookieJar(); + } + + protected function getTestCookies() + { + return array( + new Cookie(array('name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/', 'discard' => true)), + new Cookie(array('name' => 'test', 'value' => '123', 'domain' => 'baz.com', 'path' => '/foo', 'expires' => 2)), + new Cookie(array('name' => 'you', 'value' => '123', 'domain' => 'bar.com', 'path' => '/boo', 'expires' => time() + 1000)) + ); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return array( + array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false), + array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true), + array(array('googoo'), 'www.example.com', '', '', false), + array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false), + array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('baz'), 'example.com', '', 'baz', false), + ); + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->add($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->all(null, null, null, false, false)); + } + + public function testRemovesExpiredCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeExpired(); + $this->assertEquals(array($cookies[0], $cookies[2]), $this->jar->all()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeTemporary(); + $this->assertEquals(array($cookies[2]), $this->jar->all()); + } + + public function testIsSerializable() + { + $this->assertEquals('[]', $this->jar->serialize()); + $this->jar->unserialize('[]'); + $this->assertEquals(array(), $this->jar->all()); + + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove discard and expired cookies + $serialized = $this->jar->serialize(); + $data = json_decode($serialized, true); + $this->assertEquals(1, count($data)); + + $a = new ArrayCookieJar(); + $a->unserialize($serialized); + $this->assertEquals(1, count($a)); + } + + public function testRemovesSelectively() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove foo.com cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->remove('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->remove(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->add(new Cookie())); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo' + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => false + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => true + )))); + $this->assertFalse($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com' + )))); + } + + public function testDoesAddValidCookies() + { + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => 0.0 + )))); + $this->assertTrue($this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com', + 'value' => '0' + )))); + } + + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + unset($data['discard']); + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->add(new Cookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['expires'] = time() + 2000; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->all(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + $data['value'] = 'boo'; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + // Changing the value plus a parameter also must overwrite the existing one + $data['value'] = 'zoo'; + $data['secure'] = false; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals('zoo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithNoRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => array( + "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1", + "CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1" + ) + )); + + $this->jar->addCookiesFromResponse($response); + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(1, count($this->jar->all(null, null, 'fpc'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'FPCK3'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'CH'))); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $request = new Request('GET', 'http://www.example.com'); + $this->jar->addCookiesFromResponse($response, $request); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', array(0)), + array('http://example.com', array()), + array('https://example.com:8912', array()), + array('https://foo.example.com', array(0)), + array('http://foo.example.com/test/acme/', array(4)) + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = array( + new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(443, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'baz', + 'value' => 'foobar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'test', + 'value' => '123', + 'domain' => 'www.foobar.com', + 'path' => '/path/', + 'discard' => true + )), + new Cookie(array( + 'name' => 'muppet', + 'value' => 'cookie_monster', + 'domain' => '.y.example.com', + 'path' => '/acme/', + 'comment' => 'Comment goes here...', + 'expires' => time() + 86400 + )), + new Cookie(array( + 'name' => 'googoo', + 'value' => 'gaga', + 'domain' => '.example.com', + 'path' => '/test/acme/', + 'max_age' => 1500, + 'version' => 2 + )) + ); + + foreach ($bag as $cookie) { + $this->jar->add($cookie); + } + + $request = new Request('GET', $url); + $results = $this->jar->getMatchingCookies($request); + $this->assertEquals(count($cookies), count($results)); + foreach ($cookies as $i) { + $this->assertContains($bag[$i], $results); + } + } + + /** + * @expectedException \Guzzle\Plugin\Cookie\Exception\InvalidCookieException + * @expectedExceptionMessage The cookie name must not contain invalid characters: abc:@123 + */ + public function testThrowsExceptionWithStrictMode() + { + $a = new ArrayCookieJar(); + $a->setStrictMode(true); + $a->add(new Cookie(array( + 'name' => 'abc:@123', + 'value' => 'foo', + 'domain' => 'bar' + ))); + } + + public function testRemoveExistingCookieIfEmpty() + { + // Add a cookie that should not be affected + $a = new Cookie(array( + 'name' => 'foo', + 'value' => 'nope', + 'domain' => 'foo.com', + 'path' => '/abc' + )); + $this->jar->add($a); + + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'path' => '/' + ); + + $b = new Cookie($data); + $this->assertTrue($this->jar->add($b)); + $this->assertEquals(2, count($this->jar)); + + // Try to re-set the same cookie with no value: assert that cookie is not added + $data['value'] = null; + $this->assertFalse($this->jar->add(new Cookie($data))); + // assert that original cookie has been deleted + $cookies = $this->jar->all('foo.com'); + $this->assertTrue(in_array($a, $cookies, true)); + $this->assertFalse(in_array($b, $cookies, true)); + $this->assertEquals(1, count($this->jar)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php new file mode 100644 index 0000000..ac9471f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php @@ -0,0 +1,63 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals(array(), $jar->all()); + unlink($this->file); + } + + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->add(new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'baz', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'boo', + 'value' => 'bar', + 'domain' => 'foo.com', + ))); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php new file mode 100644 index 0000000..f8c175c --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php @@ -0,0 +1,134 @@ +getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('addCookiesFromResponse')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('addCookiesFromResponse') + ->with($response); + + $plugin = new CookiePlugin($mock); + $plugin->onRequestSent(new Event(array( + 'response' => $response + ))); + } + + public function testAddsCookiesToRequests() + { + $cookie = new Cookie(array( + 'name' => 'foo', + 'value' => 'bar' + )); + + $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar') + ->setMethods(array('getMatchingCookies')) + ->getMock(); + + $mock->expects($this->once()) + ->method('getMatchingCookies') + ->will($this->returnValue(array($cookie))); + + $plugin = new CookiePlugin($mock); + + $client = new Client(); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('http://www.example.com'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + + $this->assertEquals('bar', $request->getCookie('foo')); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $plugin = new CookiePlugin(new ArrayCookieJar()); + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" . + "Location: /redirect\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $client->get()->send(); + $request = $client->get(); + $request->send(); + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + + $requests = $this->getServer()->getReceivedRequests(true); + // Confirm subsequent requests have the cookie. + $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); + // Confirm the redirected request has the cookie. + $this->assertEquals('test=583551', $requests[1]->getHeader('Cookie')); + } + + public function testCookiesAreNotAddedWhenParamIsSet() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + + $jar->add(new Cookie(array( + 'domain' => 'example.com', + 'path' => '/', + 'name' => 'test', + 'value' => 'hi', + 'expires' => time() + 3600 + ))); + + $client = new Client('http://example.com'); + $client->getEventDispatcher()->addSubscriber($plugin); + + // Ensure that it is normally added + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertEquals('hi', $request->getCookie('test')); + + // Now ensure that it is not added + $request = $client->get(); + $request->getParams()->set('cookies.disable', true); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertNull($request->getCookie('test')); + } + + public function testProvidesCookieJar() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } + + public function testEscapesCookieDomains() + { + $cookie = new Cookie(array('domain' => '/foo/^$[A-Z]+/')); + $this->assertFalse($cookie->matchesDomain('foo')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php new file mode 100644 index 0000000..9fb0b43 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php @@ -0,0 +1,223 @@ +assertEquals('/', $cookie->getPath()); + $this->assertEquals(array(), $cookie->getPorts()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new Cookie(array( + 'expires' => 'November 20, 1984' + )); + $this->assertTrue(is_numeric($cookie->getExpires())); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new Cookie(array( + 'max_age' => 100 + )); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'name' => 'foo', + 'value' => 'baz', + 'path' => '/bar', + 'domain' => 'baz.com', + 'expires' => $t, + 'max_age' => 100, + 'comment' => 'Hi', + 'comment_url' => 'foo.com', + 'port' => array(1, 2), + 'version' => 2, + 'secure' => true, + 'discard' => true, + 'http_only' => true, + 'data' => array( + 'foo' => 'baz', + 'bar' => 'bam' + ) + ); + + $cookie = new Cookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertEquals('Hi', $cookie->getComment()); + $this->assertEquals('foo.com', $cookie->getCommentUrl()); + $this->assertEquals(array(1, 2), $cookie->getPorts()); + $this->assertEquals(2, $cookie->getVersion()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->getAttribute('foo')); + $this->assertEquals('bam', $cookie->getAttribute('bar')); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'bam' + ), $cookie->getAttributes()); + + $cookie->setName('a') + ->setValue('b') + ->setPath('c') + ->setDomain('bar.com') + ->setExpires(10) + ->setMaxAge(200) + ->setComment('e') + ->setCommentUrl('f') + ->setPorts(array(80)) + ->setVersion(3) + ->setSecure(false) + ->setHttpOnly(false) + ->setDiscard(false) + ->setAttribute('snoop', 'dog'); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertEquals('e', $cookie->getComment()); + $this->assertEquals('f', $cookie->getCommentUrl()); + $this->assertEquals(array(80), $cookie->getPorts()); + $this->assertEquals(3, $cookie->getVersion()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + $this->assertEquals('dog', $cookie->getAttribute('snoop')); + } + + public function testDeterminesIfExpired() + { + $c = new Cookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesPorts() + { + $cookie = new Cookie(); + // Always matches when nothing is set + $this->assertTrue($cookie->matchesPort(2)); + + $cookie->setPorts(array(1, 2)); + $this->assertTrue($cookie->matchesPort(2)); + $this->assertFalse($cookie->matchesPort(100)); + } + + public function testMatchesDomain() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('127.0.0.1'); + $this->assertTrue($cookie->matchesDomain('127.0.0.1')); + + $cookie->setDomain('.com.'); + $this->assertFalse($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('.local'); + $this->assertTrue($cookie->matchesDomain('example.local')); + } + + public function testMatchesPath() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + + // o The cookie-path and the request-path are identical. + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertFalse($cookie->matchesPath('/bar')); + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBar')); + + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + $cookie->setPath('/foo/'); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/fooBaz')); + $this->assertFalse($cookie->matchesPath('/foo')); + + } + + public function cookieValidateProvider() + { + return array( + array('foo', 'baz', 'bar', true), + array('0', '0', '0', true), + array('', 'baz', 'bar', 'The cookie name must not be empty'), + array('foo', '', 'bar', 'The cookie value must not be empty'), + array('foo', 'baz', '', 'The cookie domain must not be empty'), + array('foo\\', 'baz', '0', 'The cookie name must not contain invalid characters: foo\\'), + ); + } + + /** + * @dataProvider cookieValidateProvider + */ + public function testValidatesCookies($name, $value, $domain, $result) + { + $cookie = new Cookie(array( + 'name' => $name, + 'value' => $value, + 'domain' => $domain + )); + $this->assertSame($result, $cookie->validate()); + } + + public function testCreatesInvalidCharacterString() + { + $m = new \ReflectionMethod('Guzzle\Plugin\Cookie\Cookie', 'getInvalidCharacters'); + $m->setAccessible(true); + $p = new \ReflectionProperty('Guzzle\Plugin\Cookie\Cookie', 'invalidCharString'); + $p->setAccessible(true); + $p->setValue(''); + // Expects a string containing 51 invalid characters + $this->assertEquals(51, strlen($m->invoke($m))); + $this->assertContains('@', $m->invoke($m)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php new file mode 100644 index 0000000..2a4b49e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php @@ -0,0 +1,39 @@ +getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + Version::$emitWarnings = true; + } + + public function testAddsDigestAuthentication() + { + Version::$emitWarnings = false; + $plugin = new CurlAuthPlugin('julian', 'test', CURLAUTH_DIGEST); + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('julian', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + $this->assertEquals('julian:test', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + Version::$emitWarnings = true; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php new file mode 100644 index 0000000..6f94186 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php @@ -0,0 +1,137 @@ +flush(); + } + + public function setUp() + { + $mockError = 'Guzzle\Tests\Mock\ErrorResponseMock'; + $description = ServiceDescription::factory(array( + 'operations' => array( + 'works' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => $mockError), + array('code' => 503, 'reason' => 'foo', 'class' => $mockError), + array('code' => 200, 'reason' => 'Error!', 'class' => $mockError) + ) + ), + 'bad_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => 'Does\\Not\\Exist') + ) + ), + 'does_not_implement' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500, 'class' => __CLASS__) + ) + ), + 'no_errors' => array('httpMethod' => 'GET'), + 'no_class' => array( + 'httpMethod' => 'GET', + 'errorResponses' => array( + array('code' => 500) + ) + ), + ) + )); + $this->client = new Client($this->getServer()->getUrl()); + $this->client->setDescription($description); + } + + /** + * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException + */ + public function testSkipsWhenErrorResponsesIsNotSet() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + public function testSkipsWhenErrorResponsesIsNotSetAndAllowsSuccess() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_errors')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage Does\Not\Exist does not exist + */ + public function testEnsuresErrorResponseExists() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('bad_class')->execute(); + } + + /** + * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException + * @expectedExceptionMessage must implement Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface + */ + public function testEnsuresErrorResponseImplementsInterface() + { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('does_not_implement')->execute(); + } + + public function testThrowsSpecificErrorResponseOnMatch() + { + try { + $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $command = $this->client->getCommand('works'); + $command->execute(); + $this->fail('Exception not thrown'); + } catch (ErrorResponseMock $e) { + $this->assertSame($command, $e->command); + $this->assertEquals(500, $e->response->getStatusCode()); + } + } + + /** + * @expectedException \Guzzle\Tests\Mock\ErrorResponseMock + */ + public function testThrowsWhenCodeAndPhraseMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 Error!\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenReasonDoesNotMatch() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('works')->execute(); + } + + public function testSkipsWhenNoClassIsSet() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $this->client->addSubscriber(new ErrorResponsePlugin()); + $this->client->getCommand('no_class')->execute(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php new file mode 100644 index 0000000..41aa673 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php @@ -0,0 +1,140 @@ +get(); + $requests[$i]->setResponse(new Response(200), true); + $requests[$i]->send(); + $h->add($requests[$i]); + } + + return $requests; + } + + public function testDescribesSubscribedEvents() + { + $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents()); + } + + public function testMaintainsLimitValue() + { + $h = new HistoryPlugin(); + $this->assertSame($h, $h->setLimit(10)); + $this->assertEquals(10, $h->getLimit()); + } + + public function testAddsRequests() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 1); + $this->assertEquals(1, count($h)); + $i = $h->getIterator(); + $this->assertEquals(1, count($i)); + $this->assertEquals($requests[0], $i[0]); + } + + /** + * @depends testAddsRequests + */ + public function testMaintainsLimit() + { + $h = new HistoryPlugin(); + $h->setLimit(2); + $requests = $this->addRequests($h, 3); + $this->assertEquals(2, count($h)); + $i = 0; + foreach ($h as $request) { + if ($i > 0) { + $this->assertSame($requests[$i], $request); + } + } + } + + public function testReturnsLastRequest() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests), $h->getLastRequest()); + } + + public function testReturnsLastResponse() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); + } + + public function testClearsHistory() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertEquals(5, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + /** + * @depends testAddsRequests + */ + public function testUpdatesAddRequests() + { + $h = new HistoryPlugin(); + $client = new Client('http://127.0.0.1/'); + $client->getEventDispatcher()->addSubscriber($h); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + + $this->assertSame($request, $h->getLastRequest()); + } + + public function testCanCastToString() + { + $client = new Client('http://127.0.0.1/'); + $h = new HistoryPlugin(); + $client->getEventDispatcher()->addSubscriber($h); + + $mock = new MockPlugin(array( + new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), + new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), + new Response(200, array('Content-Length' => '2'), 'HI') + )); + + $client->getEventDispatcher()->addSubscriber($mock); + $request = $client->get(); + $request->send(); + $this->assertEquals(3, count($h)); + $this->assertEquals(3, count($mock->getReceivedRequests())); + + $h = str_replace("\r", '', $h); + $this->assertContains("> GET / HTTP/1.1\nHost: 127.0.0.1\nUser-Agent:", $h); + $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); + $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); + $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php new file mode 100644 index 0000000..ad663a5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php @@ -0,0 +1,95 @@ +adapter = new ClosureLogAdapter(function ($message) { + echo $message; + }); + } + + public function testIgnoresCurlEventsWhenNotWiringBodies() + { + $p = new LogPlugin($this->adapter); + $this->assertNotEmpty($p->getSubscribedEvents()); + $event = new Event(array('request' => new Request('GET', 'http://foo.com'))); + $p->onCurlRead($event); + $p->onCurlWrite($event); + $p->onRequestBeforeSend($event); + } + + public function testLogsWhenComplete() + { + $output = ''; + $p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) { + $output = $message; + }), '{method} {resource} | {code} {res_body}'); + + $p->onRequestSent(new Event(array( + 'request' => new Request('GET', 'http://foo.com'), + 'response' => new Response(200, array(), 'Foo') + ))); + + $this->assertEquals('GET / | 200 Foo', $output); + } + + public function testWiresBodiesWhenNeeded() + { + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->put(); + + // Send the response from the dummy server as the request body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend"); + $stream = fopen($this->getServer()->getUrl(), 'r'); + $request->setBody(EntityBody::factory($stream, 4)); + + $tmpFile = tempnam(sys_get_temp_dir(), 'non_repeatable'); + $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w'))); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse"); + + ob_start(); + $request->send(); + $message = ob_get_clean(); + + unlink($tmpFile); + $this->assertContains("send", $message); + $this->assertContains("response", $message); + } + + public function testHasHelpfulStaticFactoryMethod() + { + $s = fopen('php://temp', 'r+'); + $client = new Client(); + $client->addSubscriber(LogPlugin::getDebugPlugin(true, $s)); + $request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar'); + $request->setresponse(new Response(200), true); + $request->send(); + rewind($s); + $contents = stream_get_contents($s); + $this->assertContains('# Request:', $contents); + $this->assertContainsIns('PUT / HTTP/1.1', $contents); + $this->assertContains('# Response:', $contents); + $this->assertContainsIns('HTTP/1.1 200 OK', $contents); + $this->assertContains('# Errors:', $contents); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php new file mode 100644 index 0000000..4bd4111 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php @@ -0,0 +1,97 @@ + array( + 'test' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'ContentMD5' => array(), + 'Body' => array( + 'location' => 'body' + ) + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + + return $client; + } + + public function testHasEvents() + { + $this->assertNotEmpty(CommandContentMd5Plugin::getSubscribedEvents()); + } + + public function testValidatesMd5WhenParamExists() + { + $client = $this->getClient(); + $command = $client->getCommand('test', array( + 'Body' => 'Foo', + 'ContentMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertEquals('E1bGfXrRY42Ba/uCLdLCXQ==', (string) $request->getHeader('Content-MD5')); + } + + public function testDoesNothingWhenNoPayloadExists() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test'); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $this->assertNull($request->getHeader('Content-MD5')); + } + + public function testAddsValidationToResponsesOfContentMd5() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => true + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertNotEmpty($listeners); + } + + public function testIgnoresValidationWhenDisabled() + { + $client = $this->getClient(); + $client->getDescription()->getOperation('test')->setHttpMethod('GET'); + $command = $client->getCommand('test', array( + 'ValidateMD5' => false + )); + $event = new Event(array('command' => $command)); + $request = $command->prepare(); + $plugin = new CommandContentMd5Plugin(); + $plugin->onCommandBeforeSend($event); + $listeners = $request->getEventDispatcher()->getListeners('request.complete'); + $this->assertEmpty($listeners); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php new file mode 100644 index 0000000..482e92b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php @@ -0,0 +1,120 @@ +create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $body = 'abc'; + $hash = md5($body); + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Length' => 3 + ), 'abc'); + + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with no Content-MD5 + $response->removeHeader('Content-MD5'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testThrowsExceptionOnInvalidMd5() + { + $plugin = new Md5ValidatorPlugin(); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testSkipsWhenContentLengthIsTooLarge() + { + $plugin = new Md5ValidatorPlugin(false, 1); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + public function testProperlyValidatesWhenUsingContentEncoding() + { + $plugin = new Md5ValidatorPlugin(true); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + // Content-MD5 is the MD5 hash of the canonical content after all + // content-encoding has been applied. Because cURL will automatically + // decompress entity bodies, we need to re-compress it to calculate. + $body = EntityBody::factory('abc'); + $body->compress(); + $hash = $body->getContentMd5(); + $body->uncompress(); + + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'gzip' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + $this->assertEquals('abc', $response->getBody(true)); + + // Try again with an unknown encoding + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'foobar' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with compress + $body->compress('bzip2.compress'); + $response = new Response(200, array( + 'Content-MD5' => $body->getContentMd5(), + 'Content-Encoding' => 'compress' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with encoding and disabled content-encoding checks + $request->getEventDispatcher()->removeSubscriber($plugin); + $plugin = new Md5ValidatorPlugin(false); + $request->getEventDispatcher()->addSubscriber($plugin); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php new file mode 100644 index 0000000..3af8fef --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php @@ -0,0 +1,199 @@ +assertInternalType('array', MockPlugin::getSubscribedEvents()); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', MockPlugin::getAllEvents()); + } + + public function testCanBeTemporary() + { + $plugin = new MockPlugin(); + $this->assertFalse($plugin->isTemporary()); + $plugin = new MockPlugin(null, true); + $this->assertTrue($plugin->isTemporary()); + } + + public function testIsCountable() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + /** + * @depends testIsCountable + */ + public function testCanClearQueue() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + public function testCanInspectQueue() + { + $plugin = new MockPlugin(); + $this->assertInternalType('array', $plugin->getQueue()); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $queue = $plugin->getQueue(); + $this->assertInternalType('array', $queue); + $this->assertEquals(1, count($queue)); + } + + public function testRetrievesResponsesFromFiles() + { + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenResponseFileIsNotFound() + { + MockPlugin::getMockFile('missing/filename'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidResponsesThrowAnException() + { + $p = new MockPlugin(); + $p->addResponse($this); + } + + public function testAddsResponseObjectsToQueue() + { + $p = new MockPlugin(); + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $p->addResponse($response); + $this->assertEquals(array($response), $p->getQueue()); + } + + public function testAddsResponseFilesToQueue() + { + $p = new MockPlugin(); + $p->addResponse(__DIR__ . '/../../TestData/mock_response'); + $this->assertEquals(1, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + */ + public function testAddsMockResponseToRequestFromClient() + { + $p = new MockPlugin(); + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $p->addResponse($response); + + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertSame($response, $request->getResponse()); + $this->assertEquals(0, count($p)); + } + + /** + * @depends testAddsResponseFilesToQueue + * @expectedException \OutOfBoundsException + */ + public function testUpdateThrowsExceptionWhenEmpty() + { + $p = new MockPlugin(); + $p->onRequestBeforeSend(new Event()); + } + + /** + * @depends testAddsMockResponseToRequestFromClient + */ + public function testDetachesTemporaryWhenEmpty() + { + $p = new MockPlugin(null, true); + $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertFalse($this->hasSubscriber($client, $p)); + } + + public function testLoadsResponsesFromConstructor() + { + $p = new MockPlugin(array(new Response(200))); + $this->assertEquals(1, $p->count()); + } + + public function testStoresMockedRequests() + { + $p = new MockPlugin(array(new Response(200), new Response(200))); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $request1 = $client->get(); + $request1->send(); + $this->assertEquals(array($request1), $p->getReceivedRequests()); + + $request2 = $client->get(); + $request2->send(); + $this->assertEquals(array($request1, $request2), $p->getReceivedRequests()); + + $p->flush(); + $this->assertEquals(array(), $p->getReceivedRequests()); + } + + public function testReadsBodiesFromMockedRequests() + { + $p = new MockPlugin(array(new Response(200))); + $p->readBodies(true); + $client = new Client('http://127.0.0.1:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $body = EntityBody::factory('foo'); + $request = $client->put(); + $request->setBody($body); + $request->send(); + $this->assertEquals(3, $body->ftell()); + } + + public function testCanMockBadRequestExceptions() + { + $client = new Client('http://127.0.0.1:123/'); + $ex = new CurlException('Foo'); + $mock = new MockPlugin(array($ex)); + $client->addSubscriber($mock); + $request = $client->get('foo'); + + try { + $request->send(); + $this->fail('Did not dequeue an exception'); + } catch (CurlException $e) { + $this->assertSame($e, $ex); + $this->assertSame($request, $ex->getRequest()); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php new file mode 100644 index 0000000..3892fb6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php @@ -0,0 +1,345 @@ + 'foo', + 'consumer_secret' => 'bar', + 'token' => 'count', + 'token_secret' => 'dracula' + ); + + protected function getRequest() + { + return RequestFactory::getInstance()->create('POST', 'http://www.test.com/path?a=b&c=d', null, array( + 'e' => 'f' + )); + } + + public function testSubscribesToEvents() + { + $events = OauthPlugin::getSubscribedEvents(); + $this->assertArrayHasKey('request.before_send', $events); + } + + public function testAcceptsConfigurationData() + { + $p = new OauthPlugin($this->config); + + // Access the config object + $class = new \ReflectionClass($p); + $property = $class->getProperty('config'); + $property->setAccessible(true); + $config = $property->getValue($p); + + $this->assertEquals('foo', $config['consumer_key']); + $this->assertEquals('bar', $config['consumer_secret']); + $this->assertEquals('count', $config['token']); + $this->assertEquals('dracula', $config['token_secret']); + $this->assertEquals('1.0', $config['version']); + $this->assertEquals('HMAC-SHA1', $config['signature_method']); + $this->assertEquals('header', $config['request_method']); + } + + public function testCreatesStringToSignFromPostRequest() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + public function testCreatesStringToSignIgnoringPostFields() + { + $config = $this->config; + $config['disable_post_params'] = true; + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $sts = rawurldecode($p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + $this->assertNotContains('&e=f', $sts); + } + + public function testCreatesStringToSignFromPostRequestWithCustomContentType() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->setHeader('Content-Type', 'Foo'); + $this->assertEquals( + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', + $p->getStringToSign($request, self::TIMESTAMP, self::NONCE) + ); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testConvertsBooleansToStrings() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', true); + $request->getQuery()->set('c', false); + $this->assertContains('&a%3Dtrue%26c%3Dfalse', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + public function testCreatesStringToSignFromPostRequestWithNullValues() + { + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + 'token' => null, + 'token_secret' => 'dracula' + ); + + $p = new OauthPlugin($config); + $request = $this->getRequest(); + $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); + + $this->assertContains('&e=f', rawurldecode($signString)); + + $expectedSignString = // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_version%3D1.0'; + + $this->assertEquals($expectedSignString, $signString); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testMultiDimensionalArray() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', array('b' => array('e' => 'f', 'c' => 'd'))); + $this->assertContains('a%255Bb%255D%255Bc%255D%3Dd%26a%255Bb%255D%255Be%255D%3Df%26c%3Dd%26e%3Df%26', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testMultiDimensionalArray + */ + public function testMultiDimensionalArrayWithNonDefaultQueryAggregator() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $aggregator = new CommaAggregator(); + $query = $request->getQuery()->setAggregator($aggregator) + ->set('g', array('h', 'i', 'j')) + ->set('k', array('l')) + ->set('m', array('n', 'o')); + $this->assertContains('a%3Db%26c%3Dd%26e%3Df%26g%3Dh%2Ci%2Cj%26k%3Dl%26m%3Dn%2Co', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); + } + + /** + * @depends testCreatesStringToSignFromPostRequest + */ + public function testSignsStrings() + { + $p = new OauthPlugin(array_merge($this->config, array( + 'signature_callback' => function($string, $key) { + return "_{$string}|{$key}_"; + } + ))); + $request = $this->getRequest(); + $sig = $p->getSignature($request, self::TIMESTAMP, self::NONCE); + $this->assertEquals( + '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D'. self::NONCE .'%26oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' . + 'bar&dracula_', + base64_decode($sig) + ); + } + + /** + * Test that the Oauth is signed correctly and that extra strings haven't been added + * to the authorization header. + */ + public function testSignsOauthRequests() + { + $p = new OauthPlugin($this->config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertTrue($event['request']->hasHeader('Authorization')); + + $authorizationHeader = (string)$event['request']->getHeader('Authorization'); + + $this->assertStringStartsWith('OAuth ', $authorizationHeader); + + $stringsToCheck = array( + 'oauth_consumer_key="foo"', + 'oauth_nonce="'.urlencode($params['oauth_nonce']).'"', + 'oauth_signature="'.urlencode($params['oauth_signature']).'"', + 'oauth_signature_method="HMAC-SHA1"', + 'oauth_timestamp="' . self::TIMESTAMP . '"', + 'oauth_token="count"', + 'oauth_version="1.0"', + ); + + $totalLength = strlen('OAuth '); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $authorizationHeader); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = ', '; + } + + // Technically this test is not universally valid. It would be allowable to have extra \n characters + // in the Authorization header. However Guzzle does not do this, so we just perform a simple check + // on length to validate the Authorization header is composed of only the strings above. + $this->assertEquals($totalLength, strlen($authorizationHeader), 'Authorization has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + public function testSignsOauthQueryStringRequest() + { + $config = array_merge( + $this->config, + array('request_method' => OauthPlugin::REQUEST_METHOD_QUERY) + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $params = $p->onRequestBeforeSend($event); + + $this->assertFalse($event['request']->hasHeader('Authorization')); + + $stringsToCheck = array( + 'a=b', + 'c=d', + 'oauth_consumer_key=foo', + 'oauth_nonce='.urlencode($params['oauth_nonce']), + 'oauth_signature='.urlencode($params['oauth_signature']), + 'oauth_signature_method=HMAC-SHA1', + 'oauth_timestamp='.self::TIMESTAMP, + 'oauth_token=count', + 'oauth_version=1.0', + ); + + $queryString = (string) $event['request']->getQuery(); + + $totalLength = strlen('?'); + + //Separator is not used before first parameter. + $separator = ''; + + foreach ($stringsToCheck as $stringToCheck) { + $this->assertContains($stringToCheck, $queryString); + $totalLength += strlen($separator); + $totalLength += strlen($stringToCheck); + $separator = '&'; + } + + // Removes the last query string separator '&' + $totalLength -= 1; + + $this->assertEquals($totalLength, strlen($queryString), 'Query string has extra characters i.e. contains extra elements compared to stringsToCheck.'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidArgumentExceptionOnMethodError() + { + $config = array_merge( + $this->config, + array('request_method' => 'FakeMethod') + ); + + $p = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $p->onRequestBeforeSend($event); + } + + public function testDoesNotAddFalseyValuesToAuthorization() + { + unset($this->config['token']); + $p = new OauthPlugin($this->config); + $event = new Event(array('request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP)); + $p->onRequestBeforeSend($event); + $this->assertTrue($event['request']->hasHeader('Authorization')); + $this->assertNotContains('oauth_token=', (string) $event['request']->getHeader('Authorization')); + } + + public function testOptionalOauthParametersAreNotAutomaticallyAdded() + { + // The only required Oauth parameters are the consumer key and secret. That is enough credentials + // for signing oauth requests. + $config = array( + 'consumer_key' => 'foo', + 'consumer_secret' => 'bar', + ); + + $plugin = new OauthPlugin($config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + + $timestamp = $plugin->getTimestamp($event); + $request = $event['request']; + $nonce = $plugin->generateNonce($request); + + $paramsToSign = $plugin->getParamsToSign($request, $timestamp, $nonce); + + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'token_secret' => 'token_secret' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + $this->assertArrayNotHasKey($oauthName, $paramsToSign, "Optional Oauth param '$oauthName' was not set via config variable '$optionName', but it is listed in getParamsToSign()."); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php new file mode 100644 index 0000000..8b42fb8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php @@ -0,0 +1,149 @@ +loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader') + ->setMethods(array('build')) + ->getMockForAbstractClass(); + } + + public function tearDown() + { + foreach ($this->cleanup as $file) { + unlink($file); + } + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnlyLoadsSupportedTypes() + { + $this->loader->load(new \stdClass()); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open fooooooo.json + */ + public function testFileMustBeReadable() + { + $this->loader->load('fooooooo.json'); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unknown file extension + */ + public function testMustBeSupportedExtension() + { + $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt'); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + * @expectedExceptionMessage Error loading JSON data from + */ + public function testJsonMustBeValue() + { + $filename = tempnam(sys_get_temp_dir(), 'json') . '.json'; + file_put_contents($filename, '{/{./{}foo'); + $this->cleanup[] = $filename; + $this->loader->load($filename); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage PHP files must return an array + */ + public function testPhpFilesMustReturnAnArray() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, 'cleanup[] = $filename; + $this->loader->load($filename); + } + + public function testLoadsPhpFileIncludes() + { + $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; + file_put_contents($filename, ' "bar");'); + $this->cleanup[] = $filename; + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $config = $this->loader->load($filename); + $this->assertEquals(array('foo' => 'bar'), $config); + } + + public function testCanCreateFromJson() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($file); + // Ensure that the config files were merged using the includes directives + $this->assertArrayHasKey('includes', $data); + $this->assertArrayHasKey('services', $data); + $this->assertInternalType('array', $data['services']['foo']); + $this->assertInternalType('array', $data['services']['abstract']); + $this->assertInternalType('array', $data['services']['mock']); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testUsesAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo', $file); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load('foo'); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage Unable to open foo.json + */ + public function testCanRemoveAliases() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $this->loader->addAlias('foo.json', $file); + $this->loader->removeAlias('foo.json'); + $this->loader->load('foo.json'); + } + + public function testCanLoadArraysWithIncludes() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $config = array('includes' => array($file)); + // The build method will just return the config data + $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); + $data = $this->loader->load($config); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } + + public function testDoesNotEnterInfiniteLoop() + { + $prefix = $file = dirname(__DIR__) . '/TestData/description'; + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that the internal list of loaded files is reset + $this->loader->load("{$prefix}/../test_service2.json"); + $this->assertCount(1, $this->readAttribute($this->loader, 'loadedFiles')); + // Ensure that previously loaded files will be reloaded when starting fresh + $this->loader->load("{$prefix}/baz.json"); + $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php new file mode 100644 index 0000000..f63070e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php @@ -0,0 +1,177 @@ + array( + 'abstract' => array( + 'params' => array( + 'access_key' => 'xyz', + 'secret' => 'abc', + ), + ), + 'foo' => array( + 'extends' => 'abstract', + 'params' => array( + 'baz' => 'bar', + ), + ), + 'mock' => array( + 'extends' => 'abstract', + 'params' => array( + 'username' => 'foo', + 'password' => 'baz', + 'subdomain' => 'bar', + ) + ) + ) + ); + + $builder = $arrayFactory->load($data); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract + */ + public function testThrowsExceptionWhenExtendingNonExistentService() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'extends' => 'abstract' + ) + ) + ); + + $builder = $arrayFactory->load($data); + } + + public function testAllowsGlobalParameterOverrides() + { + $arrayFactory = new ServiceBuilderLoader(); + + $data = array( + 'services' => array( + 'foo' => array( + 'params' => array( + 'foo' => 'baz', + 'bar' => 'boo' + ) + ) + ) + ); + + $builder = $arrayFactory->load($data, array( + 'bar' => 'jar', + 'far' => 'car' + )); + + $compiled = json_decode($builder->serialize(), true); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'jar', + 'far' => 'car' + ), $compiled['foo']['params']); + } + + public function tstDoesNotErrorOnCircularReferences() + { + $arrayFactory = new ServiceBuilderLoader(); + $arrayFactory->load(array( + 'services' => array( + 'too' => array('extends' => 'ball'), + 'ball' => array('extends' => 'too'), + ) + )); + } + + public function configProvider() + { + $foo = array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '456') + ); + + return array( + array( + // Does not extend the existing `foo` service but overwrites it + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz') + ) + ), + array( + 'services' => array( + 'foo' => array('class' => 'Baz'), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ), + array( + // Extends the existing `foo` service + array( + 'services' => array( + 'foo' => $foo, + 'bar' => array('params' => array('baz' => '123')) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'foo', + 'params' => array('b' => '123', 'c' => 'def') + ) + ) + ), + array( + 'services' => array( + 'foo' => array( + 'extends' => 'bar', + 'class' => 'stdClass', + 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') + ), + 'bar' => array('params' => array('baz' => '123')) + ) + ) + ) + ); + } + + /** + * @dataProvider configProvider + */ + public function testCombinesConfigs($a, $b, $c) + { + $l = new ServiceBuilderLoader(); + $m = new \ReflectionMethod($l, 'mergeData'); + $m->setAccessible(true); + $this->assertEquals($c, $m->invoke($l, $a, $b)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php new file mode 100644 index 0000000..e1b3a1d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php @@ -0,0 +1,317 @@ + array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'michael', + 'password' => 'testing123', + 'subdomain' => 'michael', + ), + ), + 'billy.mock' => array( + 'alias' => 'Hello!', + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ), + ), + 'billy.testing' => array( + 'extends' => 'billy.mock', + 'params' => array( + 'subdomain' => 'test.billy', + ), + ), + 'missing_params' => array( + 'extends' => 'billy.mock' + ) + ); + + public function testAllowsSerialization() + { + $builder = ServiceBuilder::factory($this->arrayData); + $cached = unserialize(serialize($builder)); + $this->assertEquals($cached, $builder); + } + + public function testDelegatesFactoryMethodToAbstractFactory() + { + $builder = ServiceBuilder::factory($this->arrayData); + $c = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage No service is registered as foobar + */ + public function testThrowsExceptionWhenGettingInvalidClient() + { + ServiceBuilder::factory($this->arrayData)->get('foobar'); + } + + public function testStoresClientCopy() + { + $builder = ServiceBuilder::factory($this->arrayData); + $client = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl()); + $this->assertEquals($client, $builder->get('michael.mock')); + + // Get another client but throw this one away + $client2 = $builder->get('billy.mock', true); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client2); + $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl()); + + // Make sure the original client is still there and set + $this->assertTrue($client === $builder->get('michael.mock')); + + // Create a new billy.mock client that is stored + $client3 = $builder->get('billy.mock'); + + // Make sure that the stored billy.mock client is equal to the other stored client + $this->assertTrue($client3 === $builder->get('billy.mock')); + + // Make sure that this client is not equal to the previous throwaway client + $this->assertFalse($client2 === $builder->get('billy.mock')); + } + + public function testBuildersPassOptionsThroughToClients() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); + } + + public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); + } + + public function testUsedAsArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $this->assertTrue($b->offsetExists('michael.mock')); + $this->assertFalse($b->offsetExists('not_there')); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + + unset($b['michael.mock']); + $this->assertFalse($b->offsetExists('michael.mock')); + + $b['michael.mock'] = new Client('http://www.test.com/'); + $this->assertInstanceOf('Guzzle\Service\Client', $b['michael.mock']); + } + + public function testFactoryCanCreateFromJson() + { + $tmp = sys_get_temp_dir() . '/test.js'; + file_put_contents($tmp, json_encode($this->arrayData)); + $b = ServiceBuilder::factory($tmp); + unlink($tmp); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryCanCreateFromArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testFactoryDoesNotRequireParams() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('missing_params'); + $this->assertEquals('billy', $s->getConfig('username')); + } + + public function testBuilderAllowsReferencesBetweenClients() + { + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'other_client' => '{b}', + 'username' => 'x', + 'password' => 'y', + 'subdomain' => 'z' + ) + ), + 'b' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => '1', + 'password' => '2', + 'subdomain' => '3' + ) + ) + )); + + $client = $builder['a']; + $this->assertEquals('x', $client->getConfig('username')); + $this->assertSame($builder['b'], $client->getConfig('other_client')); + $this->assertEquals('1', $builder['b']->getConfig('username')); + } + + public function testEmitsEventsWhenClientsAreCreated() + { + // Ensure that the client signals that it emits an event + $this->assertEquals(array('service_builder.create_client'), ServiceBuilder::getAllEvents()); + + // Create a test service builder + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'test', + 'password' => '123', + 'subdomain' => 'z' + ) + ) + )); + + // Add an event listener to pick up client creation events + $emits = 0; + $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits) { + $emits++; + }); + + // Get the 'a' client by name + $client = $builder->get('a'); + + // Ensure that the event was emitted once, and that the client was present + $this->assertEquals(1, $emits); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + } + + public function testCanAddGlobalParametersToServicesOnLoad() + { + $builder = ServiceBuilder::factory($this->arrayData, array( + 'username' => 'fred', + 'new_value' => 'test' + )); + + $data = json_decode($builder->serialize(), true); + + foreach ($data as $service) { + $this->assertEquals('fred', $service['params']['username']); + $this->assertEquals('test', $service['params']['new_value']); + } + } + + public function testAddsGlobalPlugins() + { + $b = new ServiceBuilder($this->arrayData); + $b->addGlobalPlugin(new HistoryPlugin()); + $s = $b->get('michael.mock'); + $this->assertTrue($s->getEventDispatcher()->hasListeners('request.sent')); + } + + public function testCanGetData() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertEquals($this->arrayData['michael.mock'], $b->getData('michael.mock')); + $this->assertNull($b->getData('ewofweoweofe')); + } + + public function testCanGetByAlias() + { + $b = new ServiceBuilder($this->arrayData); + $this->assertSame($b->get('billy.mock'), $b->get('Hello!')); + } + + public function testCanOverwriteParametersForThrowawayClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock'); + $this->assertEquals('michael', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c2->getConfig('username')); + } + + public function testGettingAThrowawayClientWithParametersDoesNotAffectGettingOtherClients() + { + $b = new ServiceBuilder($this->arrayData); + + $c1 = $b->get('michael.mock', array('username' => 'jeremy')); + $this->assertEquals('jeremy', $c1->getConfig('username')); + + $c2 = $b->get('michael.mock'); + $this->assertEquals('michael', $c2->getConfig('username')); + } + + public function testCanUseArbitraryData() + { + $b = new ServiceBuilder(); + $b['a'] = 'foo'; + $this->assertTrue(isset($b['a'])); + $this->assertEquals('foo', $b['a']); + unset($b['a']); + $this->assertFalse(isset($b['a'])); + } + + public function testCanRegisterServiceData() + { + $b = new ServiceBuilder(); + $b['a'] = array( + 'class' => 'Guzzle\Tests\Service\Mock\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ) + ); + $this->assertTrue(isset($b['a'])); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $b['a']); + $client = $b['a']; + unset($b['a']); + $this->assertFalse(isset($b['a'])); + // Ensure that instantiated clients can be registered + $b['mock'] = $client; + $this->assertSame($client, $b['mock']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php new file mode 100644 index 0000000..b8245ad --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php @@ -0,0 +1,43 @@ +getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load('foo')); + $this->assertEquals($data, $cache->load('foo')); + } + + public function testDoesNotCacheArrays() + { + $cache = new DoctrineCacheAdapter(new ArrayCache()); + $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') + ->setMethods(array('load')) + ->getMockForAbstractClass(); + $data = array('foo' => 'bar'); + $loader->expects($this->exactly(2)) + ->method('load') + ->will($this->returnValue($data)); + $cache = new CachingConfigLoader($loader, $cache); + $this->assertEquals($data, $cache->load(array())); + $this->assertEquals($data, $cache->load(array())); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php new file mode 100644 index 0000000..aee29ed --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php @@ -0,0 +1,320 @@ +serviceTest = new ServiceDescription(array( + 'test_command' => new Operation(array( + 'doc' => 'documentationForCommand', + 'method' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'args' => array( + 'bucket' => array( + 'required' => true + ), + 'key' => array( + 'required' => true + ) + ) + )) + )); + + $this->service = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + } + + public function testAllowsCustomClientParameters() + { + $client = new Mock\MockClient(null, array( + Client::COMMAND_PARAMS => array(AbstractCommand::RESPONSE_PROCESSING => 'foo') + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('foo', $command->get(AbstractCommand::RESPONSE_PROCESSING)); + } + + public function testFactoryCreatesClient() + { + $client = Client::factory(array( + 'base_url' => 'http://www.test.com/', + 'test' => '123' + )); + + $this->assertEquals('http://www.test.com/', $client->getBaseUrl()); + $this->assertEquals('123', $client->getConfig('test')); + } + + public function testFactoryDoesNotRequireBaseUrl() + { + $client = Client::factory(); + } + + public function testDescribesEvents() + { + $this->assertInternalType('array', Client::getAllEvents()); + } + + public function testExecutesCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $cmd = new MockCommand(); + $client->execute($cmd); + + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult()); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + public function testExecutesCommandsWithArray() + { + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200), + new Response(200) + ))); + + // Create a command set and a command + $set = array(new MockCommand(), new MockCommand()); + $client->execute($set); + + // Make sure it sent + $this->assertTrue($set[0]->isExecuted()); + $this->assertTrue($set[1]->isExecuted()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidCommandIsExecuted() + { + $client = new Client(); + $client->execute(new \stdClass()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenMissingCommand() + { + $client = new Client(); + + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('test')) + ->will($this->returnValue(null)); + + $client->setCommandFactory($mock); + $client->getCommand('test'); + } + + public function testCreatesCommandsUsingCommandFactory() + { + $mockCommand = new MockCommand(); + + $client = new Mock\MockClient(); + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand)); + + $client->setCommandFactory($mock); + + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $command = $client->getCommand('foo', array('acl' => '123')); + $this->assertSame($mockCommand, $command); + $this->assertSame($client, $command->getClient()); + } + + public function testOwnsServiceDescription() + { + $client = new Mock\MockClient(); + $this->assertNull($client->getDescription()); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $this->assertSame($client, $client->setDescription($description)); + $this->assertSame($description, $client->getDescription()); + } + + public function testOwnsResourceIteratorFactory() + { + $client = new Mock\MockClient(); + + $method = new \ReflectionMethod($client, 'getResourceIteratorFactory'); + $method->setAccessible(TRUE); + $rf1 = $method->invoke($client); + + $rf = $this->readAttribute($client, 'resourceIteratorFactory'); + $this->assertInstanceOf('Guzzle\\Service\\Resource\\ResourceIteratorClassFactory', $rf); + $this->assertSame($rf1, $rf); + + $rf = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock'); + $client->setResourceIteratorFactory($rf); + $this->assertNotSame($rf1, $rf); + } + + public function testClientResetsRequestsBeforeExecutingCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHi", + "HTTP/1.1 200 OK\r\nContent-Length: 1\r\n\r\nI" + )); + + $client = new Mock\MockClient($this->getServer()->getUrl()); + + $command = $client->getCommand('mock_command'); + $client->execute($command); + $client->execute($command); + $this->assertEquals('I', $command->getResponse()->getBody(true)); + } + + public function testClientCreatesIterators() + { + $client = new Mock\MockClient(); + + $iterator = $client->getIterator('mock_command', array( + 'foo' => 'bar' + ), array( + 'limit' => 10 + )); + + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $this->assertEquals(10, $this->readAttribute($iterator, 'limit')); + + $command = $this->readAttribute($iterator, 'originalCommand'); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testClientCreatesIteratorsWithNoOptions() + { + $client = new Mock\MockClient(); + $iterator = $client->getIterator('mock_command'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testClientCreatesIteratorsWithCommands() + { + $client = new Mock\MockClient(); + $command = new MockCommand(); + $iterator = $client->getIterator($command); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $iteratorCommand = $this->readAttribute($iterator, 'originalCommand'); + $this->assertSame($command, $iteratorCommand); + } + + public function testClientHoldsInflector() + { + $client = new Mock\MockClient(); + $this->assertInstanceOf('Guzzle\Inflection\MemoizingInflector', $client->getInflector()); + + $inflector = new Inflector(); + $client->setInflector($inflector); + $this->assertSame($inflector, $client->getInflector()); + } + + public function testClientAddsGlobalCommandOptions() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar' + ) + )); + $command = $client->getCommand('mock_command'); + $this->assertEquals('bar', $command->get('mesa')); + } + + public function testSupportsServiceDescriptionBaseUrls() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $client = new Client(); + $client->setDescription($description); + $this->assertEquals('http://foo.com', $client->getBaseUrl()); + } + + public function testMergesDefaultCommandParamsCorrectly() + { + $client = new Mock\MockClient('http://www.foo.com', array( + Client::COMMAND_PARAMS => array( + 'mesa' => 'bar', + 'jar' => 'jar' + ) + )); + $command = $client->getCommand('mock_command', array('jar' => 'test')); + $this->assertEquals('bar', $command->get('mesa')); + $this->assertEquals('test', $command->get('jar')); + } + + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ + public function testWrapsSingleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(401))); + $client->addSubscriber($mock); + $client->execute(new MockCommand()); + } + + public function testWrapsMultipleCommandExceptions() + { + $client = new Mock\MockClient('http://foobaz.com'); + $mock = new MockPlugin(array(new Response(200), new Response(200), new Response(404), new Response(500))); + $client->addSubscriber($mock); + + $cmds = array(new MockCommand(), new MockCommand(), new MockCommand(), new MockCommand()); + try { + $client->execute($cmds); + } catch (CommandTransferException $e) { + $this->assertEquals(2, count($e->getFailedRequests())); + $this->assertEquals(2, count($e->getSuccessfulRequests())); + $this->assertEquals(2, count($e->getFailedCommands())); + $this->assertEquals(2, count($e->getSuccessfulCommands())); + + foreach ($e->getSuccessfulCommands() as $c) { + $this->assertTrue($c->getResponse()->isSuccessful()); + } + + foreach ($e->getFailedCommands() as $c) { + $this->assertFalse($c->getRequest()->getResponse()->isSuccessful()); + } + } + } + + public function testGetCommandAfterTwoSetDescriptions() + { + $service1 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); + $service2 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service_3.json'); + + $client = new Mock\MockClient(); + + $client->setDescription($service1); + $client->getCommand('foo_bar'); + $client->setDescription($service2); + $client->getCommand('baz_qux'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php new file mode 100644 index 0000000..1004fae --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php @@ -0,0 +1,16 @@ +setDescription(ServiceDescription::factory(__DIR__ . '/../../TestData/test_service.json')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php new file mode 100644 index 0000000..d762246 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php @@ -0,0 +1,54 @@ + function($command, $api) { + $command->set('testing', '123'); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + return $request; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + $this->assertEquals('123', $c->get('testing')); + $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl()); + } + + /** + * @expectedException UnexpectedValueException + * @expectedExceptionMessage Closure command did not return a RequestInterface object + */ + public function testMustReturnRequest() + { + $c = new ClosureCommand(array( + 'closure' => function($command, $api) { + return false; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php new file mode 100644 index 0000000..b7173d4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php @@ -0,0 +1,445 @@ +assertEquals('123', $command->get('test')); + $this->assertFalse($command->isPrepared()); + $this->assertFalse($command->isExecuted()); + } + + public function testDeterminesShortName() + { + $api = new Operation(array('name' => 'foobar')); + $command = new MockCommand(array(), $api); + $this->assertEquals('foobar', $command->getName()); + + $command = new MockCommand(); + $this->assertEquals('mock_command', $command->getName()); + + $command = new Sub(); + $this->assertEquals('sub.sub', $command->getName()); + } + + /** + * @expectedException RuntimeException + */ + public function testGetRequestThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getRequest(); + } + + public function testGetResponseExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResponse()); + $this->assertSame($response, $command->getResponse()); + } + + public function testGetResultExecutesCommandsWhenNeeded() + { + $response = new Response(200); + $client = $this->getClient(); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + $this->assertSame($response, $command->getResult()); + $this->assertSame($response, $command->getResult()); + } + + public function testSetClient() + { + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client); + $this->assertEquals($client, $command->getClient()); + + unset($client); + unset($command); + + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client)->prepare(); + $this->assertEquals($client, $command->getClient()); + $this->assertTrue($command->isPrepared()); + } + + public function testExecute() + { + $client = $this->getClient(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), '123'); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $this->assertSame($command, $command->setClient($client)); + + // Returns the result of the command + $this->assertInstanceOf('SimpleXMLElement', $command->execute()); + + $this->assertTrue($command->isPrepared()); + $this->assertTrue($command->isExecuted()); + $this->assertSame($response, $command->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest()); + // Make sure that the result was automatically set to a SimpleXMLElement + $this->assertInstanceOf('SimpleXMLElement', $command->getResult()); + $this->assertEquals('123', (string) $command->getResult()->data); + } + + public function testConvertsJsonResponsesToArray() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), '{ "key": "Hi!" }' + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + $this->assertEquals(array( + 'key' => 'Hi!' + ), $command->getResult()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testConvertsInvalidJsonResponsesToArray() + { + $json = '{ "key": "Hi!" }invalid'; + // Some implementations of php-json extension are not strict enough + // and allow to parse invalid json ignoring invalid parts + // See https://github.com/remicollet/pecl-json-c/issues/5 + if (json_decode($json) && JSON_ERROR_NONE === json_last_error()) { + $this->markTestSkipped('php-pecl-json library regression issues'); + } + + $client = $this->getClient(); + $this->setMockResponse($client, array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), $json + ) + )); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + } + + public function testProcessResponseIsNotXml() + { + $client = $this->getClient(); + $this->setMockResponse($client, array( + new Response(200, array( + 'Content-Type' => 'application/octet-stream' + ), 'abc,def,ghi') + )); + $command = new MockCommand(); + $client->execute($command); + + // Make sure that the result was not converted to XML + $this->assertFalse($command->getResult() instanceof \SimpleXMLElement); + } + + /** + * @expectedException RuntimeException + */ + public function testExecuteThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->execute(); + } + + /** + * @expectedException RuntimeException + */ + public function testPrepareThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->prepare(); + } + + public function testCommandsAllowsCustomRequestHeaders() + { + $command = new MockCommand(); + $command->getRequestHeaders()->set('test', '123'); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('123', $command->getRequestHeaders()->get('test')); + + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', (string) $command->getRequest()->getHeader('test')); + } + + public function testCommandsAllowsCustomRequestHeadersAsArray() + { + $command = new MockCommand(array(AbstractCommand::HEADERS_OPTION => array('Foo' => 'Bar'))); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo')); + } + + private function getOperation() + { + return new Operation(array( + 'name' => 'foobar', + 'httpMethod' => 'POST', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'type' => 'string' + ) + ))); + } + + public function testCommandsUsesOperation() + { + $api = $this->getOperation(); + $command = new MockCommand(array(), $api); + $this->assertSame($api, $command->getOperation()); + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', $command->get('test')); + $this->assertSame($api, $command->getOperation($api)); + } + + public function testCloneMakesNewRequest() + { + $client = $this->getClient(); + $command = new MockCommand(array(), $this->getOperation()); + $command->setClient($client); + + $command->prepare(); + $this->assertTrue($command->isPrepared()); + + $command2 = clone $command; + $this->assertFalse($command2->isPrepared()); + } + + public function testHasOnCompleteMethod() + { + $that = $this; + $called = 0; + + $testFunction = function($command) use (&$called, $that) { + $called++; + $that->assertInstanceOf('Guzzle\Service\Command\CommandInterface', $command); + }; + + $client = $this->getClient(); + $command = new MockCommand(array( + 'command.on_complete' => $testFunction + ), $this->getOperation()); + $command->setClient($client); + + $command->prepare()->setResponse(new Response(200), true); + $command->execute(); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnCompleteMustBeCallable() + { + $client = $this->getClient(); + $command = new MockCommand(); + $command->setOnComplete('foo'); + } + + public function testCanSetResultManually() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200) + ))); + $command = new MockCommand(); + $client->execute($command); + $command->setResult('foo!'); + $this->assertEquals('foo!', $command->getResult()); + } + + public function testCanInitConfig() + { + $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand') + ->setConstructorArgs(array(array( + 'foo' => 'bar' + ), new Operation(array( + 'parameters' => array( + 'baz' => new Parameter(array( + 'default' => 'baaar' + )) + ) + )))) + ->getMockForAbstractClass(); + + $this->assertEquals('bar', $command['foo']); + $this->assertEquals('baaar', $command['baz']); + } + + public function testAddsCurlOptionsToRequestsWhenPreparing() + { + $command = new MockCommand(array( + 'foo' => 'bar', + 'curl.options' => array('CURLOPT_PROXYPORT' => 8080) + )); + $client = new Client(); + $command->setClient($client); + $request = $command->prepare(); + $this->assertEquals(8080, $request->getCurlOptions()->get(CURLOPT_PROXYPORT)); + } + + public function testIsInvokable() + { + $client = $this->getClient(); + $response = new Response(200); + $this->setMockResponse($client, array($response)); + $command = new MockCommand(); + $command->setClient($client); + // Returns the result of the command + $this->assertSame($response, $command()); + } + + public function testCreatesDefaultOperation() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $this->assertInstanceOf('Guzzle\Service\Description\Operation', $command->getOperation()); + } + + public function testAllowsValidatorToBeInjected() + { + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); + $v = new SchemaValidator(); + $command->setValidator($v); + $this->assertSame($v, $this->readAttribute($command, 'validator')); + } + + public function testCanDisableValidation() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate')) + ->getMock(); + $v->expects($this->never())->method('validate'); + $command->setValidator($v); + $command->set(AbstractCommand::DISABLE_VALIDATION, true); + $command->prepare(); + } + + public function testValidatorDoesNotUpdateNonDefaultValues() + { + $command = new MockCommand(array('test' => 123, 'foo' => 'bar')); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('bar', $command->get('foo')); + } + + public function testValidatorUpdatesDefaultValues() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->prepare(); + $this->assertEquals(123, $command->get('test')); + $this->assertEquals('abc', $command->get('_internal')); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage [Foo] Baz + */ + public function testValidatesCommandBeforeSending() + { + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('validate')->will($this->returnValue(false)); + $v->expects($this->any())->method('getErrors')->will($this->returnValue(array('[Foo] Baz', '[Bar] Boo'))); + $command->setValidator($v); + $command->prepare(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage Validation errors: [abc] must be of type string + */ + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'parameters' => array( + 'baz' => array('type' => 'integer') + ), + 'additionalParameters' => array( + 'type' => 'string' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo', array( + 'abc' => false, + 'command.headers' => array('foo' => 'bar') + )); + $command->prepare(); + } + + public function testCanAccessValidationErrorsFromCommand() + { + $validationErrors = array('[Foo] Baz', '[Bar] Boo'); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + + $this->assertFalse($command->getValidationErrors()); + + $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') + ->setMethods(array('validate', 'getErrors')) + ->getMock(); + $v->expects($this->any())->method('getErrors')->will($this->returnValue($validationErrors)); + $command->setValidator($v); + + $this->assertEquals($validationErrors, $command->getValidationErrors()); + } + + public function testCanChangeResponseBody() + { + $body = EntityBody::factory(); + $command = new MockCommand(); + $command->setClient(new \Guzzle\Service\Client()); + $command->set(AbstractCommand::RESPONSE_BODY, $body); + $request = $command->prepare(); + $this->assertSame($body, $this->readAttribute($request, 'responseBody')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php new file mode 100644 index 0000000..b7a4682 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php @@ -0,0 +1,122 @@ +serializer = DefaultRequestSerializer::getInstance(); + $this->client = new Client('http://foo.com/baz'); + $this->operation = new Operation(array('httpMethod' => 'POST')); + $this->command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setConstructorArgs(array(array(), $this->operation)) + ->getMockForAbstractClass(); + $this->command->setClient($this->client); + } + + public function testAllowsCustomVisitor() + { + $this->serializer->addVisitor('custom', new HeaderVisitor()); + $this->command['test'] = '123'; + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'custom'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('123', (string) $request->getHeader('test')); + } + + public function testUsesRelativePath() + { + $this->operation->setUri('bar'); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar', (string) $request->getUrl()); + } + + public function testUsesRelativePathWithUriLocations() + { + $this->command['test'] = '123'; + $this->operation->setUri('bar/{test}'); + $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'uri'))); + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar/123', (string) $request->getUrl()); + } + + public function testAllowsCustomFactory() + { + $f = new VisitorFlyweight(); + $serializer = new DefaultRequestSerializer($f); + $this->assertSame($f, $this->readAttribute($serializer, 'factory')); + } + + public function testMixedParams() + { + $this->operation->setUri('bar{?limit,fields}'); + $this->operation->addParam(new Parameter(array( + 'name' => 'limit', + 'location' => 'uri', + 'required' => false, + ))); + $this->operation->addParam(new Parameter(array( + 'name' => 'fields', + 'location' => 'uri', + 'required' => true, + ))); + + $this->command['fields'] = array('id', 'name'); + + $request = $this->serializer->prepare($this->command); + $this->assertEquals('http://foo.com/baz/bar?fields='.urlencode('id,name'), (string) $request->getUrl()); + } + + public function testValidatesAdditionalParameters() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'bar' => array('location' => 'header') + ), + 'additionalParameters' => array( + 'location' => 'json' + ) + ) + ) + )); + + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('foo'); + $command['bar'] = 'test'; + $command['hello'] = 'abc'; + $request = $command->prepare(); + $this->assertEquals('test', (string) $request->getHeader('bar')); + $this->assertEquals('{"hello":"abc"}', (string) $request->getBody()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php new file mode 100644 index 0000000..a6a02f9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php @@ -0,0 +1,59 @@ +setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testParsesJsonResponses() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } + + /** + * @expectedException \Guzzle\Common\Exception\RuntimeException + */ + public function testThrowsExceptionWhenParsingJsonFails() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array('Content-Type' => 'application/json'), '{"Baz":ddw}'), true); + $op->execute(); + } + + public function testAddsContentTypeWhenExpectsIsSetOnCommand() + { + $op = new OperationCommand(array(), new Operation()); + $op['command.expects'] = 'application/json'; + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, null, '{"Baz":"Bar"}'), true); + $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php new file mode 100644 index 0000000..ab1041a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php @@ -0,0 +1,76 @@ +client = new Client(); + + $map = new MapFactory(array( + 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + $this->factory = new AliasFactory($this->client, array( + 'foo' => 'test', + 'bar' => 'sub', + 'sub' => 'test1', + 'krull' => 'test3', + 'krull_2' => 'krull', + 'sub_2' => 'bar', + 'bad_link' => 'jarjar' + )); + + $map2 = new MapFactory(array( + 'test3' => 'Guzzle\Tests\Service\Mock\Command\Sub\Sub' + )); + + $this->client->setCommandFactory(new CompositeFactory(array($map, $this->factory, $map2))); + } + + public function aliasProvider() + { + return array( + array('foo', 'Guzzle\Tests\Service\Mock\Command\MockCommand', false), + array('bar', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub_2', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('krull', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('krull_2', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('missing', null, true), + array('bad_link', null, true) + ); + } + + /** + * @dataProvider aliasProvider + */ + public function testAliasesCommands($key, $result, $exception) + { + try { + $command = $this->client->getCommand($key); + if (is_null($result)) { + $this->assertNull($command); + } else { + $this->assertInstanceof($result, $command); + } + } catch (\Exception $e) { + if (!$exception) { + $this->fail('Got exception when it was not expected'); + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php new file mode 100644 index 0000000..b896dcf --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php @@ -0,0 +1,124 @@ +getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } + + public function testIsIterable() + { + $factory = new CompositeFactory(array($this->getFactory(), $this->getFactory())); + $this->assertEquals(2, count($factory)); + $this->assertEquals(2, count(iterator_to_array($factory->getIterator()))); + } + + public function testFindsFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factory = new CompositeFactory(array($f1, $f2)); + $this->assertNull($factory->find('foo')); + $this->assertNull($factory->find($this->getFactory())); + $this->assertSame($f1, $factory->find('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertSame($f2, $factory->find('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + $this->assertSame($f1, $factory->find($f1)); + $this->assertSame($f2, $factory->find($f2)); + + $this->assertFalse($factory->has('foo')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + } + + public function testCreatesCommands() + { + $factory = new CompositeFactory(); + $this->assertNull($factory->factory('foo')); + + $f1 = $this->getFactory(); + $mockCommand1 = $this->getMockForAbstractClass('Guzzle\\Service\\Command\\AbstractCommand'); + + $f1->expects($this->once()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand1)); + + $factory = new CompositeFactory(array($f1)); + $this->assertSame($mockCommand1, $factory->factory('foo')); + } + + public function testAllowsRemovalOfFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factories = array($f1, $f2, $f3); + $factory = new CompositeFactory($factories); + + $factory->remove('foo'); + $this->assertEquals($factories, $factory->getIterator()->getArrayCopy()); + + $factory->remove($f1); + $this->assertEquals(array($f2, $f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\MapFactory'); + $this->assertEquals(array($f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + + $factory->remove('foo'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + } + + public function testAddsFactoriesBeforeAndAtEnd() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $f4 = $this->getFactory(); + + $factory = new CompositeFactory(); + + $factory->add($f1); + $this->assertEquals(array($f1), $factory->getIterator()->getArrayCopy()); + + $factory->add($f2); + $this->assertEquals(array($f1, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f3, $f2); + $this->assertEquals(array($f1, $f3, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f4, 'Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array($f1, $f4, $f3, $f2), $factory->getIterator()->getArrayCopy()); + } + + public function testProvidesDefaultChainForClients() + { + $client = $this->getMock('Guzzle\\Service\\Client'); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(1, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[0]); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->expects($this->once()) + ->method('getDescription') + ->will($this->returnValue($description)); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(2, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $a[0]); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[1]); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php new file mode 100644 index 0000000..7664718 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php @@ -0,0 +1,49 @@ + $prefix + )); + } + + $factory = new ConcreteClassFactory($client); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php new file mode 100644 index 0000000..ee720d1 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php @@ -0,0 +1,37 @@ + 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php new file mode 100644 index 0000000..3372634 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php @@ -0,0 +1,68 @@ +getDescription(); + + $factory = new ServiceDescriptionFactory($d); + $this->assertSame($d, $factory->getServiceDescription()); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } + + public function testUsesUcFirstIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Test')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('test')); + } + + public function testUsesInflectionIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('JarJar')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('jar_jar')); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array( + 'jar_jar' => array('class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand'), + 'binks' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'), + 'Test' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand') + ) + )); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php new file mode 100644 index 0000000..46b472e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php @@ -0,0 +1,110 @@ +command = new MockCommand(); + $this->request = new EntityEnclosingRequest('POST', 'http://www.test.com/some/path.php'); + $this->validator = new SchemaValidator(); + } + + protected function getCommand($location) + { + $command = new OperationCommand(array(), $this->getNestedCommand($location)); + $command->setClient(new MockClient()); + + return $command; + } + + protected function getNestedCommand($location) + { + return new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'object', + 'location' => $location, + 'sentAs' => 'Foo', + 'required' => true, + 'properties' => array( + 'test' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'type' => 'boolean', + 'default' => true + ), + 'jenga' => array( + 'type' => 'string', + 'default' => 'hello', + 'sentAs' => 'Jenga_Yall!', + 'filters' => array('strtoupper') + ) + ) + ), + 'bar' => array('default' => 123) + ), + 'additionalProperties' => array( + 'type' => 'string', + 'filters' => array('strtoupper'), + 'location' => $location + ) + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => $location, + 'items' => array( + 'type' => 'string', + 'filters' => array('strtoupper') + ) + )), + ) + )); + } + + protected function getCommandWithArrayParamAndFilters() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => new Parameter(array( + 'type' => 'string', + 'location' => 'query', + 'sentAs' => 'Foo', + 'required' => true, + 'default' => 'bar', + 'filters' => array('strtoupper') + )), + 'arr' => new Parameter(array( + 'type' => 'array', + 'location' => 'query', + 'sentAs' => 'Arr', + 'required' => true, + 'default' => array(123, 456, 789), + 'filters' => array(array('method' => 'implode', 'args' => array(',', '@value'))) + )) + ) + )); + $command = new OperationCommand(array(), $operation); + $command->setClient(new MockClient()); + + return $command; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php new file mode 100644 index 0000000..2a95c45 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php @@ -0,0 +1,63 @@ +getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testAddsExpectHeaderWhenSetToTrue() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', true); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getBody()); + } + + public function testCanDisableExpectHeader() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $param->setData('expect_header', false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + } + + public function testCanSetExpectHeaderBasedOnSize() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + // The body is less than the cutoff + $param->setData('expect_header', 5); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertNull($this->request->getHeader('Expect')); + // Now check when the body is greater than the cutoff + $param->setData('expect_header', 2); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('100-Continue', (string) $this->request->getHeader('Expect')); + } + + public function testAddsContentEncodingWhenSetOnBody() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); + $body = EntityBody::factory('foo'); + $body->compress(); + $visitor->visit($this->command, $this->request, $param, $body); + $this->assertEquals('gzip', (string) $this->request->getHeader('Content-Encoding')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php new file mode 100644 index 0000000..7ea1ae9 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(new Parameter(array())); + $visitor->visit($this->command, $this->request, $param, 'test'); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setAdditionalProperties(false); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', (string) $this->request->getHeader('test')); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); + $param->setSentAs('x-foo-'); + $param->setAdditionalProperties(new Parameter(array( + 'type' => 'string' + ))); + $visitor->visit($this->command, $this->request, $param, array( + 'bar' => 'test', + 'baz' => '123' + )); + $this->assertEquals('test', (string) $this->request->getHeader('x-foo-bar')); + $this->assertEquals('123', (string) $this->request->getHeader('x-foo-baz')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php new file mode 100644 index 0000000..ea6782f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php @@ -0,0 +1,60 @@ +after($this->command, $this->request); + + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test2'), 'abc'); + $visitor->after($this->command, $this->request); + $this->assertEquals('{"test":"123","test2":"abc"}', (string) $this->request->getBody()); + } + + public function testAddsJsonHeader() + { + $visitor = new Visitor(); + $visitor->setContentTypeHeader('application/json-foo'); + $param = $this->getNestedCommand('json')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $visitor->after($this->command, $this->request); + $this->assertEquals('application/json-foo', (string) $this->request->getHeader('Content-Type')); + } + + public function testRecursivelyBuildsJsonBodies() + { + $command = $this->getCommand('json'); + $request = $command->prepare(); + $this->assertEquals('{"Foo":{"test":{"baz":true,"Jenga_Yall!":"HELLO"},"bar":123}}', (string) $request->getBody()); + } + + public function testAppliesFiltersToAdditionalProperties() + { + $command = $this->getCommand('json'); + $command->set('foo', array('not_set' => 'abc')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals('ABC', $result['Foo']['not_set']); + } + + public function testAppliesFiltersToArrayItemValues() + { + $command = $this->getCommand('json'); + $command->set('arr', array('a', 'b')); + $request = $command->prepare(); + $result = json_decode($request->getBody(), true); + $this->assertEquals(array('A', 'B'), $result['arr']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php new file mode 100644 index 0000000..540b410 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php @@ -0,0 +1,33 @@ +getNestedCommand('postField')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); + $this->assertEquals('123', (string) $this->request->getPostField('test')); + } + + public function testRecursivelyBuildsPostFields() + { + $command = $this->getCommand('postField'); + $request = $command->prepare(); + $visitor = new Visitor(); + $param = $command->getOperation()->getParam('foo'); + $visitor->visit($command, $request, $param, $command['foo']); + $visitor->after($command, $request); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode((string) $request->getPostFields()) + ); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php new file mode 100644 index 0000000..21e3cec --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php @@ -0,0 +1,54 @@ +getNestedCommand('postFile')->getParam('foo'); + + // Test using a path to a file + $visitor->visit($this->command, $this->request, $param->setSentAs('test_3'), __FILE__); + $this->assertInternalType('array', $this->request->getPostFile('test_3')); + + // Test with a PostFile + $visitor->visit($this->command, $this->request, $param->setSentAs(null), new PostFile('baz', __FILE__)); + $this->assertInternalType('array', $this->request->getPostFile('baz')); + } + + public function testVisitsLocationWithMultipleFiles() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'DoPost' => array( + 'httpMethod' => 'POST', + 'parameters' => array( + 'foo' => array( + 'location' => 'postFile', + 'type' => array('string', 'array') + ) + ) + ) + ) + )); + $this->getServer()->flush(); + $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length:0\r\n\r\n")); + $client = new Client($this->getServer()->getUrl()); + $client->setDescription($description); + $command = $client->getCommand('DoPost', array('foo' => array(__FILE__, __FILE__))); + $command->execute(); + $received = $this->getServer()->getReceivedRequests(); + $this->assertContains('name="foo[0]";', $received[0]); + $this->assertContains('name="foo[1]";', $received[0]); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php new file mode 100644 index 0000000..607af76 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php @@ -0,0 +1,48 @@ +getNestedCommand('query')->getParam('foo')->setSentAs('test'); + $visitor->visit($this->command, $this->request, $param, '123'); + $this->assertEquals('123', $this->request->getQuery()->get('test')); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testRecursivelyBuildsQueryStrings() + { + $command = $this->getCommand('query'); + $command->getOperation()->getParam('foo')->setSentAs('Foo'); + $request = $command->prepare(); + $this->assertEquals( + 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', + rawurldecode($request->getQuery()) + ); + } + + /** + * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively + */ + public function testFiltersAreAppliedToArrayParamType() + { + $command = $this->getCommandWithArrayParamAndFilters(); + $request = $command->prepare(); + $query = $request->getQuery(); + // param type 'string' + $this->assertEquals('BAR', $query->get('Foo')); + // param type 'array' + $this->assertEquals('123,456,789', $query->get('Arr')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php new file mode 100644 index 0000000..ff8cec5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php @@ -0,0 +1,20 @@ +getNestedCommand('response_body')->getParam('foo'); + $visitor->visit($this->command, $this->request, $param, sys_get_temp_dir() . '/foo.txt'); + $body = $this->readAttribute($this->request, 'responseBody'); + $this->assertContains('/foo.txt', $body->getUri()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php new file mode 100644 index 0000000..beb58b0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php @@ -0,0 +1,558 @@ + array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => 'http://foo.com' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + 'testbar' + ), + // Ensure that the content-type is not added + array(array('parameters' => array('Foo' => array('location' => 'xml', 'type' => 'string'))), array(), ''), + // Test with adding attributes and no namespace + array( + array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test' + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string', 'data' => array('xmlAttribute' => true)) + ) + ), + array('Foo' => 'test', 'Baz' => 'bar'), + '' + ), + // Test adding with an array + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'numeric', + 'sentAs' => 'Bar' + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array(1, 2)), + 'test12' + ), + // Test adding an object + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testabcfoo' + ), + // Add an array that contains an object + array( + array( + 'parameters' => array( + 'Baz' => array( + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array('A' => array(), 'B' => array()) + ) + ) + ) + ), + array('Baz' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + )), + '1234' + ), + // Add an object of attributes + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), + 'testfoo' + ), + // Check order doesn't matter + array( + array( + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), + 'Bam' => array() + ) + ) + ) + ), + array('Foo' => 'test', 'Baz' => array('Bam' => 'foo', 'Bar' => 'abc')), + 'testfoo' + ), + // Add values with custom namespaces + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => 'test'), + 'test' + ), + // Add attributes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:baz', + 'data' => array( + 'xmlNamespace' => 'http://foo.com', + 'xmlAttribute' => true + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + '' + ), + // Add nodes with custom namespace prefix + array( + array( + 'parameters' => array( + 'Wrap' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Foo' => array( + 'type' => 'string', + 'sentAs' => 'xsi:Foo', + 'data' => array( + 'xmlNamespace' => 'http://foobar.com' + ) + ) + ) + ), + ) + ), + array('Wrap' => array( + 'Foo' => 'test' + )), + 'test' + ), + array( + array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'data' => array( + 'xmlNamespace' => 'http://foo.com' + ) + ) + ) + ), + array('Foo' => '

    This is a title

    '), + 'This is a title]]>' + ), + // Flat array at top level + array( + array( + 'parameters' => array( + 'Bars' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Bar', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ), + 'Boos' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'location' => 'xml', + 'items' => array( + 'sentAs' => 'Boo', + 'type' => 'string' + ) + ) + ) + ), + array( + 'Bars' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ), + 'Boos' => array('test', '123') + ), + '1234test123' + ), + // Nested flat arrays + array( + array( + 'parameters' => array( + 'Delete' => array( + 'type' => 'object', + 'location' => 'xml', + 'properties' => array( + 'Items' => array( + 'type' => 'array', + 'data' => array('xmlFlattened' => true), + 'items' => array( + 'type' => 'object', + 'sentAs' => 'Item', + 'properties' => array( + 'A' => array(), + 'B' => array() + ) + ) + ) + ) + ) + ) + ), + array( + 'Delete' => array( + 'Items' => array( + array('A' => '1', 'B' => '2'), + array('A' => '3', 'B' => '4') + ) + ) + ), + '1234' + ) + ); + } + + /** + * @dataProvider xmlProvider + */ + public function testSerializesXml(array $operation, array $input, $xml) + { + $operation = new Operation($operation); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array($input, $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://www.test.com/some/path.php')); + $request = $command->prepare(); + if (!empty($input)) { + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + } else { + $this->assertNull($request->getHeader('Content-Type')); + } + $body = str_replace(array("\n", ""), '', (string) $request->getBody()); + $this->assertEquals($xml, $body); + } + + public function testAddsContentTypeAndTopLevelValues() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'test', + 'namespaces' => array( + 'xsi' => 'http://foo.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string'), + 'Baz' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Baz' => 'bar' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'testbar' . "\n", + (string) $request->getBody() + ); + } + + public function testCanChangeContentType() + { + $visitor = new XmlVisitor(); + $visitor->setContentTypeHeader('application/foo'); + $this->assertEquals('application/foo', $this->readAttribute($visitor, 'contentType')); + } + + public function testCanAddArrayOfSimpleTypes() + { + $request = new EntityEnclosingRequest('POST', 'http://foo.com'); + $visitor = new XmlVisitor(); + $param = new Parameter(array( + 'type' => 'object', + 'location' => 'xml', + 'name' => 'Out', + 'properties' => array( + 'Nodes' => array( + 'required' => true, + 'type' => 'array', + 'min' => 1, + 'items' => array('type' => 'string', 'sentAs' => 'Node') + ) + ) + )); + + $param->setParent(new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Test', + 'namespaces' => array( + 'https://foo/' + ) + ) + ) + ))); + + $value = array('Nodes' => array('foo', 'baz')); + $this->assertTrue($this->validator->validate($param, $value)); + $visitor->visit($this->command, $request, $param, $value); + $visitor->after($this->command, $request); + + $this->assertEquals( + "\n" + . "foobaz\n", + (string) $request->getBody() + ); + } + + public function testCanAddMultipleNamespacesToRoot() + { + $operation = new Operation(array( + 'data' => array( + 'xmlRoot' => array( + 'name' => 'Hi', + 'namespaces' => array( + 'xsi' => 'http://foo.com', + 'foo' => 'http://foobar.com' + ) + ) + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml', 'type' => 'string') + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test' + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testValuesAreFiltered() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string', + 'filters' => array('strtoupper') + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'filters' => array('strtoupper') + ) + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => 'test', + 'Bar' => array( + 'Baz' => 'abc' + ) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'TESTABC' . "\n", + (string) $request->getBody() + ); + } + + public function testSkipsNullValues() + { + $operation = new Operation(array( + 'parameters' => array( + 'Foo' => array( + 'location' => 'xml', + 'type' => 'string' + ), + 'Bar' => array( + 'location' => 'xml', + 'type' => 'object', + 'properties' => array( + 'Baz' => array(), + 'Bam' => array(), + ) + ), + 'Arr' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string' + ) + ) + ) + )); + + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array( + 'Foo' => null, + 'Bar' => array( + 'Bar' => null, + 'Bam' => 'test' + ), + 'Arr' => array(null) + ), $operation)) + ->getMockForAbstractClass(); + + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsXmlEncoding() + { + $operation = new Operation(array( + 'data' => array( + 'xmlEncoding' => 'UTF-8' + ), + 'parameters' => array( + 'Foo' => array('location' => 'xml') + ) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array('Foo' => 'test'), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client()); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . 'test' . "\n", + (string) $request->getBody() + ); + } + + public function testAllowsSendingXmlPayloadIfNoXmlParamsWereSet() + { + $operation = new Operation(array( + 'httpMethod' => 'POST', + 'data' => array('xmlAllowEmpty' => true), + 'parameters' => array('Foo' => array('location' => 'xml')) + )); + $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') + ->setConstructorArgs(array(array(), $operation)) + ->getMockForAbstractClass(); + $command->setClient(new Client('http://foo.com')); + $request = $command->prepare(); + $this->assertEquals( + '' . "\n" + . '' . "\n", + (string) $request->getBody() + ); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php new file mode 100644 index 0000000..7b86003 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php @@ -0,0 +1,29 @@ +value = array(); + $this->command = new MockCommand(); + $this->response = new Response(200, array( + 'X-Foo' => 'bar', + 'Content-Length' => 3, + 'Content-Type' => 'text/plain' + ), 'Foo'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php new file mode 100644 index 0000000..932e39b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php @@ -0,0 +1,21 @@ + 'body', 'name' => 'foo')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('Foo', (string) $this->value['foo']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php new file mode 100644 index 0000000..db54b1a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php @@ -0,0 +1,98 @@ + 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type' + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + } + + public function testVisitsLocationWithFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'filters' => array('strtoupper') + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('TEXT/PLAIN', $this->value['Content-Type']); + } + + public function testVisitsMappedPrefixHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Metadata', + 'sentAs' => 'X-Baz-', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'string' + ) + )); + $response = new Response(200, array( + 'X-Baz-Test' => 'ABC', + 'X-Baz-Bar' => array('123', '456'), + 'Content-Length' => 3 + ), 'Foo'); + $visitor->visit($this->command, $response, $param, $this->value); + $this->assertEquals(array( + 'Metadata' => array( + 'Test' => 'ABC', + 'Bar' => array('123', '456') + ) + ), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownHeaders() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['Content-Type']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'header', + 'name' => 'ContentType', + 'sentAs' => 'Content-Type', + 'additionalParameters' => false + )); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('text/plain', $this->value['ContentType']); + $this->assertArrayNotHasKey('X-Foo', $this->value); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php new file mode 100644 index 0000000..4f8d30b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php @@ -0,0 +1,157 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, '{"foo":"bar"}'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('foo' => 'bar'), $result); + } + + public function testVisitsLocation() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'array', + 'items' => array( + 'filters' => 'strtoupper', + 'type' => 'string' + ) + )); + $this->value = array('foo' => array('a', 'b', 'c')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('A', 'B', 'C'), $this->value['foo']); + } + + public function testRenamesTopLevelValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'sentAs' => 'Baz', + 'type' => 'string', + )); + $this->value = array('Baz' => 'test'); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'test'), $this->value); + } + + public function testRenamesDoesNotFailForNonExistentKey() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('unknown' => 'Unknown')), $this->value); + } + + public function testTraversesObjectsAndAppliesFilters() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'foo' => array('filters' => 'strtoupper'), + 'bar' => array('filters' => 'strtolower') + ) + )); + $this->value = array('foo' => array('foo' => 'hello', 'bar' => 'THERE')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => 'HELLO', 'bar' => 'there'), $this->value['foo']); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testWalksAdditionalProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'object', + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'filters' => array('base64_decode') + ) + ), + ), + )); + $this->value = array('foo' => array('baz' => array('bar' => 'Zm9v'))); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('foo', $this->value['foo']['baz']['bar']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php new file mode 100644 index 0000000..23cd40f --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php @@ -0,0 +1,21 @@ + 'reasonPhrase', 'name' => 'phrase')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals('OK', $this->value['phrase']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php new file mode 100644 index 0000000..7211a58 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php @@ -0,0 +1,21 @@ + 'statusCode', 'name' => 'code')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(200, $this->value['code']); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php new file mode 100644 index 0000000..f87cec7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php @@ -0,0 +1,431 @@ +getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesXmlWithNamespace() + { + $this->markTestSkipped("Response/XmlVisitor cannot accept 'xmlns' in response, see #368 (http://git.io/USa1mA)."); + + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Bar' => 'test'), $result); + } + + public function testBeforeMethodParsesNestedXml() + { + $visitor = new Visitor(); + $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') + ->setMethods(array('getResponse')) + ->getMockForAbstractClass(); + $command->expects($this->once()) + ->method('getResponse') + ->will($this->returnValue(new Response(200, null, 'test'))); + $result = array(); + $visitor->before($command, $result); + $this->assertEquals(array('Items' => array('Bar' => 'test')), $result); + } + + public function testCanExtractAndRenameTopLevelXmlValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Bar' + )); + $value = array('Bar' => 'test'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertArrayHasKey('foo', $value); + $this->assertEquals('test', $value['foo']); + } + + public function testEnsuresRepeatedArraysAreInCorrectLocations() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'sentAs' => 'Foo', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string'), + 'Bam' => array('type' => 'string') + ) + ) + )); + + $xml = new \SimpleXMLElement('12'); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'foo' => array( + array ( + 'Bar' => '1', + 'Baz' => '2' + ) + ) + ), $value); + } + + public function testEnsuresFlatArraysAreFlat() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'foo', + 'type' => 'array', + 'items' => array('type' => 'string') + )); + + $value = array('foo' => array('bar', 'baz')); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar', 'baz')), $value); + + $value = array('foo' => 'bar'); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar')), $value); + } + + public function xmlDataProvider() + { + $param = new Parameter(array( + 'location' => 'xml', + 'name' => 'Items', + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'name' => 'Item', + 'properties' => array( + 'Bar' => array('type' => 'string'), + 'Baz' => array('type' => 'string') + ) + ) + )); + + return array( + array($param, '12', array( + 'Items' => array( + array('Bar' => 1), + array('Bar' => 2) + ) + )), + array($param, '1', array( + 'Items' => array( + array('Bar' => 1) + ) + )), + array($param, '', array( + 'Items' => array() + )) + ); + } + + /** + * @dataProvider xmlDataProvider + */ + public function testEnsuresWrappedArraysAreInCorrectLocations($param, $xml, $result) + { + $visitor = new Visitor(); + $xml = new \SimpleXMLElement($xml); + $value = json_decode(json_encode($xml), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals($result, $value); + } + + public function testCanRenameValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'TerminatingInstances', + 'type' => 'array', + 'location' => 'xml', + 'sentAs' => 'instancesSet', + 'items' => array( + 'name' => 'item', + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'InstanceId' => array( + 'type' => 'string', + 'sentAs' => 'instanceId', + ), + 'CurrentState' => array( + 'type' => 'object', + 'sentAs' => 'currentState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'sentAs' => 'previousState', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + ), + 'Name' => array( + 'type' => 'string', + 'sentAs' => 'name', + ), + ), + ), + ), + ) + )); + + $value = array( + 'instancesSet' => array ( + 'item' => array ( + 'instanceId' => 'i-3ea74257', + 'currentState' => array( + 'code' => '32', + 'name' => 'shutting-down', + ), + 'previousState' => array( + 'code' => '16', + 'name' => 'running', + ), + ), + ) + ); + + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'TerminatingInstances' => array( + array( + 'InstanceId' => 'i-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'shutting-down', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'running', + ) + ) + ) + ), $value); + } + + public function testCanRenameAttributes() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'RunningQueues', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'sentAs' => 'item', + 'properties' => array( + 'QueueId' => array( + 'type' => 'string', + 'sentAs' => 'queue_id', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'CurrentState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + 'PreviousState' => array( + 'type' => 'object', + 'properties' => array( + 'Code' => array( + 'type' => 'numeric', + 'sentAs' => 'code', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + 'Name' => array( + 'sentAs' => 'name', + 'data' => array( + 'xmlAttribute' => true, + ), + ), + ), + ), + ), + ) + )); + + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + + $this->assertEquals(array( + 'RunningQueues' => array( + array( + 'QueueId' => 'q-3ea74257', + 'CurrentState' => array( + 'Code' => '32', + 'Name' => 'processing', + ), + 'PreviousState' => array( + 'Code' => '16', + 'Name' => 'wait', + ), + ), + ) + ), $value); + } + + public function testAddsEmptyArraysWhenValueIsMissing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'location' => 'xml', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + 'Bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'array'), + ) + ) + ) + ) + )); + + $value = array(); + $visitor->visit($this->command, $this->response, $param, $value); + + $value = array( + 'Foo' => array( + 'Bar' => array() + ) + ); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array( + 'Foo' => array( + array( + 'Bar' => array() + ) + ) + ), $value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownProperties() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'type' => 'string', + 'name' => 'bar', + ), + ), + )); + $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testDiscardingUnknownPropertiesWithAliasing() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'bar' => array( + 'name' => 'bar', + 'sentAs' => 'baz', + ), + ), + )); + $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); + $visitor->visit($this->command, $this->response, $param, $this->value); + $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); + } + + public function testProperlyHandlesEmptyStringValues() + { + $visitor = new Visitor(); + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string') + ), + )); + $xml = ''; + $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); + $visitor->visit($this->command, $this->response, $param, $value); + $this->assertEquals(array('foo' => array('bar' => '')), $value); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php new file mode 100644 index 0000000..a252ffe --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php @@ -0,0 +1,53 @@ +assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', $f->getRequestVisitor('json')); + $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', $f->getResponseVisitor('json')); + } + + public function testCanUseCustomMappings() + { + $f = new VisitorFlyweight(array()); + $this->assertEquals(array(), $this->readAttribute($f, 'mappings')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage No request visitor has been mapped for foo + */ + public function testThrowsExceptionWhenRetrievingUnknownVisitor() + { + VisitorFlyweight::getInstance()->getRequestVisitor('foo'); + } + + public function testCachesVisitors() + { + $f = new VisitorFlyweight(); + $v1 = $f->getRequestVisitor('json'); + $this->assertSame($v1, $f->getRequestVisitor('json')); + } + + public function testAllowsAddingVisitors() + { + $f = new VisitorFlyweight(); + $j1 = new JsonRequestVisitor(); + $j2 = new JsonResponseVisitor(); + $f->addRequestVisitor('json', $j1); + $f->addResponseVisitor('json', $j2); + $this->assertSame($j1, $f->getRequestVisitor('json')); + $this->assertSame($j2, $f->getResponseVisitor('json')); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php new file mode 100644 index 0000000..95fb533 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php @@ -0,0 +1,102 @@ +getRequestSerializer(); + $b = new DefaultRequestSerializer(VisitorFlyweight::getInstance()); + $operation->setRequestSerializer($b); + $this->assertNotSame($a, $operation->getRequestSerializer()); + } + + public function testPreparesRequestUsingSerializer() + { + $op = new OperationCommand(array(), new Operation()); + $op->setClient(new Client()); + $s = $this->getMockBuilder('Guzzle\Service\Command\RequestSerializerInterface') + ->setMethods(array('prepare')) + ->getMockForAbstractClass(); + $s->expects($this->once()) + ->method('prepare') + ->will($this->returnValue(new EntityEnclosingRequest('POST', 'http://foo.com'))); + $op->setRequestSerializer($s); + $op->prepare(); + } + + public function testParsesResponsesWithResponseParser() + { + $op = new OperationCommand(array(), new Operation()); + $p = $this->getMockBuilder('Guzzle\Service\Command\ResponseParserInterface') + ->setMethods(array('parse')) + ->getMockForAbstractClass(); + $p->expects($this->once()) + ->method('parse') + ->will($this->returnValue(array('foo' => 'bar'))); + $op->setResponseParser($p); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200), true); + $this->assertEquals(array('foo' => 'bar'), $op->execute()); + } + + public function testParsesResponsesUsingModelParserWhenMatchingModelIsFound() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'foo' => array('responseClass' => 'bar', 'responseType' => 'model') + ), + 'models' => array( + 'bar' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array('type' => 'string', 'location' => 'xml') + ) + ) + ) + )); + + $op = new OperationCommand(array(), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $request->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'), true); + $result = $op->execute(); + $this->assertEquals(new Model(array('Baz' => 'Bar')), $result); + } + + public function testAllowsRawResponses() + { + $description = new ServiceDescription(array( + 'operations' => array('foo' => array('responseClass' => 'bar', 'responseType' => 'model')), + 'models' => array('bar' => array()) + )); + $op = new OperationCommand(array( + OperationCommand::RESPONSE_PROCESSING => OperationCommand::TYPE_RAW + ), $description->getOperation('foo')); + $op->setClient(new Client()); + $request = $op->prepare(); + $response = new Response(200, array( + 'Content-Type' => 'application/xml' + ), 'Bar'); + $request->setResponse($response, true); + $this->assertSame($response, $op->execute()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php new file mode 100644 index 0000000..69ba1fc --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php @@ -0,0 +1,335 @@ +addVisitor('foo', $visitor); + $this->assertSame($visitor, $this->readAttribute($p, 'factory')->getResponseVisitor('foo')); + } + + public function testUsesParentParser() + { + $p = new OperationResponseParser(new VisitorFlyweight()); + $operation = new Operation(); + $operation->setServiceDescription(new ServiceDescription()); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($p)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/xml'), 'C'), true); + $this->assertInstanceOf('SimpleXMLElement', $op->execute()); + } + + public function testVisitsLocations() + { + $parser = new OperationResponseParser(new VisitorFlyweight(array())); + $parser->addVisitor('statusCode', new StatusCodeVisitor()); + $parser->addVisitor('reasonPhrase', new ReasonPhraseVisitor()); + $parser->addVisitor('json', new JsonVisitor()); + $op = new OperationCommand(array(), $this->getDescription()->getOperation('test')); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $result = $op->execute(); + $this->assertEquals(201, $result['code']); + $this->assertEquals('Created', $result['phrase']); + } + + public function testVisitsLocationsForJsonResponse() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123', + 'code' => 200, + 'phrase' => 'OK' + ), $result->toArray()); + } + + public function testSkipsUnkownModels() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $operation->setResponseClass('Baz')->setResponseType('model'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(201), true); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $op->execute()); + } + + public function testAllowsModelProcessingToBeDisabled() + { + $parser = OperationResponseParser::getInstance(); + $operation = $this->getDescription()->getOperation('test'); + $op = new OperationCommand(array('command.response_processing' => 'native'), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Service\Resource\Model', $result); + $this->assertEquals(array( + 'baz' => 'bar', + 'enigma' => '123' + ), $result->toArray()); + } + + public function testCanInjectModelSchemaIntoModels() + { + $parser = new OperationResponseParser(VisitorFlyweight::getInstance(), true); + $desc = $this->getDescription(); + $operation = $desc->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/json' + ), '{"baz":"bar","enigma":"123"}'), true); + $result = $op->execute(); + $this->assertSame($result->getStructure(), $desc->getModel('Foo')); + } + + public function testDoesNotParseXmlWhenNotUsingXmlVisitor() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array('baz' => array('location' => 'body')) + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $brokenXml = '<><><>>>>'; + $op->prepare()->setResponse(new Response(200, array( + 'Content-Type' => 'application/xml' + ), $brokenXml), true); + $result = $op->execute(); + $this->assertEquals(array('baz'), $result->getKeys()); + $this->assertEquals($brokenXml, (string) $result['baz']); + } + + public function testVisitsAdditionalProperties() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'code' => array('location' => 'statusCode') + ), + 'additionalProperties' => array( + 'location' => 'json', + 'type' => 'object', + 'properties' => array( + 'a' => array( + 'type' => 'string', + 'filters' => 'strtoupper' + ) + ) + ) + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '[{"a":"test"},{"a":"baz"}]'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'code' => 200, + array('a' => 'TEST'), + array('a' => 'BAZ') + ), $result); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/399 + */ + public function testAdditionalPropertiesDisabledDiscardsData() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'name' => array( + 'location' => 'json', + 'type' => 'string', + ), + 'nested' => array( + 'location' => 'json', + 'type' => 'object', + 'additionalProperties' => false, + 'properties' => array( + 'width' => array( + 'type' => 'integer' + ) + ), + ), + 'code' => array('location' => 'statusCode') + ), + + ) + ) + )); + + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"name":"test", "volume":2.0, "nested":{"width":10,"bogus":1}}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array( + 'name' => 'test', + 'nested' => array( + 'width' => 10, + ), + 'code' => 200 + ), $result); + } + + public function testCreatesCustomResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Guzzle\Tests\Mock\CustomResponseModel')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $result = $op->execute(); + $this->assertInstanceOf('Guzzle\Tests\Mock\CustomResponseModel', $result); + $this->assertSame($op, $result->command); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage must exist + */ + public function testEnsuresResponseClassExists() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo\Baz\Bar')) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + /** + * @expectedException \Guzzle\Service\Exception\ResponseClassException + * @expectedExceptionMessage and implement + */ + public function testEnsuresResponseClassImplementsResponseClassInterface() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => __CLASS__)) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); + $op->execute(); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Foo' => array( + 'type' => 'object', + 'properties' => array( + 'baz' => array('type' => 'string', 'location' => 'json'), + 'code' => array('location' => 'statusCode'), + 'phrase' => array('location' => 'reasonPhrase'), + ) + ) + ) + )); + } + + public function testCanAddListenerToParseDomainObjects() + { + $client = new Client(); + $client->setDescription(ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'FooBazBar')) + ))); + $foo = new \stdClass(); + $client->getEventDispatcher()->addListener('command.parse_response', function ($e) use ($foo) { + $e['result'] = $foo; + }); + $command = $client->getCommand('test'); + $command->prepare()->setResponse(new Response(200), true); + $result = $command->execute(); + $this->assertSame($result, $foo); + } + + /** + * @group issue-399 + * @link https://github.com/guzzle/guzzle/issues/501 + */ + public function testAdditionalPropertiesWithRefAreResolved() + { + $parser = OperationResponseParser::getInstance(); + $description = ServiceDescription::factory(array( + 'operations' => array('test' => array('responseClass' => 'Foo')), + 'models' => array( + 'Baz' => array('type' => 'string'), + 'Foo' => array( + 'type' => 'object', + 'additionalProperties' => array('$ref' => 'Baz', 'location' => 'json') + ) + ) + )); + $operation = $description->getOperation('test'); + $op = new OperationCommand(array(), $operation); + $op->setResponseParser($parser)->setClient(new Client()); + $json = '{"a":"a","b":"b","c":"c"}'; + $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); + $result = $op->execute()->toArray(); + $this->assertEquals(array('a' => 'a', 'b' => 'b', 'c' => 'c'), $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php new file mode 100644 index 0000000..ae33b69 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php @@ -0,0 +1,308 @@ + 'test', + 'summary' => 'doc', + 'notes' => 'notes', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'POST', + 'uri' => '/api/v1', + 'responseClass' => 'array', + 'responseNotes' => 'returns the json_decoded response', + 'deprecated' => true, + 'parameters' => array( + 'key' => array( + 'required' => true, + 'type' => 'string', + 'maxLength' => 10 + ), + 'key_2' => array( + 'required' => true, + 'type' => 'integer', + 'default' => 10 + ) + ) + )); + + $this->assertEquals('test', $c->getName()); + $this->assertEquals('doc', $c->getSummary()); + $this->assertEquals('http://www.example.com', $c->getDocumentationUrl()); + $this->assertEquals('POST', $c->getHttpMethod()); + $this->assertEquals('/api/v1', $c->getUri()); + $this->assertEquals('array', $c->getResponseClass()); + $this->assertEquals('returns the json_decoded response', $c->getResponseNotes()); + $this->assertTrue($c->getDeprecated()); + $this->assertEquals('Guzzle\\Service\\Command\\OperationCommand', $c->getClass()); + $this->assertEquals(array( + 'key' => new Parameter(array( + 'name' => 'key', + 'required' => true, + 'type' => 'string', + 'maxLength' => 10, + 'parent' => $c + )), + 'key_2' => new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )) + ), $c->getParams()); + + $this->assertEquals(new Parameter(array( + 'name' => 'key_2', + 'required' => true, + 'type' => 'integer', + 'default' => 10, + 'parent' => $c + )), $c->getParam('key_2')); + + $this->assertNull($c->getParam('afefwef')); + $this->assertArrayNotHasKey('parent', $c->getParam('key_2')->toArray()); + } + + public function testAllowsConcreteCommands() + { + $c = new Operation(array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'parameters' => array( + 'p' => new Parameter(array( + 'name' => 'foo' + )) + ) + )); + $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getClass()); + } + + public function testConvertsToArray() + { + $data = array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'summary' => 'test', + 'documentationUrl' => 'http://www.example.com', + 'httpMethod' => 'PUT', + 'uri' => '/', + 'parameters' => array('p' => array('name' => 'foo')) + ); + $c = new Operation($data); + $toArray = $c->toArray(); + unset($data['name']); + $this->assertArrayHasKey('parameters', $toArray); + $this->assertInternalType('array', $toArray['parameters']); + + // Normalize the array + unset($data['parameters']); + unset($toArray['parameters']); + + $data['responseType'] = 'primitive'; + $data['responseClass'] = 'array'; + $this->assertEquals($data, $toArray); + } + + public function testDeterminesIfHasParam() + { + $command = $this->getTestCommand(); + $this->assertTrue($command->hasParam('data')); + $this->assertFalse($command->hasParam('baz')); + } + + public function testReturnsParamNames() + { + $command = $this->getTestCommand(); + $this->assertEquals(array('data'), $command->getParamNames()); + } + + protected function getTestCommand() + { + return new Operation(array( + 'parameters' => array( + 'data' => new Parameter(array( + 'type' => 'string' + )) + ) + )); + } + + public function testCanBuildUpCommands() + { + $c = new Operation(array()); + $c->setName('foo') + ->setClass('Baz') + ->setDeprecated(false) + ->setSummary('summary') + ->setDocumentationUrl('http://www.foo.com') + ->setHttpMethod('PUT') + ->setResponseNotes('oh') + ->setResponseClass('string') + ->setUri('/foo/bar') + ->addParam(new Parameter(array( + 'name' => 'test' + ))); + + $this->assertEquals('foo', $c->getName()); + $this->assertEquals('Baz', $c->getClass()); + $this->assertEquals(false, $c->getDeprecated()); + $this->assertEquals('summary', $c->getSummary()); + $this->assertEquals('http://www.foo.com', $c->getDocumentationUrl()); + $this->assertEquals('PUT', $c->getHttpMethod()); + $this->assertEquals('oh', $c->getResponseNotes()); + $this->assertEquals('string', $c->getResponseClass()); + $this->assertEquals('/foo/bar', $c->getUri()); + $this->assertEquals(array('test'), $c->getParamNames()); + } + + public function testCanRemoveParams() + { + $c = new Operation(array()); + $c->addParam(new Parameter(array('name' => 'foo'))); + $this->assertTrue($c->hasParam('foo')); + $c->removeParam('foo'); + $this->assertFalse($c->hasParam('foo')); + } + + public function testAddsNameToParametersIfNeeded() + { + $command = new Operation(array('parameters' => array('foo' => new Parameter(array())))); + $this->assertEquals('foo', $command->getParam('foo')->getName()); + } + + public function testContainsApiErrorInformation() + { + $command = $this->getOperation(); + $this->assertEquals(1, count($command->getErrorResponses())); + $arr = $command->toArray(); + $this->assertEquals(1, count($arr['errorResponses'])); + $command->addErrorResponse(400, 'Foo', 'Baz\\Bar'); + $this->assertEquals(2, count($command->getErrorResponses())); + $command->setErrorResponses(array()); + $this->assertEquals(0, count($command->getErrorResponses())); + } + + public function testHasNotes() + { + $o = new Operation(array('notes' => 'foo')); + $this->assertEquals('foo', $o->getNotes()); + $o->setNotes('bar'); + $this->assertEquals('bar', $o->getNotes()); + } + + public function testHasData() + { + $o = new Operation(array('data' => array('foo' => 'baz', 'bar' => 123))); + $o->setData('test', false); + $this->assertEquals('baz', $o->getData('foo')); + $this->assertEquals(123, $o->getData('bar')); + $this->assertNull($o->getData('wfefwe')); + $this->assertEquals(array( + 'parameters' => array(), + 'class' => 'Guzzle\Service\Command\OperationCommand', + 'data' => array('foo' => 'baz', 'bar' => 123, 'test' => false), + 'responseClass' => 'array', + 'responseType' => 'primitive' + ), $o->toArray()); + } + + public function testHasServiceDescription() + { + $s = new ServiceDescription(); + $o = new Operation(array(), $s); + $this->assertSame($s, $o->getServiceDescription()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesResponseType() + { + $o = new Operation(array('responseClass' => 'array', 'responseType' => 'foo')); + } + + public function testInfersResponseType() + { + $o = $this->getOperation(); + $o->setServiceDescription(new ServiceDescription(array('models' => array('Foo' => array())))); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('boolean')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('array')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('integer')->getResponseType()); + $this->assertEquals('primitive', $o->setResponseClass('string')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass('foo')->getResponseType()); + $this->assertEquals('class', $o->setResponseClass(__CLASS__)->getResponseType()); + $this->assertEquals('model', $o->setResponseClass('Foo')->getResponseType()); + } + + public function testHasResponseType() + { + // infers in the constructor + $o = new Operation(array('responseClass' => 'array')); + $this->assertEquals('primitive', $o->getResponseType()); + // Infers when set + $o = new Operation(); + $this->assertEquals('primitive', $o->getResponseType()); + $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); + } + + public function testHasAdditionalParameters() + { + $o = new Operation(array( + 'additionalParameters' => array( + 'type' => 'string', 'name' => 'binks' + ), + 'parameters' => array( + 'foo' => array('type' => 'integer') + ) + )); + $this->assertEquals('string', $o->getAdditionalParameters()->getType()); + $arr = $o->toArray(); + $this->assertEquals(array( + 'type' => 'string' + ), $arr['additionalParameters']); + } + + /** + * @return Operation + */ + protected function getOperation() + { + return new Operation(array( + 'name' => 'OperationTest', + 'class' => get_class($this), + 'parameters' => array( + 'test' => array('type' => 'object'), + 'bool_1' => array('default' => true, 'type' => 'boolean'), + 'bool_2' => array('default' => false), + 'float' => array('type' => 'numeric'), + 'int' => array('type' => 'integer'), + 'date' => array('type' => 'string'), + 'timestamp' => array('type' => 'string'), + 'string' => array('type' => 'string'), + 'username' => array('type' => 'string', 'required' => true, 'filters' => 'strtolower'), + 'test_function' => array('type' => 'string', 'filters' => __CLASS__ . '::strtoupper') + ), + 'errorResponses' => array( + array('code' => 503, 'reason' => 'InsufficientCapacity', 'class' => 'Guzzle\\Exception\\RuntimeException') + ) + )); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php new file mode 100644 index 0000000..b9c162a --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php @@ -0,0 +1,411 @@ + 'foo', + 'type' => 'bar', + 'required' => true, + 'default' => '123', + 'description' => '456', + 'minLength' => 2, + 'maxLength' => 5, + 'location' => 'body', + 'static' => 'static!', + 'filters' => array('trim', 'json_encode') + ); + + public function testCreatesParamFromArray() + { + $p = new Parameter($this->data); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('bar', $p->getType()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals('123', $p->getDefault()); + $this->assertEquals('456', $p->getDescription()); + $this->assertEquals(2, $p->getMinLength()); + $this->assertEquals(5, $p->getMaxLength()); + $this->assertEquals('body', $p->getLocation()); + $this->assertEquals('static!', $p->getStatic()); + $this->assertEquals(array('trim', 'json_encode'), $p->getFilters()); + } + + public function testCanConvertToArray() + { + $p = new Parameter($this->data); + unset($this->data['name']); + $this->assertEquals($this->data, $p->toArray()); + } + + public function testUsesStatic() + { + $d = $this->data; + $d['default'] = 'booboo'; + $d['static'] = true; + $p = new Parameter($d); + $this->assertEquals('booboo', $p->getValue('bar')); + } + + public function testUsesDefault() + { + $d = $this->data; + $d['default'] = 'foo'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue(null)); + } + + public function testReturnsYourValue() + { + $d = $this->data; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('foo', $p->getValue('foo')); + } + + public function testZeroValueDoesNotCauseDefaultToBeReturned() + { + $d = $this->data; + $d['default'] = '1'; + $d['static'] = null; + $p = new Parameter($d); + $this->assertEquals('0', $p->getValue('0')); + } + + public function testFiltersValues() + { + $d = $this->data; + $d['static'] = null; + $d['filters'] = 'strtoupper'; + $p = new Parameter($d); + $this->assertEquals('FOO', $p->filter('foo')); + } + + public function testConvertsBooleans() + { + $p = new Parameter(array('type' => 'boolean')); + $this->assertEquals(true, $p->filter('true')); + $this->assertEquals(false, $p->filter('false')); + } + + public function testUsesArrayByDefaultForFilters() + { + $d = $this->data; + $d['filters'] = null; + $p = new Parameter($d); + $this->assertEquals(array(), $p->getFilters()); + } + + public function testAllowsSimpleLocationValue() + { + $p = new Parameter(array('name' => 'myname', 'location' => 'foo', 'sentAs' => 'Hello')); + $this->assertEquals('foo', $p->getLocation()); + $this->assertEquals('Hello', $p->getSentAs()); + } + + public function testParsesTypeValues() + { + $p = new Parameter(array('type' => 'foo')); + $this->assertEquals('foo', $p->getType()); + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage A [method] value must be specified for each complex filter + */ + public function testValidatesComplexFilters() + { + $p = new Parameter(array('filters' => array(array('args' => 'foo')))); + } + + public function testCanBuildUpParams() + { + $p = new Parameter(array()); + $p->setName('foo') + ->setDescription('c') + ->setFilters(array('d')) + ->setLocation('e') + ->setSentAs('f') + ->setMaxLength(1) + ->setMinLength(1) + ->setMinimum(2) + ->setMaximum(2) + ->setMinItems(3) + ->setMaxItems(3) + ->setRequired(true) + ->setStatic(true) + ->setDefault('h') + ->setType('i'); + + $p->addFilter('foo'); + + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('h', $p->getDefault()); + $this->assertEquals('c', $p->getDescription()); + $this->assertEquals(array('d', 'foo'), $p->getFilters()); + $this->assertEquals('e', $p->getLocation()); + $this->assertEquals('f', $p->getSentAs()); + $this->assertEquals(1, $p->getMaxLength()); + $this->assertEquals(1, $p->getMinLength()); + $this->assertEquals(2, $p->getMaximum()); + $this->assertEquals(2, $p->getMinimum()); + $this->assertEquals(3, $p->getMaxItems()); + $this->assertEquals(3, $p->getMinItems()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals(true, $p->getStatic()); + $this->assertEquals('i', $p->getType()); + } + + public function testAllowsNestedShape() + { + $command = $this->getServiceBuilder()->get('mock')->getCommand('mock_command')->getOperation(); + $param = new Parameter(array( + 'parent' => $command, + 'name' => 'foo', + 'type' => 'object', + 'location' => 'query', + 'properties' => array( + 'foo' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'baz' => array( + 'name' => 'baz', + 'type' => 'bool', + ) + ) + ), + 'bar' => array( + 'name' => 'bar', + 'default' => '123' + ) + ) + )); + + $this->assertSame($command, $param->getParent()); + $this->assertNotEmpty($param->getProperties()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('foo')); + $this->assertSame($param, $param->getProperty('foo')->getParent()); + $this->assertSame($param->getProperty('foo'), $param->getProperty('foo')->getProperty('baz')->getParent()); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('bar')); + $this->assertSame($param, $param->getProperty('bar')->getParent()); + + $array = $param->toArray(); + $this->assertInternalType('array', $array['properties']); + $this->assertArrayHasKey('foo', $array['properties']); + $this->assertArrayHasKey('bar', $array['properties']); + } + + public function testAllowsComplexFilters() + { + $that = $this; + $param = new Parameter(array()); + $param->setFilters(array(array('method' => function ($a, $b, $c, $d) use ($that, $param) { + $that->assertEquals('test', $a); + $that->assertEquals('my_value!', $b); + $that->assertEquals('bar', $c); + $that->assertSame($param, $d); + return 'abc' . $b; + }, 'args' => array('test', '@value', 'bar', '@api')))); + $this->assertEquals('abcmy_value!', $param->filter('my_value!')); + } + + public function testCanChangeParentOfNestedParameter() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param2->setParent($param1); + $this->assertSame($param1, $param2->getParent()); + } + + public function testCanRemoveFromNestedStructure() + { + $param1 = new Parameter(array('name' => 'parent')); + $param2 = new Parameter(array('name' => 'child')); + $param1->addProperty($param2); + $this->assertSame($param1, $param2->getParent()); + $this->assertSame($param2, $param1->getProperty('child')); + + // Remove a single child from the structure + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + // Remove the entire structure + $param1->addProperty($param2); + $param1->removeProperty('child'); + $this->assertNull($param1->getProperty('child')); + } + + public function testAddsAdditionalProperties() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getAdditionalProperties()); + $this->assertNull($p->getAdditionalProperties()->getAdditionalProperties()); + $p = new Parameter(array('type' => 'object')); + $this->assertTrue($p->getAdditionalProperties()); + } + + public function testAddsItems() + { + $p = new Parameter(array( + 'type' => 'array', + 'items' => array('type' => 'string') + )); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getItems()); + $out = $p->toArray(); + $this->assertEquals('array', $out['type']); + $this->assertInternalType('array', $out['items']); + } + + public function testHasExtraProperties() + { + $p = new Parameter(); + $this->assertEquals(array(), $p->getData()); + $p->setData(array('foo' => 'bar')); + $this->assertEquals('bar', $p->getData('foo')); + $p->setData('baz', 'boo'); + $this->assertEquals(array('foo' => 'bar', 'baz' => 'boo'), $p->getData()); + } + + public function testCanRetrieveKnownPropertiesUsingDataMethod() + { + $p = new Parameter(); + $this->assertEquals(null, $p->getData('foo')); + $p->setName('test'); + $this->assertEquals('test', $p->getData('name')); + } + + public function testHasInstanceOf() + { + $p = new Parameter(); + $this->assertNull($p->getInstanceOf()); + $p->setInstanceOf('Foo'); + $this->assertEquals('Foo', $p->getInstanceOf()); + } + + public function testHasPattern() + { + $p = new Parameter(); + $this->assertNull($p->getPattern()); + $p->setPattern('/[0-9]+/'); + $this->assertEquals('/[0-9]+/', $p->getPattern()); + } + + public function testHasEnum() + { + $p = new Parameter(); + $this->assertNull($p->getEnum()); + $p->setEnum(array('foo', 'bar')); + $this->assertEquals(array('foo', 'bar'), $p->getEnum()); + } + + public function testSerializesItems() + { + $p = new Parameter(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + )); + $this->assertEquals(array( + 'type' => 'object', + 'additionalProperties' => array('type' => 'string') + ), $p->toArray()); + } + + public function testResolvesRefKeysRecursively() + { + $description = new ServiceDescription(array( + 'models' => array( + 'JarJar' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'Anakin' => array('type' => 'array', 'items' => array('$ref' => 'JarJar')) + ) + )); + $p = new Parameter(array('$ref' => 'Anakin', 'description' => 'added'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), + 'description' => 'added' + ), $p->toArray()); + } + + public function testResolvesExtendsRecursively() + { + $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'a'); + $anakin = array('type' => 'array', 'items' => array('extends' => 'JarJar', 'description' => 'b')); + $description = new ServiceDescription(array( + 'models' => array('JarJar' => $jarJar, 'Anakin' => $anakin) + )); + // Description attribute will be updated, and format added + $p = new Parameter(array('extends' => 'Anakin', 'format' => 'date'), $description); + $this->assertEquals(array( + 'type' => 'array', + 'format' => 'date', + 'items' => array( + 'type' => 'string', + 'default' => 'Mesa address tha senate!', + 'description' => 'b' + ) + ), $p->toArray()); + } + + public function testHasKeyMethod() + { + $p = new Parameter(array('name' => 'foo', 'sentAs' => 'bar')); + $this->assertEquals('bar', $p->getWireName()); + $p->setSentAs(null); + $this->assertEquals('foo', $p->getWireName()); + } + + public function testIncludesNameInToArrayWhenItemsAttributeHasName() + { + $p = new Parameter(array( + 'type' => 'array', + 'name' => 'Abc', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object' + ) + )); + $result = $p->toArray(); + $this->assertEquals(array( + 'type' => 'array', + 'items' => array( + 'name' => 'Foo', + 'type' => 'object', + 'additionalProperties' => true + ) + ), $result); + } + + public function dateTimeProvider() + { + $d = 'October 13, 2012 16:15:46 UTC'; + + return array( + array($d, 'date-time', '2012-10-13T16:15:46Z'), + array($d, 'date', '2012-10-13'), + array($d, 'timestamp', strtotime($d)), + array(new \DateTime($d), 'timestamp', strtotime($d)) + ); + } + + /** + * @dataProvider dateTimeProvider + */ + public function testAppliesFormat($d, $format, $result) + { + $p = new Parameter(); + $p->setFormat($format); + $this->assertEquals($format, $p->getFormat()); + $this->assertEquals($result, $p->filter($d)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php new file mode 100644 index 0000000..eb3619b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php @@ -0,0 +1,61 @@ +assertEquals($result, SchemaFormatter::format($format, $value)); + } + + /** + * @expectedException \Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesDateTimeInput() + { + SchemaFormatter::format('date-time', false); + } + + public function testEnsuresTimestampsAreIntegers() + { + $t = time(); + $result = SchemaFormatter::format('timestamp', $t); + $this->assertSame($t, $result); + $this->assertInternalType('int', $result); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php new file mode 100644 index 0000000..4d6cc87 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php @@ -0,0 +1,326 @@ +validator = new SchemaValidator(); + } + + public function testValidatesArrayListsAreNumericallyIndexed() + { + $value = array(array(1)); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be an array of properties. Got a numerically indexed array.'), + $this->validator->getErrors() + ); + } + + public function testValidatesArrayListsContainProperItems() + { + $value = array(true); + $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals( + array('[Foo][0] must be of type object'), + $this->validator->getErrors() + ); + } + + public function testAddsDefaultValuesInLists() + { + $value = array(array()); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array(array('Bar' => true)), $value); + } + + public function testMergesDefaultValuesInLists() + { + $value = array( + array('Baz' => 'hello!'), + array('Bar' => false) + ); + $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); + $this->assertEquals(array( + array( + 'Baz' => 'hello!', + 'Bar' => true + ), + array('Bar' => false) + ), $value); + } + + public function testCorrectlyConvertsParametersToArrayWhenArraysArePresent() + { + $param = $this->getComplexParam(); + $result = $param->toArray(); + $this->assertInternalType('array', $result['items']); + $this->assertEquals('array', $result['type']); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getItems()); + } + + public function testAllowsInstanceOf() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $this->assertFalse($this->validator->validate($p, $p)); + $this->assertEquals(array('[foo] must be an instance of ' . __CLASS__), $this->validator->getErrors()); + } + + public function testEnforcesInstanceOfOnlyWhenObject() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => array('object', 'string'), + 'instanceOf' => get_class($this) + )); + $this->assertTrue($this->validator->validate($p, $this)); + $s = 'test'; + $this->assertTrue($this->validator->validate($p, $s)); + } + + public function testConvertsObjectsToArraysWhenToArrayInterface() + { + $o = $this->getMockBuilder('Guzzle\Common\ToArrayInterface') + ->setMethods(array('toArray')) + ->getMockForAbstractClass(); + $o->expects($this->once()) + ->method('toArray') + ->will($this->returnValue(array( + 'foo' => 'bar' + ))); + $p = new Parameter(array( + 'name' => 'test', + 'type' => 'object', + 'properties' => array( + 'foo' => array('required' => 'true') + ) + )); + $this->assertTrue($this->validator->validate($p, $o)); + } + + public function testMergesValidationErrorsInPropertiesWithParent() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array( + 'bar' => array('type' => 'string', 'required' => true, 'description' => 'This is what it does'), + 'test' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 5), + 'test2' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 2), + 'test3' => array('type' => 'integer', 'minimum' => 100), + 'test4' => array('type' => 'integer', 'maximum' => 10), + 'test5' => array('type' => 'array', 'maxItems' => 2), + 'test6' => array('type' => 'string', 'enum' => array('a', 'bc')), + 'test7' => array('type' => 'string', 'pattern' => '/[0-9]+/'), + 'test8' => array('type' => 'number'), + 'baz' => array( + 'type' => 'array', + 'minItems' => 2, + 'required' => true, + "items" => array("type" => "string") + ) + ) + )); + + $value = array( + 'test' => 'a', + 'test2' => 'abc', + 'baz' => array(false), + 'test3' => 10, + 'test4' => 100, + 'test5' => array(1, 3, 4), + 'test6' => 'Foo', + 'test7' => 'abc', + 'test8' => 'abc' + ); + + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array ( + '[foo][bar] is a required string: This is what it does', + '[foo][baz] must contain 2 or more elements', + '[foo][baz][0] must be of type string', + '[foo][test2] length must be less than or equal to 2', + '[foo][test3] must be greater than or equal to 100', + '[foo][test4] must be less than or equal to 10', + '[foo][test5] must contain 2 or fewer elements', + '[foo][test6] must be one of "a" or "bc"', + '[foo][test7] must match the following regular expression: /[0-9]+/', + '[foo][test8] must be of type number', + '[foo][test] length must be greater than or equal to 2', + ), $this->validator->getErrors()); + } + + public function testHandlesNullValuesInArraysWithDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'foo' => array('default' => 'hi') + ) + ) + ) + )); + $value = array(); + $this->assertTrue($this->validator->validate($p, $value)); + $this->assertEquals(array('bar' => array('foo' => 'hi')), $value); + } + + public function testFailsWhenNullValuesInArraysWithNoDefaults() + { + $p = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'bar' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array('foo' => array('type' => 'string')) + ) + ) + )); + $value = array(); + $this->assertFalse($this->validator->validate($p, $value)); + $this->assertEquals(array('[foo][bar] is a required object'), $this->validator->getErrors()); + } + + public function testChecksTypes() + { + $p = new SchemaValidator(); + $r = new \ReflectionMethod($p, 'determineType'); + $r->setAccessible(true); + $this->assertEquals('any', $r->invoke($p, 'any', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'foo', 'foo')); + $this->assertEquals('string', $r->invoke($p, 'string', 'hello')); + $this->assertEquals(false, $r->invoke($p, 'string', false)); + $this->assertEquals('integer', $r->invoke($p, 'integer', 1)); + $this->assertEquals(false, $r->invoke($p, 'integer', 'abc')); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', 1)); + $this->assertEquals('numeric', $r->invoke($p, 'numeric', '1')); + $this->assertEquals('number', $r->invoke($p, 'number', 1)); + $this->assertEquals('number', $r->invoke($p, 'number', '1')); + $this->assertEquals(false, $r->invoke($p, 'numeric', 'a')); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', true)); + $this->assertEquals('boolean', $r->invoke($p, 'boolean', false)); + $this->assertEquals(false, $r->invoke($p, 'boolean', 'false')); + $this->assertEquals('null', $r->invoke($p, 'null', null)); + $this->assertEquals(false, $r->invoke($p, 'null', 'abc')); + $this->assertEquals('array', $r->invoke($p, 'array', array())); + $this->assertEquals(false, $r->invoke($p, 'array', 'foo')); + } + + public function testValidatesFalseAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => false + )); + $value = array('test' => '123'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] is not an allowed property'), $this->validator->getErrors()); + $value = array('bar' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testAllowsUndefinedAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')) + )); + $value = array('test' => '123'); + $this->assertTrue($this->validator->validate($param, $value)); + } + + public function testValidatesAdditionalProperties() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'properties' => array('bar' => array('type' => 'string')), + 'additionalProperties' => array('type' => 'integer') + )); + $value = array('test' => 'foo'); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test] must be of type integer'), $this->validator->getErrors()); + } + + public function testValidatesAdditionalPropertiesThatArrayArrays() + { + $param = new Parameter(array( + 'name' => 'foo', + 'type' => 'object', + 'additionalProperties' => array( + 'type' => 'array', + 'items' => array('type' => 'string') + ) + )); + $value = array('test' => array(true)); + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[foo][test][0] must be of type string'), $this->validator->getErrors()); + } + + public function testIntegersCastToStringWhenTypeMismatch() + { + $param = new Parameter(array('name' => 'test', 'type' => 'string')); + $value = 12; + $this->assertTrue($this->validator->validate($param, $value)); + $this->assertEquals('12', $value); + } + + public function testRequiredMessageIncludesType() + { + $param = new Parameter(array('name' => 'test', 'type' => array('string', 'boolean'), 'required' => true)); + $value = null; + $this->assertFalse($this->validator->validate($param, $value)); + $this->assertEquals(array('[test] is a required string or boolean'), $this->validator->getErrors()); + } + + protected function getComplexParam() + { + return new Parameter(array( + 'name' => 'Foo', + 'type' => 'array', + 'required' => true, + 'min' => 1, + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'Baz' => array( + 'type' => 'string', + ), + 'Bar' => array( + 'required' => true, + 'type' => 'boolean', + 'default' => true + ) + ) + ) + )); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php new file mode 100644 index 0000000..bbfd1d6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php @@ -0,0 +1,177 @@ + true, + 'baz' => array('bar'), + 'apiVersion' => '123', + 'operations' => array() + )); + + $this->assertEquals(true, $d->getData('foo')); + $this->assertEquals(array('bar'), $d->getData('baz')); + $this->assertEquals('123', $d->getApiVersion()); + } + + public function testAllowsDeepNestedInheritance() + { + $d = ServiceDescription::factory(array( + 'operations' => array( + 'abstract' => array( + 'httpMethod' => 'HEAD', + 'parameters' => array( + 'test' => array('type' => 'string', 'required' => true) + ) + ), + 'abstract2' => array('uri' => '/test', 'extends' => 'abstract'), + 'concrete' => array('extends' => 'abstract2'), + 'override' => array('extends' => 'abstract', 'httpMethod' => 'PUT'), + 'override2' => array('extends' => 'override', 'httpMethod' => 'POST', 'uri' => '/') + ) + )); + + $c = $d->getOperation('concrete'); + $this->assertEquals('/test', $c->getUri()); + $this->assertEquals('HEAD', $c->getHttpMethod()); + $params = $c->getParams(); + $param = $params['test']; + $this->assertEquals('string', $param->getType()); + $this->assertTrue($param->getRequired()); + + // Ensure that merging HTTP method does not make an array + $this->assertEquals('PUT', $d->getOperation('override')->getHttpMethod()); + $this->assertEquals('POST', $d->getOperation('override2')->getHttpMethod()); + $this->assertEquals('/', $d->getOperation('override2')->getUri()); + } + + /** + * @expectedException RuntimeException + */ + public function testThrowsExceptionWhenExtendingMissingCommand() + { + ServiceDescription::factory(array( + 'operations' => array( + 'concrete' => array( + 'extends' => 'missing' + ) + ) + )); + } + + public function testAllowsMultipleInheritance() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'a' => array( + 'httpMethod' => 'GET', + 'parameters' => array( + 'a1' => array( + 'default' => 'foo', + 'required' => true, + 'prepend' => 'hi' + ) + ) + ), + 'b' => array( + 'extends' => 'a', + 'parameters' => array( + 'b2' => array() + ) + ), + 'c' => array( + 'parameters' => array( + 'a1' => array( + 'default' => 'bar', + 'required' => true, + 'description' => 'test' + ), + 'c3' => array() + ) + ), + 'd' => array( + 'httpMethod' => 'DELETE', + 'extends' => array('b', 'c'), + 'parameters' => array( + 'test' => array() + ) + ) + ) + )); + + $command = $description->getOperation('d'); + $this->assertEquals('DELETE', $command->getHttpMethod()); + $this->assertContains('a1', $command->getParamNames()); + $this->assertContains('b2', $command->getParamNames()); + $this->assertContains('c3', $command->getParamNames()); + $this->assertContains('test', $command->getParamNames()); + + $this->assertTrue($command->getParam('a1')->getRequired()); + $this->assertEquals('bar', $command->getParam('a1')->getDefault()); + $this->assertEquals('test', $command->getParam('a1')->getDescription()); + } + + public function testAddsOtherFields() + { + $description = ServiceDescription::factory(array( + 'operations' => array(), + 'description' => 'Foo', + 'apiVersion' => 'bar' + )); + $this->assertEquals('Foo', $description->getDescription()); + $this->assertEquals('bar', $description->getApiVersion()); + } + + public function testCanLoadNestedExtends() + { + $description = ServiceDescription::factory(array( + 'operations' => array( + 'root' => array( + 'class' => 'foo' + ), + 'foo' => array( + 'extends' => 'root', + 'parameters' => array( + 'baz' => array('type' => 'string') + ) + ), + 'foo_2' => array( + 'extends' => 'foo', + 'parameters' => array( + 'bar' => array('type' => 'string') + ) + ), + 'foo_3' => array( + 'class' => 'bar', + 'parameters' => array( + 'bar2' => array('type' => 'string') + ) + ), + 'foo_4' => array( + 'extends' => array('foo_2', 'foo_3'), + 'parameters' => array( + 'bar3' => array('type' => 'string') + ) + ) + ) + )); + + $this->assertTrue($description->hasOperation('foo_4')); + $foo4 = $description->getOperation('foo_4'); + $this->assertTrue($foo4->hasParam('baz')); + $this->assertTrue($foo4->hasParam('bar')); + $this->assertTrue($foo4->hasParam('bar2')); + $this->assertTrue($foo4->hasParam('bar3')); + $this->assertEquals('bar', $foo4->getClass()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php new file mode 100644 index 0000000..ff25452 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php @@ -0,0 +1,240 @@ +serviceData = array( + 'test_command' => new Operation(array( + 'name' => 'test_command', + 'description' => 'documentationForCommand', + 'httpMethod' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'parameters' => array( + 'bucket' => array('required' => true), + 'key' => array('required' => true) + ) + )) + ); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::factory + * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build + */ + public function testFactoryDelegatesToConcreteFactories() + { + $jsonFile = __DIR__ . '/../../TestData/test_service.json'; + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); + } + + public function testConstructor() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $this->assertEquals(1, count($service->getOperations())); + $this->assertFalse($service->hasOperation('foobar')); + $this->assertTrue($service->hasOperation('test_command')); + } + + public function testIsSerializable() + { + $service = new ServiceDescription(array('operations' => $this->serviceData)); + $data = serialize($service); + $d2 = unserialize($data); + $this->assertEquals(serialize($service), serialize($d2)); + } + + public function testSerializesParameters() + { + $service = new ServiceDescription(array( + 'operations' => array( + 'foo' => new Operation(array('parameters' => array('foo' => array('type' => 'string')))) + ) + )); + $serialized = serialize($service); + $this->assertContains('"parameters":{"foo":', $serialized); + $service = unserialize($serialized); + $this->assertTrue($service->getOperation('foo')->hasParam('foo')); + } + + public function testAllowsForJsonBasedArrayParamsFunctionalTest() + { + $description = new ServiceDescription(array( + 'operations' => array( + 'test' => new Operation(array( + 'httpMethod' => 'PUT', + 'parameters' => array( + 'data' => array( + 'required' => true, + 'filters' => 'json_encode', + 'location' => 'body' + ) + ) + )) + ) + )); + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('test', array( + 'data' => array( + 'foo' => 'bar' + ) + )); + + $request = $command->prepare(); + $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); + } + + public function testContainsModels() + { + $d = new ServiceDescription(array( + 'operations' => array('foo' => array()), + 'models' => array( + 'Tag' => array('type' => 'object'), + 'Person' => array('type' => 'object') + ) + )); + $this->assertTrue($d->hasModel('Tag')); + $this->assertTrue($d->hasModel('Person')); + $this->assertFalse($d->hasModel('Foo')); + $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $d->getModel('Tag')); + $this->assertNull($d->getModel('Foo')); + $this->assertContains('"models":{', serialize($d)); + $this->assertEquals(array('Tag', 'Person'), array_keys($d->getModels())); + } + + public function testCanAddModels() + { + $d = new ServiceDescription(array()); + $this->assertFalse($d->hasModel('Foo')); + $d->addModel(new Parameter(array('name' => 'Foo'))); + $this->assertTrue($d->hasModel('Foo')); + } + + public function testHasAttributes() + { + $d = new ServiceDescription(array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Description', + 'apiVersion' => '1.24' + )); + + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + + $s = serialize($d); + $this->assertContains('"name":"Name"', $s); + $this->assertContains('"description":"Description"', $s); + $this->assertContains('"apiVersion":"1.24"', $s); + + $d = unserialize($s); + $this->assertEquals('Name', $d->getName()); + $this->assertEquals('Description', $d->getDescription()); + $this->assertEquals('1.24', $d->getApiVersion()); + } + + public function testPersistsCustomAttributes() + { + $data = array( + 'operations' => array('foo' => array('class' => 'foo', 'parameters' => array())), + 'name' => 'Name', + 'description' => 'Test', + 'apiVersion' => '1.24', + 'auth' => 'foo', + 'keyParam' => 'bar' + ); + $d = new ServiceDescription($data); + $d->setData('hello', 'baz'); + $this->assertEquals('foo', $d->getData('auth')); + $this->assertEquals('baz', $d->getData('hello')); + $this->assertEquals('bar', $d->getData('keyParam')); + // responseClass and responseType are added by default + $data['operations']['foo']['responseClass'] = 'array'; + $data['operations']['foo']['responseType'] = 'primitive'; + $this->assertEquals($data + array('hello' => 'baz'), json_decode($d->serialize(), true)); + } + + public function testHasToArray() + { + $data = array( + 'operations' => array(), + 'name' => 'Name', + 'description' => 'Test' + ); + $d = new ServiceDescription($data); + $arr = $d->toArray(); + $this->assertEquals('Name', $arr['name']); + $this->assertEquals('Test', $arr['description']); + } + + public function testReturnsNullWhenRetrievingMissingOperation() + { + $s = new ServiceDescription(array()); + $this->assertNull($s->getOperation('foo')); + } + + public function testCanAddOperations() + { + $s = new ServiceDescription(array()); + $this->assertFalse($s->hasOperation('Foo')); + $s->addOperation(new Operation(array('name' => 'Foo'))); + $this->assertTrue($s->hasOperation('Foo')); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testValidatesOperationTypes() + { + $s = new ServiceDescription(array( + 'operations' => array('foo' => new \stdClass()) + )); + } + + public function testHasBaseUrl() + { + $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + $description->setBaseUrl('http://foobar.com'); + $this->assertEquals('http://foobar.com', $description->getBaseUrl()); + } + + public function testCanUseBasePath() + { + $description = new ServiceDescription(array('basePath' => 'http://foo.com')); + $this->assertEquals('http://foo.com', $description->getBaseUrl()); + } + + public function testModelsHaveNames() + { + $desc = array( + 'models' => array( + 'date' => array('type' => 'string'), + 'user'=> array( + 'type' => 'object', + 'properties' => array( + 'dob' => array('$ref' => 'date') + ) + ) + ) + ); + + $s = ServiceDescription::factory($desc); + $this->assertEquals('date', $s->getModel('date')->getName()); + $this->assertEquals('dob', $s->getModel('user')->getProperty('dob')->getName()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php new file mode 100644 index 0000000..be0d4ac --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php @@ -0,0 +1,66 @@ +addSuccessfulCommand($c1)->addFailedCommand($c2); + $this->assertSame(array($c1), $e->getSuccessfulCommands()); + $this->assertSame(array($c2), $e->getFailedCommands()); + $this->assertSame(array($c1, $c2), $e->getAllCommands()); + } + + public function testConvertsMultiExceptionIntoCommandTransfer() + { + $r1 = new Request('GET', 'http://foo.com'); + $r2 = new Request('GET', 'http://foobaz.com'); + $e = new MultiTransferException('Test', 123); + $e->addSuccessfulRequest($r1)->addFailedRequest($r2); + $ce = CommandTransferException::fromMultiTransferException($e); + + $this->assertInstanceOf('Guzzle\Service\Exception\CommandTransferException', $ce); + $this->assertEquals('Test', $ce->getMessage()); + $this->assertEquals(123, $ce->getCode()); + $this->assertSame(array($r1), $ce->getSuccessfulRequests()); + $this->assertSame(array($r2), $ce->getFailedRequests()); + } + + public function testCanRetrieveExceptionForCommand() + { + $r1 = new Request('GET', 'http://foo.com'); + $e1 = new \Exception('foo'); + $c1 = $this->getMockBuilder('Guzzle\Tests\Service\Mock\Command\MockCommand') + ->setMethods(array('getRequest')) + ->getMock(); + $c1->expects($this->once())->method('getRequest')->will($this->returnValue($r1)); + + $e = new MultiTransferException('Test', 123); + $e->addFailedRequestWithException($r1, $e1); + $ce = CommandTransferException::fromMultiTransferException($e); + $ce->addFailedCommand($c1); + + $this->assertSame($e1, $ce->getExceptionForFailedCommand($c1)); + } + + public function testAddsNonRequestExceptions() + { + $e = new MultiTransferException(); + $e->add(new \Exception('bar')); + $e->addFailedRequestWithException(new Request('GET', 'http://www.foo.com'), new \Exception('foo')); + $ce = CommandTransferException::fromMultiTransferException($e); + $this->assertEquals(2, count($ce)); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php new file mode 100644 index 0000000..6455295 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php @@ -0,0 +1,15 @@ +assertEquals($items, $e->getCommands()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php new file mode 100644 index 0000000..ef789d8 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php @@ -0,0 +1,17 @@ +setErrors($errors); + $this->assertEquals($errors, $e->getErrors()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php new file mode 100644 index 0000000..4ab423e --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php @@ -0,0 +1,31 @@ + 'iterable_command', + 'parameters' => array( + 'page_size' => array('type' => 'integer'), + 'next_token' => array('type' => 'string') + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest('GET'); + + // Add the next token and page size query string values + $this->request->getQuery()->set('next_token', $this->get('next_token')); + + if ($this->get('page_size')) { + $this->request->getQuery()->set('page_size', $this->get('page_size')); + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php new file mode 100644 index 0000000..831a7e7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php @@ -0,0 +1,32 @@ + get_called_class() == __CLASS__ ? 'mock_command' : 'sub.sub', + 'httpMethod' => 'POST', + 'parameters' => array( + 'test' => array( + 'default' => 123, + 'required' => true, + 'doc' => 'Test argument' + ), + '_internal' => array( + 'default' => 'abc' + ), + 'foo' => array('filters' => array('strtoupper')) + ) + )); + } + + protected function build() + { + $this->request = $this->client->createRequest(); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php new file mode 100644 index 0000000..72ae1f6 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php @@ -0,0 +1,30 @@ + 'other_command', + 'parameters' => array( + 'test' => array( + 'default' => '123', + 'required' => true, + 'doc' => 'Test argument' + ), + 'other' => array(), + 'arg' => array('type' => 'string'), + 'static' => array('static' => true, 'default' => 'this is static') + ) + )); + } + + protected function build() + { + $this->request = $this->client->getRequest('HEAD'); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php new file mode 100644 index 0000000..d348480 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php @@ -0,0 +1,7 @@ + '{scheme}://127.0.0.1:8124/{api_version}/{subdomain}', + 'scheme' => 'http', + 'api_version' => 'v1' + ), array('username', 'password', 'subdomain')); + + return new self($config->get('base_url'), $config); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php new file mode 100644 index 0000000..8faf412 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php @@ -0,0 +1,42 @@ +nextToken) { + $this->command->set('next_token', $this->nextToken); + } + + $this->command->set('page_size', (int) $this->calculatePageSize()); + $this->command->execute(); + + $data = json_decode($this->command->getResponse()->getBody(true), true); + + $this->nextToken = $data['next_token']; + + return $data['resources']; + } + + public function next() + { + $this->calledNext++; + parent::next(); + } + + public function getResources() + { + return $this->resources; + } + + public function getIteratedCount() + { + return $this->iteratedCount; + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php new file mode 100644 index 0000000..41c2073 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php @@ -0,0 +1,37 @@ +assertFalse($factory->canBuild($cmd)); + $factory->build($cmd); + } + + public function testBuildsResourceIterators() + { + $f1 = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $factory = new CompositeResourceIteratorFactory(array()); + $factory->addFactory($f1); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php new file mode 100644 index 0000000..d166e92 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php @@ -0,0 +1,40 @@ +build(new MockCommand()); + } + + public function testBuildsResourceIterators() + { + $factory = new MapResourceIteratorFactory(array( + 'mock_command' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testUsesWildcardMappings() + { + $factory = new MapResourceIteratorFactory(array( + '*' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' + )); + $iterator = $factory->build(new MockCommand()); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php new file mode 100644 index 0000000..7214133 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php @@ -0,0 +1,65 @@ + 'object')); + $model = new Model(array('foo' => 'bar'), $param); + $this->assertSame($param, $model->getStructure()); + $this->assertEquals('bar', $model->get('foo')); + $this->assertEquals('bar', $model['foo']); + } + + public function testCanBeUsedWithoutStructure() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => array( + 'Boo' => 'Bam' + ) + )); + $transform = function ($key, $value) { + return ($value && is_array($value)) ? new Collection($value) : $value; + }; + $model = $model->map($transform); + $this->assertInstanceOf('Guzzle\Common\Collection', $model->getPath('Bar')); + } + + public function testAllowsFiltering() + { + $model = new Model(array( + 'Foo' => 'baz', + 'Bar' => 'a' + )); + $model = $model->filter(function ($i, $v) { + return $v[0] == 'a'; + }); + $this->assertEquals(array('Bar' => 'a'), $model->toArray()); + } + + public function testDoesNotIncludeEmptyStructureInString() + { + $model = new Model(array('Foo' => 'baz')); + $str = (string) $model; + $this->assertContains('Debug output of model', $str); + $this->assertNotContains('Model structure', $str); + } + + public function testDoesIncludeModelStructureInString() + { + $model = new Model(array('Foo' => 'baz'), new Parameter(array('name' => 'Foo'))); + $str = (string) $model; + $this->assertContains('Debug output of Foo model', $str); + $this->assertContains('Model structure', $str); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php new file mode 100644 index 0000000..7b061b5 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php @@ -0,0 +1,41 @@ +registerNamespace('Baz'); + $command = new MockCommand(); + $factory->build($command); + } + + public function testBuildsResourceIterators() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $command = new MockCommand(); + $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + public function testChecksIfCanBuild() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service'); + $this->assertFalse($factory->canBuild(new MockCommand())); + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $this->assertTrue($factory->canBuild(new MockCommand())); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php new file mode 100644 index 0000000..573fb6d --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php @@ -0,0 +1,184 @@ +assertInternalType('array', ResourceIterator::getAllEvents()); + } + + public function testConstructorConfiguresDefaults() + { + $ri = $this->getMockForAbstractClass('Guzzle\\Service\\Resource\\ResourceIterator', array( + $this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), + array( + 'limit' => 10, + 'page_size' => 3 + ) + ), 'MockIterator'); + + $this->assertEquals(false, $ri->getNextToken()); + $this->assertEquals(false, $ri->current()); + } + + public function testSendsRequestsForNextSetOfResources() + { + // Queue up an array of responses for iterating + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }" + )); + + // Create a new resource iterator using the IterableCommand mock + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3 + )); + + // Ensure that no requests have been sent yet + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + + //$this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $ri->toArray(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[2]->getQuery()->get('page_size')); + + // Reset and resend + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", + )); + + $d = array(); + foreach ($ri as $data) { + $d[] = $data; + } + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d); + } + + public function testCalculatesPageSize() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"j\", \"k\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3, + 'limit' => 7 + )); + + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(1, $requests[2]->getQuery()->get('page_size')); + } + + public function testUseAsArray() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"g\", \"h\", \"i\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the key is never < 0 + $this->assertEquals(0, $ri->key()); + $this->assertEquals(0, count($ri)); + + // Ensure that the iterator can be used as KVP array + $data = array(); + foreach ($ri as $key => $value) { + $data[$key] = $value; + } + + // Ensure that the iterate is countable + $this->assertEquals(6, count($ri)); + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i'), $data); + } + + public function testBailsWhenSendReturnsNoResults() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the iterator can be used as KVP array + $data = $ri->toArray(); + + // Ensure that the iterate is countable + $this->assertEquals(3, count($ri)); + $this->assertEquals(array('d', 'e', 'f'), $data); + + $this->assertEquals(2, $ri->getRequestCount()); + } + + public function testHoldsDataOptions() + { + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $this->assertNull($ri->get('foo')); + $this->assertSame($ri, $ri->set('foo', 'bar')); + $this->assertEquals('bar', $ri->get('foo')); + } + + public function testSettingLimitOrPageSizeClearsData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + + $ri->setLimit(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + $ri->setPageSize(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + } + + public function testWorksWithCustomAppendIterator() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $a = new \Guzzle\Iterator\AppendIterator(); + $a->append($ri); + $results = iterator_to_array($a, false); + $this->assertEquals(4, $ri->calledNext); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php new file mode 100644 index 0000000..083aaa0 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php @@ -0,0 +1,172 @@ +client = new Client($this->getServer()->getUrl()); + $this->factory = new PhpStreamRequestFactory(); + } + + public function testOpensValidStreamByCreatingContext() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + $this->assertEquals(2, $stream->getSize()); + } + + public function testOpensValidStreamByPassingContextAndMerging() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createContext', 'createStream')) + ->getMock(); + $this->factory->expects($this->never()) + ->method('createContext'); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + + $context = array('http' => array('method' => 'HEAD', 'ignore_errors' => false)); + $this->factory->fromRequest($request, stream_context_create($context)); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('HEAD', $options['http']['method']); + $this->assertFalse($options['http']['ignore_errors']); + $this->assertEquals('1.1', $options['http']['protocol_version']); + } + + public function testAppliesProxySettings() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://foo.com'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertEquals('tcp://foo.com', $options['http']['proxy']); + } + + public function testAddsPostFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->post('/', array('Foo' => 'Bar'), array('foo' => 'baz bar')); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('POST / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('foo=baz%20bar', $received[0]); + } + + public function testAddsBody() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->put('/', array('Foo' => 'Bar'), 'Testing...123'); + $stream = $this->factory->fromRequest($request); + $this->assertEquals('hi', (string) $stream); + + $headers = $this->factory->getLastResponseHeaders(); + $this->assertContains('HTTP/1.1 200 OK', $headers); + $this->assertContains('Content-Length: 2', $headers); + $this->assertSame($headers, $stream->getCustomData('response_headers')); + + $received = $this->getServer()->getReceivedRequests(); + $this->assertEquals(1, count($received)); + $this->assertContains('PUT / HTTP/1.1', $received[0]); + $this->assertContains('host: ', $received[0]); + $this->assertContains('user-agent: Guzzle/', $received[0]); + $this->assertContains('foo: Bar', $received[0]); + $this->assertContains('content-length: 13', $received[0]); + $this->assertContains('Testing...123', $received[0]); + } + + public function testCanDisableSslValidation() + { + $request = $this->client->get('/'); + $request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertFalse($options['ssl']['verify_peer']); + } + + public function testUsesSslValidationByDefault() + { + $request = $this->client->get('/'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); + $this->assertTrue($options['ssl']['verify_peer']); + $this->assertSame($request->getCurlOptions()->get(CURLOPT_CAINFO), $options['ssl']['cafile']); + } + + public function testBasicAuthAddsUserAndPassToUrl() + { + $request = $this->client->get('/'); + $request->setAuth('Foo', 'Bar'); + $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') + ->setMethods(array('createStream')) + ->getMock(); + $this->factory->expects($this->once()) + ->method('createStream') + ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); + $this->factory->fromRequest($request); + $this->assertContains('Foo:Bar@', (string) $this->readAttribute($this->factory, 'url')); + } + + public function testCanCreateCustomStreamClass() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request = $this->client->get('/'); + $stream = $this->factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody')); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $stream); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php new file mode 100644 index 0000000..4973f25 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/Stream/StreamTest.php @@ -0,0 +1,189 @@ +assertEquals($handle, $stream->getStream()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isLocal()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('PHP', $stream->getWrapper()); + $this->assertEquals('TEMP', $stream->getStreamType()); + $this->assertEquals(4, $stream->getSize()); + $this->assertEquals('php://temp', $stream->getUri()); + $this->assertEquals(array(), $stream->getWrapperData()); + $this->assertFalse($stream->isConsumed()); + unset($stream); + } + + public function testCanModifyStream() + { + $handle1 = fopen('php://temp', 'r+'); + $handle2 = fopen('php://temp', 'r+'); + $stream = new Stream($handle1); + $this->assertSame($handle1, $stream->getStream()); + $stream->setStream($handle2, 10); + $this->assertEquals(10, $stream->getSize()); + $this->assertSame($handle2, $stream->getStream()); + } + + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string) $stream); + unset($stream); + + $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'r'); + $stream = new Stream($handle); + $this->assertEquals('', (string) $stream); + unset($stream); + } + + public function testConvertsToStringAndRestoresCursorPos() + { + $handle = fopen('php://temp', 'w+'); + $stream = new Stream($handle); + $stream->write('foobazbar'); + $stream->seek(3); + $this->assertEquals('foobazbar', (string) $stream); + $this->assertEquals(3, $stream->ftell()); + } + + public function testIsConsumed() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->isConsumed()); + $stream->read(4); + $this->assertTrue($stream->isConsumed()); + } + + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + unset($stream); + } + + public function testWrapsStream() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertTrue($stream->isSeekable()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('da', $stream->read(2)); + $this->assertEquals('ta', $stream->read(2)); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('data', $stream->read(4)); + $stream->write('_appended'); + $stream->seek(0); + $this->assertEquals('data_appended', $stream->read(13)); + } + + public function testGetSize() + { + $size = filesize(__DIR__ . '/../../../bootstrap.php'); + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals($handle, $stream->getStream()); + $this->assertEquals($size, $stream->getSize()); + $this->assertEquals($size, $stream->getSize()); + unset($stream); + + // Make sure that false is returned when the size cannot be determined + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $handle = fopen('http://127.0.0.1:' . $this->getServer()->getPort(), 'r'); + $stream = new Stream($handle); + $this->assertEquals(false, $stream->getSize()); + unset($stream); + } + + public function testEnsuresSizeIsConsistent() + { + $h = fopen('php://temp', 'r+'); + fwrite($h, 'foo'); + $stream = new Stream($h); + $this->assertEquals(3, $stream->getSize()); + $stream->write('test'); + $this->assertEquals(7, $stream->getSize()); + fclose($h); + } + + public function testAbstractsMetaData() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals('plainfile', $stream->getMetaData('wrapper_type')); + $this->assertEquals(null, $stream->getMetaData('wrapper_data')); + $this->assertInternalType('array', $stream->getMetaData()); + } + + public function testDoesNotAttemptToWriteToReadonlyStream() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->write('foo')); + } + + public function testProvidesStreamPosition() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $stream->read(2); + $this->assertSame(ftell($handle), $stream->ftell()); + $this->assertEquals(2, $stream->ftell()); + } + + public function testRewindIsSeekZero() + { + $stream = new Stream(fopen('php://temp', 'w+')); + $stream->write('foobazbar'); + $this->assertTrue($stream->rewind()); + $this->assertEquals('foobazbar', $stream->read(9)); + } + + public function testCanDetachStream() + { + $r = fopen('php://temp', 'w+'); + $stream = new Stream($r); + $stream->detachStream(); + $this->assertNull($stream->getStream()); + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt new file mode 100644 index 0000000..e69de29 diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json new file mode 100644 index 0000000..c354ed7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/bar.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json new file mode 100644 index 0000000..765237b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/baz.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json", "bar.json"] +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json new file mode 100644 index 0000000..cee5005 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/foo.json @@ -0,0 +1,8 @@ +{ + "includes": ["recursive.json"], + "operations": { + "abstract": { + "httpMethod": "POST" + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json new file mode 100644 index 0000000..c354ed7 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/description/recursive.json @@ -0,0 +1,3 @@ +{ + "includes": ["foo.json"] +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response new file mode 100644 index 0000000..b6938a2 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Length: 0 + diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json new file mode 100644 index 0000000..7b2a9da --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json @@ -0,0 +1,18 @@ +{ + "includes": [ "json2.json" ], + "services": { + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json new file mode 100644 index 0000000..08e5566 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json @@ -0,0 +1,11 @@ +{ + "services": { + "foo": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "baz": "bar" + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json new file mode 100644 index 0000000..25452e4 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json @@ -0,0 +1,71 @@ +{ + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + }, + + "test.abstract.aws": { + "params": { + "access_key": "12345", + "secret_key": "abcd" + } + }, + + "test.s3": { + "class": "Guzzle\\Service\\Aws\\S3Client", + "extends": "test.abstract.aws", + "params": { + "devpay_product_token": "", + "devpay_user_token": "" + } + }, + + "test.simple_db": { + "class": "Guzzle\\Service\\Aws\\SimpleDb\\SimpleDbClient", + "extends": "test.abstract.aws" + }, + + "test.sqs": { + "class": "Guzzle\\Service\\Aws\\Sqs\\SqsClient", + "extends": "test.abstract.aws" + }, + + "test.centinel": { + "class": "Guzzle\\Service\\CardinalCommerce\\Centinel.CentinelClient", + "params": { + "password": "test", + "processor_id": "123", + "merchant_id": "456" + } + }, + + "test.mws": { + "class": "Guzzle\\Service\\Mws\\MwsClient", + "extends": "test.abstract.aws", + "params": { + "merchant_id": "ABCDE", + "marketplace_id": "FGHIJ", + "application_name": "GuzzleTest", + "application_version": "0.1", + "base_url": "https://mws.amazonservices.com" + } + }, + + "mock": { + "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", + "params": { + "username": "test_user", + "password": "****", + "subdomain": "test" + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json new file mode 100644 index 0000000..01557ca --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "foo_bar": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json new file mode 100644 index 0000000..66dd9ef --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json @@ -0,0 +1,7 @@ +{ + "operations": { + "abstract": { + "uri": "/abstract" + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json new file mode 100644 index 0000000..ae2ae0b --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service_3.json @@ -0,0 +1,40 @@ +{ + "includes": [ "test_service2.json" ], + "operations": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + }, + "baz_qux": { + "uri": "/testing", + "parameters": { + "other": { + "location": "json", + "location_key": "Other" + }, + "test": { + "type": "object", + "location": "json", + "properties": { + "baz": { + "type": "boolean", + "default": true + }, + "bar": { + "type": "string", + "filters": [ + { + "method": "strtolower", + "args": ["test", "@value"] + }, + "strtoupper" + ] + } + } + } + } + } + } +} diff --git a/source/vendor/guzzle/guzzle/tests/bootstrap.php b/source/vendor/guzzle/guzzle/tests/bootstrap.php new file mode 100644 index 0000000..28908d3 --- /dev/null +++ b/source/vendor/guzzle/guzzle/tests/bootstrap.php @@ -0,0 +1,10 @@ +> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi + +before_script: + - composer self-update + - composer install --prefer-source --no-interaction --dev + - sudo mkdir -p /home/travis/build/kosinix/grafika/tests/tmp + - sudo chmod -R 0777 /home/travis/build/kosinix/grafika/tests/ + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]]; then phpenv config-rm xdebug.ini; fi + + +script: + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then phpunit; fi + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.4" ]]; then phpunit; fi + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.5" ]]; then phpunit; fi + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.6" ]]; then phpunit; fi + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]]; then phpdbg -qrr phpunit; fi \ No newline at end of file diff --git a/source/vendor/kosinix/grafika/README.md b/source/vendor/kosinix/grafika/README.md new file mode 100644 index 0000000..36eb9d0 --- /dev/null +++ b/source/vendor/kosinix/grafika/README.md @@ -0,0 +1,32 @@ +# Grafika + +[![Build Status](https://travis-ci.org/kosinix/grafika.svg?branch=master)](https://travis-ci.org/kosinix/grafika) + +An image processing library for PHP + +## Unique Features + +These are the features that make Grafika unique from other libs: + +- [Smart Crop](https://kosinix.github.io/grafika/smart-crop.html) - Grafika can guess the crop position based on the image content where the most important regions are preserved. +- [Animated GIF Support](https://kosinix.github.io/grafika/animated-gif.html) - It can resize animated GIFs on both GD and Imagick. On GD, Grafika uses its own GIF parser to do this. +- [5 Resize Modes](https://kosinix.github.io/grafika/resizing.html) - Resize is a first class citizen in Grafika. Call them directly using resizeFit, resizeFill, resizeExact, resizeExactWidth, and resizeExactHeight or use the generic resize api. +- [Image Compare](https://kosinix.github.io/grafika/compare-images.html) - Find how similar two images are using perceptual hashes or check if they are exactly equal. +- [Advance Filters](https://kosinix.github.io/grafika/filters/Sobel.html) - Sobel and Floyd-Steinberg Dithering. More will be added in future releases. +- [Image Blending](https://kosinix.github.io/grafika/editor/blend.html) - Blend 2 images using the following modes: normal, multiply, overlay and screen. +- **Normalized API** - No need to worry about the differences between GD and Imagick API, Grafika normalizes these operations for you. + +See documentation for a full list of features. + +## Documentation +[https://kosinix.github.io/grafika](https://kosinix.github.io/grafika) + +## API: +[https://kosinix.github.io/grafika/api](https://kosinix.github.io/grafika/api) + +## Packagist +[https://packagist.org/packages/kosinix/grafika](https://packagist.org/packages/kosinix/grafika) + +## License +- MIT License +- GPL 2 diff --git a/source/vendor/kosinix/grafika/composer.json b/source/vendor/kosinix/grafika/composer.json new file mode 100644 index 0000000..0f0f36e --- /dev/null +++ b/source/vendor/kosinix/grafika/composer.json @@ -0,0 +1,25 @@ +{ + "name": "kosinix/grafika", + "description": "An image manipulation library for PHP.", + "keywords": ["grafika"], + "homepage": "http://kosinix.github.io/grafika", + "type": "library", + "license": [ + "MIT", + "GPL-2.0+" + ], + "authors": [ + { + "name": "Nico Amarilla", + "homepage": "https://www.kosinix.com" + } + ], + "require": { + "php": ">=5.3" + }, + "autoload": { + "psr-4": { + "Grafika\\": "src/Grafika" + } + } +} \ No newline at end of file diff --git a/source/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf b/source/vendor/kosinix/grafika/fonts/LiberationSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..59d2e251b04ce9540c1915c5bd153cd1a709390c GIT binary patch literal 133828 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%0A#4WO;h)-5om@Q%c|Jj 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 ARGF#7{pond}<3 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

    ^v)J+KBFjPXaex$4uUdO)Ztb7Fq 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�cT0OLpS;$4Y^yq^x zG_FN+yeVHa?#?F$CRT4;0610|^GBQ9dUfc?@>t$Ll*rR)9N$M)UE@{Cf?4u96*~^laF`+~$w`R;c!EAy$k0&+5JBxI!BzEikeVtLz(-3#Ok<;j{4-}%^o-@0mu^dq) z*P~-sugm-;yKdcrbeb`9;%?#?MJZS92HEy(v=YAjd)D?Qq^^w|gl$8|;Qhn@pmZ_b zJr{$jlnC%n6RsaZpn)c(D(Iz2JpWhpAA0nrwC%UG!W6R%z7ESh&Z7wVY7RGJ{o^0c zKacOZ*B4CenbNM;UaTz$JJbr?fWEwOR&F$R!wv9lR(@`7KD3tHg!3JBWAVRK$Jg_7 zbAd*fFuU#!=+%jWJuw8a6o~XsSrvd!%mH=tBAJ>L0&EyI0M(%sx+xE=&J*92RgY=?&iK6NC)V%oOO0>eUo}>| zUHNo074sr~pszDMs&rX>QHx!f3$&_DHj7EE*Mwr}cnLBgF}uxafI>wEGqiH2IUnt3 z5X`2uueZ6BkV*np=h!As%&dcEXSoLgsJ>j7*t%$~Wyi|ecJ#Wf7%#UsO}EZSyjU&- z@#rg{SK99{fY*<`+%ja->59TB)rZ*&EH8}OR9%eJ5m?tAAX=zUBuzxvw5f;W>Td&k z?pp>0l5tB^SXm`fljz(3ZY1YM*h0bKBca7Y3MQ~>S&`Q0G!};On+Sg5mWT!P- ziVqOZTuWy!<%zsW{&|Gk0z>1NYGf>IF2FzXZJ*Af&$R2ib9$@ZAO5!C0{x;On;rum zN&=2>wvdV^T}*BmTH11*5?ge}Xp+sahMH77l#vC9rN|O~6CNId0RLOqCT`#LjHmlp+7KX{~(AXm&}pBaAkUjH_nNeGu58 zHIk0kwxBmuO1&<==Vq(NCY0dHc^{nd!_d3JfNTermGmq)=*oslt0_51WxObxqdsE; zOHUwroYEQq7`U(Ss9zD@XIr81|@%+edVItC7>^H;{ekhvZSB>G%)H zF1^KI^Eq@D3;A@0I#)M7*NEDXAgtuGD39;x1=ySMWPiEb(`CfYHTj1ZXAuM|-(Y$% z+m*?5Wq<8<+nqMI$>rIL=yUaBm)zp=bfA29d=O+Q*0&D>O^YGo=YhFVl+LAzmR6`2 z$Q4Ol(C|!qG2pF9EtD>zWy93Qk6WcM!)R`Wm;q>4m zi)wU<97t=FiI-EsM9Q;t=D_ugh117EXiXMJrO?<7>0{Yrna824l|e``5~$Mz?MI)P z51*OY?FPoz845MflKWy z(*Gh-5J4=UV&YF`wagULYUDOp04fW|Q_hduSX~7(S#!iE z33w6{kO_p><+e#Djj|`HcS0-^4 z(x%JqYBZaZsY7XUJv+xX5S58PGG^8e%{+wei3S3PUo`vuHvh5{*K4o2yWtQ7XdOn% zN5=MA4kFJA;7i<-1cgdaS5!a3*hSKa_CRBl44RyB{a*(wMz~BA-_Z73B_f_IrB$4t zc&8l8J0qA!e~i3;`3w0KF+hI$&O0bt%YXYk6NDcNSA0in(70MF^{7%t^7Qwn&aU=a zW_4yPl^TN|)^DAk)6;%_W^xgxT`!#9k6~Ab4dQ-jYBGNt)}yfUbTTCiv7Za^uY{D& z`-wqvg84w55T$SM*- z$@37qmY~6NVR8)ve-iE3Zk@WX{4J9xL^hBoFn+?6khaXMy_rf8-kDDn6NSX3W2Skl z=Wa(kpu3dMWplPAYdgLLG%zdq$7TPX{lPHO1*$NT`ot8b01p!AX?YUjA10Xflm%O& z11#BLiidGnh`!-)7&gj5YT<*$GX@w_AsIGDDx(S>C`Ai)2!IacJk_O7>mblYxS9}U zXZeyE^e_x+01N#I2++v#=g5~(;(ELD)nGW-kL=VQi z@oYy^m#Lhqoo7^7Ob+ZnQWlgV1L_^yY`knynZa5VODR`xu#NW1`e(Yjhtw|Dbf9-F z(8h0pHu&J(=YT5aQ1c2X@G%N-I*AhPKmgDN?!pH@b=-g{Ea@r3edB?Hu6DK4Y}>oJe$uIyjqe>Hw9hA ziK2t{bLfLZdyx zSi?e}#u^rk*g~5u>Y@!5Y^>0z#2BNn2H2u7N!j`|LhCX0I`ZgH$z-_{iYB^~oe5MP zueR^>Iiy1JyJrp^x({F@w-BP)B5j&G z@h*+cD8}Z_oJ7qNs$ECgp?nR!_tj5f%~tKFU)5e2U$bQ_tP}7P+u~~ziM8=7mNk3e9nAI4%^(xmuDu0jfS46M}HubkENQEMQ>{M!A*O) zu+E*UkQ!#Lj^>h0{A|c4)uv$P>ClF?#Gq=V(wS)<>gYG0xZcp#4Z}YBo`s4UnE7aj zobJyUZm=Q7rmdi+!s3~a*HS**WT>Q$#VL3HThaheCk-JerI<=o)wi->)Pshissd>N zLY)CsD-WD-k)*}d7+4tLJ3Skga-}FP0fxY`m{wJcn}ZGs^w-KIGMP8w&bxfpj8yIj znDiQjTr6tHG&VH+G9~S;%q7ndEcF_RB&sp9VbVua%FrKQuI~u0Lxrc@-spUlKq`#s zwIn)ObJ8O zL~{FZ2)ZNW2AC+LiU*=Gf3&dUxGNE~$3wYfKAGZA_t|o9|8&K?%b6U9@MKUVW@dQu z6D)Qh9^!f`ZHa_D7hzOSlG9dNBN@9wHA}jK}P2V~;2mVIo%h zz(&AMVhb;xUa5PT>?2!6{a@0;-pMPz(hxU z-83};$5S^@`ygI{wlJ&XDo=`HCLL`$MFZpmznH94GllB;dy>zRf97z^SMCY1*$pkQ z$-Y6OF7Lj!qLTZvYoZJe>r(Z@ zH=qqtrOl}-G~4&)AkqqOo6q0LxCd$|0{}PPFFbc1ph7+9Jzo! zAVyEc6#zyCpz1=A31fd}>m^qgiyOUG>p+gdQ0d5xXd<5+HK{rd+5$TV@+Cc?3hJ6cy@MBt`iCi zk$!zauTi6YNqZomniY1{-el~bxD-88DkggydeT)aW&l0|Uh1C%}TkvYv85 zslxEXb0X&;2I1j40(7NoPH*kD>7x9 zR)iLpQU(DR=E|eDi2&YU$ztrHP zAD!M0>y?s$WH=dFH)~m6ymM@D8T2T|ys^>Bvf0aKj`t0f(CK;ITDjWp^{+Z`ndoS_ zi7a3gfM18R2a9bmHsDA|q&^}xwaLSIj+#R>yN5RZ z%IYh^Y?Iu4uUgJ#a{x0^Lv2Z07&aGZ^)|FgA%XhzE{LuB*B-lL?gC%2g2cslR*)O2 zprX8?gs+57jSt(F4U<2%C?ip?dn_DzPs(pb#D6Xz$=MM&D$DKIsBx<^*$VIpj=?;@ z@Iar12K;yz=pr!M02NYt)~Of*rFfYp)%`{*ojUy!->r~;M~XKMNbnn1D6RZdy-d=Uxl1$!bT6sX9*6=$w*(T@g#3w`Qhxkc~`o0Tjw zuAJBiXTyT|5YIrIBo0=+9CB^AViDI71KMdfnlWjmQx?Y5q0stqN<&^a7P!~~T3JHN zU^48p9+(vr42PD?ATKL*1v*<|%bqg3stiyCzUZ(`B?b0maVjEx?S?`)sZr}qXm1H^ zy18(wL|$pt=JHwZ=4}HrhMfH~K8Mn1#3v^t9GpjH;Nf9bTc`K1JhtPgx}(^Phd8of zUcvJ)2N72e4Pe0;1)(SY zX{dz>L(NnFv=OQ!9&2?M&LXjnWWb`R8IWX0hakP7H24cu%ow@lu$5Cy~3X zutZQ%9O!7yFYhVZpmTAqGpBPZ11g0i2+O=4*FwoJG|ozyCVSPeN8NJePU+rjJ=Juy zWc>R-$GV_44J0n$3F{!7dyqlrv;$(hQOsSFBiS#m&8S033m+_lfzv2FQHmz8ve?Y& zD*DK)(Yo^C+5vt8N0br>)Ner=(G%-&TWZ%p1Si)h6)IO02}A9@VL_YBPKb)X{&jJA zI$+631Ca&fuevJPHp~9j%A?id<$~T)ydSvk+KJV0onUuY;f?@cpS1R(Cr zQVEz}ELKIRsqJVFr;Td8lbCmFsdQ`Uy6Z}{XVN-<9Z&1hgjePtYMUv7YFwZ6TOqMN9<3`ynP9_$+A@%N7)g zu31YRMHryCwD#u`BIe}t1AJ-6s5lrJ9}+1cb0^$a?I3fE>n5Oz0$-CKdQ5)`bmoLO zX#(;*;4M&WP6rNv)RHMm7t{6y787s-hm`6|OVQu498odr$q*d=$F3*EEsUF1^>)P{ z+wy1I8)Gp0qNtaRs$tUD5|Mf34 zFSl)54y{ZZFaSKk2WTmK)2gV zMfKjJ#GMa5_#i%Ode`0&(Bn7{X{9P=plTBGGGJX$Qz3BpKzhNt0>c`E2Zc;vT0?RG zCVs%Z`|i@I^6yF?)b3+2An0zGSVmlpS9_2j;p<~ivKiYMX5bAU%^RvZit7PY z+77ClhpHxU{8vTtFxpc5DjkUXVB*ZH_=?cmV7SBeVSG8E2Ie8~C%JG%Ici@S?$n?x z10Voubkme`f;}$E;lkUlr}Su@Mxd37QXO^NV#2!MV^SQ&SYommT%K0Dk$h89sO*Tg zgo8#c48i#Reatcorpp9ObILM#ISnd>4O1wqh{0ghPI9;cksvM+3&cMyy#mAa1!)*c zD3lOOEf&4aT`(I@KY8TDx^T8yY%6zF`-)@N-!@#V{SMY*iUP16eiKYQfVsyKbwFn| zJCx`qmChHoD||jD1Q{3yCU&5o<;$5ie#L5N0i$n{hVGV&RCpPV2&0M;3P4qKhbvB;T%a(TjM25X? z<>I~4)?^{MVe=2>%x|0*3e=(u#G7+!pQo4=aH6IJ>oK>%!jC=zF`b5Vf?{ zi4)+(J#^PY4Pcz|x$#sefR=@!g%Uto7E%;I`P=Y~;vkS87!hDh(%{oBDtymFCQvclA1qHgpt*2YODO3dof{kMZCh8^I#zn*5%TYawS5k^C4xR$F)WGbB%#>g zj-$fPO1h`cbhqVv>2zHlJPNRJriq~A(*Q8~@mbPVA`W?_XoDC>AOUDvDMca^ll6)JEO8AdP%p94i` zn55uH-~=YEBgo@QAv!=srzt57ufr0v1a)@U3?@$heqckgDc@rmDYt{2(!S6%+T4|0 zlrH^){2FnKNINl6M9jeW>deIFDiH`ctNnK{-V!l6EP*6z6pDxi-;bPI=5ts~MW@3P z3_9%&=&`lCm!2(tzi{1kg<6)9D1SIGZ}t_;o(O-|G0x{os?{IrhbELVmxvyoGGh|O5 zf9=8XxbbHUzN9ne6KJ)wblU!&WU*D$Ri30wph@fb$AI4|V4u4o8{Y}IfO)r4-k^zp&=zK*ol?s8Ax}QJB?O=i%=I z?Z`ll|UMvRz@k76>$_iJnKa-Y4&{vGr# zIL>SVn@ie+B26q!-Xi2QHj2_xE>|K4@jBD~vh;oT2eLu|4d;=kI52l);j{DBpobFC z(Z;7{K>VQ6BJ6|~{VFYO7$z8z<{?kvkt|dU@CN8hii78kdp0QV3tJzPX=JD>zc5CX z%>j=GPKge50F}cDVi8K5uRLqD&WX-5iP+3Ni^8!$+O>NA;AZqp?M{a$>66LH%d}!C zKdTO|D5Cpn9}}{~jZ2qr_ibAdO8c7729XHHw>9KsBEIm20)J_rVbl{_*qJP~i;i3@ z`#mt*@G;1*r9kTR!PYsLRS%J>$@x%|L$jvBu9M-{$)A`nDVn2WSn(;s!=y(ESCC=f zBG_*ml@w!BAHW!aAeKffvd6@2E|WUR!|}^mZKFe1d;hcML~2(57SHR;&V;>@bSUlc znk)INR3elTA(qq$RZ%_efL{-5-n<_2nArB&KdMgcQ&IbAR(D&X!bx9uMtni)Ou?7Zo1ZSl<~Qr=J?5VX4NZA-K! zjZF2&736*p7HDZa>B0G&Sjy|PxB9JSP4HDIzv;D~(_tAx$gG%o(cL2y$m2KzT zX*)Z;-RZse-uw2xo9u2jJ=r9iN)nO~2#^M;gb<3fkkCOeph76p6ckYbMNm)yK`DNK zq97g2T>kf*nb`zCKmR+KEwi(md(L_5^R`~!FA+iOf1WGny7>FHVZDX>?>z!Ig9C6T z%$c~BVxl3*U^V#BM3_Lu5;9>o>MP*XPbZ){1KXSXa5}x5azLhWsH+~iG~hJa?E|ZG zc8ArQ41hK|x#xT9;^#boYI54!n$3jxGVUv`WfQxN6<^#r;<|+rv}$yV95CqI73iGh zeDHjmxm~FkTjp>GpwV7yORKuJqY8bItLV$6x4Iy$P+yGIZfh^3%hqZv(?uoc>~UYR z?0J}{+y#@sjETujy%lRoE>)@%3$s6oCSQx&{0>jYg2$ku?}ev~&_0tbkWbO$I4z zyF7p_*+C8`A$M-H;Z5V4C)x|{8r%e|rHPG*tty~}Q}%OHC;flU`N*@Y2EFFKsbBeT zTD;N396-C-?GQr0joIuQUH>?4yO$5wk|Nry-$H>~j*^Bz7hVqf_@l6Hm z4g3q_Gl1s#j+S$7o z_r(0gfp%wSArWz%@iU#&&GAWHX600bG5v4l{X4 zfP9$lz71*}OT>D~H=O{0{R+mE^4ihuYy7oq2< z4D$zw4YAQU^MzDd?vu#7ghtaACaBcKWPG9E@kYq6Qy~taD=s}}=bHR>lrEvHU#S_* z=SLErbdlMmVe^c(jV;=1Yz`_gyH^%5I{me8pC0;a@b?MeZ#}8|BXh9Hi33+)BOY@B zmhwd1VSWZY3@Ub-4G#X3EjtpQH@+iq!R&-54AtaPv1znyFGGO9?$<2u9S*pvV>2o3Zska9pFFO zi=O#6$78JFkTk^?tOC>AZ=nWX4S8;OA|xQ@WF>XZY)G)W^^HLeE~dz4JodCmG=zp4 zqzJkU4ir-&9PTt)!(-jKvCC{!o6XSJy|mu5xl*l`S`yX1O&guwtGC*`XeWekxU?!~ z@Cl-3Cm-+*hS6$wjOg`J4Y$&&tDOJ>)rnhwSbm0tq|w#o5l7ITO*(w`u9@k<-hqoW z(zV^AFvA403WU-u`f)Cq52R#rZlN`)ap>dBK^Qj5W{Whd3}=`r7EtYg*HydibfV>g z?|%d9BCtu!5QD?@ow&2i4Cv{7S|wxyio)gz$<%@I1fZ92+BfdJKz8qRE+qTGT0hTZ z@}xxNpwVLGNPi+Nl&-R0p3#d%+&@zW=>T(k3H`nVqqgQV&_o&X323EE{>w%}0c4nO zh5Caou*!6^Z#y_LI*{q>vj<$6;-8k+UV${TyO?pb?kjXV$t4H{J(1cUPD>`(eXtRS ze}M2}eG+OtvH-R=PGkq;19%cYFzMDEf5MzVsDwL-1wM*O0Pw+7t+FH9pKOcJ3c0MH z6$#LHYx^atl$^y9wlKGO^aW6^I07>3#=;c^=40x?4QD$Rt&e45dBw2HSX@3nEG-LK z0*;G!?H3PZJGy}y>+$jw#2g=i`J^~X69AMJG%3P&NFH#PaBoe51g%dJ2T1DY(Yb)uFlKuM;wD&b zIw7`~02;)WF@Zi#nTbTaG_a?J)dWX|eLZqwr+}xpQw513)V=Lbd0*-kdb&GyI(C$^ z*4pGxiq9^uZQ`(aErPVFwT1bCQfex+!Xyc)5`qf0OL?+8nD4EVwIJnhHm5FqfA_@b zP@-?BV{HMsAA5{>xp1Rh8Vbjx%c9}I_R4Ni(BL#yJL;l^x^esZzx{31ZdeW=sim(U z0{lG$=jw(S(3}}(Ku^4LXY6X&Nm$Yl>ccvrZu%0SXWCc>#1QNOcoU(}#z=aAUdc+a z(LhL*hWhcC88txs(Z4lt5ocEiK%vWx{%WvA(H(kN8gaQ_N>*BFSeO#7N1llru*YL zE3LPNv*rH$=xBc#s?w}rd6naB)>K7rYlFs;D%;#b9;Ag4w#XH-`}0~`Mi~o+E?!{e zw&Ip{VA(LQ&hPg0&c079EByII5Op%y3i?8oan)gUN3~d{{&Lpn_@-!2NpBh?xuo^n zocn;zaNh!wbMPNK7Z42L-|$S^<&{gAcc_l{UwY}2+RbvMJMLF1m@5KwQ7n~cSCsN4 z^dTdqegH}F0LmXKp-Mgrj44N;B^20P+;5 zwl0vS0~B@oK20Wp5P*lw$|^H=Rj!7tQE^!#;@4^~bGQbDBISn`%n&G3i1HYZTvwus zF!E&_KbOLM&v(5+M_x8JqOWNvgHX~Pap~tSSSC>}Zf_G06jL)Br2O6gDKZ}c9oP5k zeiCobfL4G29}s07{%s_2Vq**pw#K7D*1)J_A$7q@3|pMPR9_kqtjS31hQc2-il{Zg zOKYV1>k3zwV!e^ka$DezC&c1bS&`PG6}9znSD&1p7LAV0nj6 z0wvV_um`~IR6xw5aRPw1Hfu+w(NzusFmU?SDd(#WZ^Ic--+!vYe7Eu|TuoYqQjk3Y zfhNsuX$3TaBVz7U0bKy{wW>lTVJhP&WqpwuZf{5TzF7LrZ%S*k{sl6P-pXZbl!>vP zOtDvZ-8pc3IFW42XIO^tNSx?tI=a%4?^M|2fOZo_Fx%Phb%yG z5yRk=2E%U7X~;#x&%tXT+DuLTJ>K4Du~&l=)yXQe1HI`i=qy?XVo&sUb%rK}yTfxA zFM*x`dmcFXY1vFe8Pg^3A=*WyP5 z?X&uqj;vT%j&^Q6_YAB1(Sx;5$!@>gSxgQKcOSMU02zQlQJc#h3HxTsz5{FJ1HSPv z)EVKZod~!!+NhrD0=sdB0<0gBHbKszu8-=v`E;*#+HoV|xJ+SdquFu3`S}F3d!|OF zl)L@$^Zd+izDT8(`aOX;Ay;wBFU>?13m=~);&NLcdTOqnZPRG=iI~Pp?fWZS%*$`W zh5FG0Oh1fjfc9+ZjPsVFLm<%_{Rd^e)l{ac-k$ED zesz9+KPtipOmkhJw;qH11r@|etUzt;^$bQhNHX{pFXEIR1L}%ceF!KJjvrgUJg^!Y zz;qLqCX;Vr)kGQL)dP95mZdz*FPWcf4{Xj{Qbd2&Xe>r&kJH#u6%C9=Lmo${#-}EK zH$31R2|9OPXmhkF>4{0`yWPTKxAScmN$k95!3Xr!_J9JX#DXe}x4X z=sGg768dOYoz9KWvkmtg;z33jMx5ht`VBeVuzMR~8A~=IDDVb=M#mJ7Ypfb!Thfyq zZ+}MuZX(v4{%I9`goW5Xq`P@bGRn~MenU18hCb@Xs7MLI2^=$jIMpc^Dy`WG= zgaYN_Tn@sRi_o&aq6hAJSTlQQw3E})A~Xg2Z5_iMSVcI~0IUO7i9z%QI$$*(4Y}P|4-~)>Z#xe3jfFQkW;l0z zz}$a7UmytpB;hx48)H0L??I})_A5x={vtmRS~qOB>1|oQ0JJqFLQ&eHw|)q1Y7I(> z%NZH(WDaz6db%CT?VtjdNRYnG&X^1g!Ec8^G0dE$OE2YWuMVeJOBI=*I| zcGD*6gGw+0-Ydv31LQ~H8Qd7Rw1W&IliEOZHFf}pTgZCN&74HA(uk*yw*j<)idMm9 zeA7gro;5@3@~l=#8<|%{ttz!H7t_$^m6$h?=@U=_HDs#okeQN@#6ZC)L`f@im&aI; zNo1-;CA4=aQy!3y=h}J$#`d-@`l62RbD68q+7g1;A+x7+*W@Avrv>NuXmrY0czF8( z!9by-qmg(EYpUz&M?fx{fp4)5jcFB9iJ?}7952ovAgWshwJY$PMjZ&Oh>x+Z+4@lE zkAFPoE9BQDN-aXw{>qzgKL0$TYG*+P3SW@Y(98*+Aq#_r)C)?5(#NG&Hmo#hTR>=Il%j#gBd+{v$7;RcnB&L&uBy&fW{hL z_BZ@ah?BrvHZ=|BLQbYBae&_H40k$;3=~L)n{et!q^9wV!Df_fI*SGD5G;d%KX=cX zw;-*QqcWdAtXAoqt$wpgtHhbqPuUktnE7l$g)1<2gBT9l!zXV%HdCif`xA+r`hJ_$ z843->L!LaXe%(1X=VA2J;4mcDT0(1QuHHLYac9fzZWF&nQ0570%k%NHPAt%ykQ*d8 z8-^F6jaLu!L9@+vmv@ak67?a^ImjB|q{Kh$uKPCJS2AP+E*aXSpz?_ZGzp=J&IACC)Gkf&P+llK!MGcm&qJ|QK+*KuB6*8f!@H({L($8 zg9qV*9WxHgJUP(4f_dbuZ;1!mE}U4lNcIHwG4cBDg!SS0BA(-g=Ku}ij6(;B-kmC$ zAl5lquAK}wB2obR(O42Fp@y;%O{%XDm$yOlz&H$_jAPNS#xJlAYgKWWaxDru@C-^8 zb)>XP`-AYE`s?(2Z@MYJtaQ^&FUe}U(hDMPD^D)Q3#CfSmff~u1@ohs;>CYZ$TD7( zUA8O;qPv5qr(NbSa|fFH{DH_>_kX>mcnMtZ@82`?C-vMUKW#I#b>YP>*pE_Ma{G5| z8j(Kv9UMXdITwPupL+qXvJ=k<$Q*nUs4gYb_#)U0&_5(ly95CM8Mf*lss2hVmO&qa?FyG-_umWz6@ zEHE9wgolQU#u*OF6i%0 z{^9)+4AuE+@k5FEMWarwW$rLugy3r2dD+_G%4*s;~A7HdJ5=(^?)|xSZlX5 zDMzZA_`QrzFQ;|sm`;u6FfHhd3qeA=1I{3G2xPEXk9*C{%!SKVx&6sd&JB{)C~~{KzxwZ@71oPqbmQ848hkSxyXdLUihw+8%ztn; z#5Kky7G|}&R^ruV4-hC(QZZ~rU?~jm!uSk6P7))+rq=&wLs(grs@U`$ih**NCN{b#z@>{m#|2EoTZlAfaY>hxi!FXwQtUCbV@TAe`5Bnm8t9J$y%(bAt zCN{cYA7{FJ|9uy+emV*^b^<8IMX+bwBWDH?&}56Iu`vXRKX@nW+|e*!h*-vU0N$*+ zVggDdJc2<6*3CCud-xX9jXRfg4p9L|HraW>&CD|e=4Ir5^;Mv9WMsmb#jCT7mk*9R zumO+5+zo@ldEg)uUV$`_ihudyM9w!pv@_G&SxL|GFwY!6o?D$=wJN(hM{S?%9h=R3 z6pt*FF4iiv`VkNG+9@~Kgehaj0Ud0IxwDV4a^OGOKo3;_4O|9m1p5mJgDeWv3e1Ed z78tyGs?G#A7-&cZ^22HV!OQ(GP9{-R@SD_bB(44ApLiNusI*Oqf^V2Q64e|^g#k1Q z#zPAi)w+r%joBO;XzTUnHCnkA#k_i_3o-|}w8U-hSoI5|7pG4MAL@ZQXdGSzDyA+` z2@HBAfQULf`ufs*{?hzjz)vdZC32T=jM7P3gP!>l)ySe)zwVgXq(K4gxyTyIX9F**clDy%+ zvJAmxSSx6`N*PQ4<~eku)?RY!(8VXO4M+UGXdvK8vZM{LW|r4?3ax?i z2v6z1!Zm2pNb5D3oNH}5_(ikJcpjz$cVzOP1=ER?E1wKynPUwIXI-`cwMKOeC+P?V z9tIHS!*~BPhyw({f_`H+pkIJbi}4D&u~+G4K(byI4BLY)7Q>+Tl*jNtT{SQO1TAuL zP8~wm#>1Y3YI1(<1JgH|i(lk_ z{p%~QdghtjFVMH<2jiXIq*5XmIfwLWqwep9*s63^Ed%)SA)^oFEtd3H)vO+|%k*+H z^x%<<_jp)d~U*w?(h==`HHWlpSWcW%%#PPAab+qiUM#+guiHV!j@RUm;#DODZ&Cd!+%^QBlz z+Ozy_n^AXSCuG$8Wnl5l+1&a3UOr2S-dwe|lIiZuCo%6Lm}_~QbsLL~k$;TcLMWL; zmg|49LDCpXJ@vGwRJ+8 zy#fGFT5XiXmES#f}>eyTW&0Rk8ed4r`5btq?c1k%t}KU&)g zod)0g%B(inl8|NZKq7maNuS9U&sKc9IEW)nGV8IRrMyJ}IoIBp#8>KM7R zo#I#1?oix1nT)HoQ{j|u?V@V!^LA>W_W4w+@RE_TX|THj1j&N>&7-XEfjorn)L=`@ zfK9a$^k!O#g7JV;6tH1?mg=O-|WYr z^W~uP>r$(!$R*g+C7?b0_tOv7(cNdRow)UL&%Vv)FHUfk^OvvLe{b!nwzjXj{?&%g z?j9eR_t53J{>7`#_qD1Q_6>E_-i^o6Pnmnr2aF7jFa=n(6>|803jb%I9z6wvv+;l8 z<2qGr4u%yx>K(Bp6lO=To$9@CFCt$T}4{sr;G;Xm)4!DdpE7<%R1e@(5#6?QwutB z9r^ZLIc=HRmM*8;&?WS>4JB+u^d9Supq#Cj%@Aju*E*}9LyW# z5+NkM%Ro#Z)_d<$73!l;ake`fWtgw@mCmiTTwB@;qfJ_rApEyy5sggo|P+vZK!Q`w2#hMwm`OZ3c;bB!@S3zsUbb$M$Ukx+y z(u5{~^%0dw`8QvD!C~cTXY7F4Ob@~t zq2ql3)Z==X=12T*_Q=?aHq)~Zvz zg>;}4cjTe2@gY<`*JX1z0-Yk%slXKI;}S4Srg_Z1Zii4u*x%)G}OLT9~yD7EcprI(AteiyWApU~>V zUVFkeHdcHY3eTwgthmkn8lqG=n1Dv^8TZV>n4}bND;!ZK_kMmPcYp()tnQMC& z%~`@MKsPTNo!y)1o4@3|x67M0mEXQ#NxTmQMhQi>bh~}B%ysX*6<4_+&2m211K3?o^>pI`^kK8AQ*S`Fc`9vhv zQRN@~i21hQ-sM!ntp#Yywd#T`7pi*a&oA^pGcVybD+hU1$d-<*trX&gg%2()RpM7D6?D(U_6?+S zVwo14tv6q&q z+ZLHGzx??F2Y!C(rBt$g)MxY9N+BmS*32CIonL__+PgEQLVv7wKerRa(N)jE^*Q>2 zSw9cmdk9~3xq)f%yTCs(kaf@$70YJ;8i1&X%K-X>RBx+kBCLrphIQc|7ZIMHQWuOs zek!LLtRJFl>Kw&d!l+8fsb#YjbSxZDAUX3Smuu>t8vs8V%+nT~IzO+ss8ak>#0)M~ zfAm}9nS~hYXsLCTA?Tu|lh+IvA})%xdi5Pn9>-{~<=e$~Fn|1^0Z1Kml`C2_#XKpw z48EZ>s@(ndqVsaOvJlB+jZ(qYp%QZ`6%eUiA+zgMM5!;C0NfF;^-|!wX2wGmj6Gb$ zISmvy{bM5J>j8jzFL?-eVu=AySmbHV(qKbaD=gYV$3p(Po?wfprG;5!&=?)*4V^h! ziJk?onJ0Pa#S$8Uh5#?V$c&eD6iWbc*`aK~S_->k3W=;wS7*o>Y(xZ6wC7KkN&=i61t#yXwcyL48&VcdkUavxCywg zV=%KmO{{0k7pKi%s>j5e-F=#t7#0OqXiW#Z312|%8$mZPN7QvzwJI7}2qbgF!RbRv=AXYi@2?ckt`hr< zWRcw+c@0=L2x#dD^vzGtn2&W1W_7Ttll9_PpdOgxN~i_f+4a;>lCqt~)P}_a&#i)1 zSA=NDCy6ZhYLGqd-`Z_~8SKJYS3 zQ>@B1FL`H~$>Q{x@-B@c(U-|ta!~8Xb|}R2BQZlkZPcp()oWR&2`n#OL%lcJSM4x7 zEfgi?GKC`1+A4dgz~uAH`kwL90?0Y7w5;)2Jywg3qGWv`qigH-Jv&u=g`#yu##Xxn z1__=cf_b@}Mu!Q8hHy8>0oafQUtG?h!gmt zxdp&e6#kB^y^+oc=513B;k-Zs_18^XLMlyD0wQq@qXPc{)}g@#D;D_k{EA6#_(iX< zY^QODo<03-0?7O|xmFpqCX?)%3l3jl>^0l>d!bjGB^Yp}4TfBHR^RNA+~8q7m!+nI ztJjBw5{g}p6+9c4*F0qrxGnNzUpi+g(6mCW>2wCd0i!~riew&}E#NX=&u{7GLoyb{ zr`lkUAan~4!iZT|LckV=>t}Y=k;Nfm01$4}`m*TbK%ubeTpCn@JmPqIn%|ypNbfPl>v^8UD`7-XWVY zZjuUkd_HYVE_Y|Re0k19)2+BM0W{rIB2+|B?9Kcf+VaR8cD+HIZB_M;M!M{3n_Xj{ z*|EgKd_6{MjrPHP)`FBm3JsmQxxJuKtLR|c=-eg7l3i+%>A(na-1IB4)zMzC=*lx~ zK69j0b-P?{y{oh|;hh-Bj*LwT+XlwhgqBA2^Gf1@-clxW$7khuCbDutG(vH$6y*+D zJYOM!a;U8Uc0HaYQn%~%kRgC`AN|S93kL{N1w!r(W^3qfU_1~q5YT-+55v}Dmv?35l5Za|+Fd61kt1@mK9c|w zBX2V~gQ21~V!!?FQl~;F(NZ_+uY6cI;kTLXQ0Fa$87m8;Hi?45{UQ z$IuG>pucC9rsSaML~kbP$K$yr}A zW<-)&M;H+=6B}gHk$U_p0ka#{OI^akCV{V4*E`J^3Z%9)xtMGUp&XExm>Qrhzd^0w z-NKVA&DVzgc6%t5t-1zsX^%$(<1IN!Gz>{rBC%3s>{!X@G>V`*%+u)=wElr(`Kzva zAc81|QWrJQK4z>4qz2hW3VE~=3D|OOvp5ZSCC!Is)n{y4y6%jr4Re{@Fw&`~x6l@i z2c-uWGol>_Wu0Y%3rK|m{;uof0Oo6gmo&yVK{tEY9Bf;h{=oD@ zxZ)IELf{`zA~#(%G9Ec^&V1p`Cv<1-*ug?yGCjmq#qR2-%uh7YnZqt$(B>QZR?S&- zsO9mChx5d?SlK;${*}y!MklnlDj1O?k8aJ4zd|CSbyn*xEfREl!A`*hhP9H!^UUWt3zb{P7fX!KEv5KEl_O}d zo%~WBDy*vgU=FvP%T`VCgGp}+nkPn-58N#brd2vMn=6=~4J)6e?p_rLCzV<+vwF?> z9G)ctviNU2X82qN2|41E!pogKMl*o+$vuMgP$@s1bwIUE46WZ`RsGL zlMedo%vyjH31Z0U1?0_G`KIs5&#aYQD zpAXuyW3C5+X)-^E4dPtOSa|oqOK<~eVa5Q0O2bbGvW+;VF-rC`GIF7B| zI47)`J+v$i{>@A>=3(Q`0zL%Mej=!M?8!FWDXjdtlOQ)bY;#2t5PbP=vjYKbc z`5nkdEsXRI%<-u^v-vdi&|qF=?hJwYIy1}%A4}s?> z?Ezm6$8xZ~4mZ6U?9o8-Kj2%?nUE+3Ec3ubue)Za340c#-4&>Oktv~{JarE9()oAX zk^T1S2OnhYMY~!V>r6trixN&|c|OlteDY?Ny*7qk+s1r~1lzLboRfX<8sG0{BYVd1re)#=;_XZTCU`E(dn60>&b(2Ksh-%H{zLZHqeha>V|h2=$9ZU*gFT|2rxcKM&No3q2L_{TLIcz z<3XLS|kAOLipiyqX7O2$?jr(3#WBlvoSHl&6LTs&_5Um%o|tdaQPMWA!@Bp}4f~|~+XB}d z2ib>l=$`=(RA)@hSl6)cAs-ZnpFk_Xa+N&N%pF#B8i)aVGQCc`-cuy^jdBF6LdYH} zz-EHPQ5E?m2wK6$*+ERa-~>p}I`wmRAhuMs^S7*PV3cvI0m`ksyqMF+5rAeulYEDz z%Uj~M;EZ0LLXtSQoOc?jp9j4*hc}w;upRE!s{rC?ZB5QfWCd)l@}F5K0{0i!+$EFn zE)klOzDzRTaq-^^eFNq@#D%WHm1k^=M`a>zo4^>_5KhMuH?CVTW%X07A{7htvOH+C zeSpOBOBd8uh0~#2Hkt~?7H&>da$SDCTK_x51q(S1V`wJ0IjfiLM+%8AdV%rtw5=&uo}f?HQvM1#L5f;7o7&;5gz|H5bp&K zn8fXEZ{~w>-`s&DZcGSM2H01;=satb`Az3KS6Pj%rDSI`X)GyKke22!^IQh~hWyg} zrUDh3YuT{j_|m18?s53WIyZ#e{=odR>})9LrdX(jeb4#X-tJJab9SWmXNsR} zZ3&pHyLRl|CGV}KGqv;C?OgiyXP*UI_g|2pv3LUg=m5SjZ((c}_dAh+T&?4l{U9Ak zbsZ2F>;oo;xQcYX!k7zw#EIM>*^?k62(Q(VeGJEbBeDQ*My3~eJkhrw(iS*8!9O5DRD^88Zm!;%QeRjSy5fvjJpK>nGTXsP2nC~(4U)yv-bVhC>;zWcKIyU_Yh0EuM}jKO!m1Kj=8CYwOc)ffTQf0 zvz7{M%IwVGq+Sl>?H_IQ=*1m?@S3JQLu|k_N7-O+cG{E0aRsd3?btg4J8Amyw~9|s zC$1kX1btSGz+_T~+m8Ra1mWnXn7gf}A6fV3`;6C{cg6eANoEPUsrg(^X=f#@t}hTr zv3FVRmk2t>DuSn( zNy;*<{`GKCucOtEZBa>GVU3U{SG&5^bXq0kBV=A_oZ@i!(xusKOMl7@eRnvFw{Kim z(92}0LNH*>O62A*Xc?NTVE$oxLBX7B&?t2Yr6zd^j6Ua3!$L(;z?XJu%~7i{xaUx* zv)GmMnW{#wLS*vNa~H{Hhb>oLziQ#C&~R&2Y<0~WtsdW}T1W|dVIafK3%Bo=7b;Vi z<@{4u%$rxcGnWZ=s0z6)`gnE~Yi%y9UAjmR3@_Eqo~fLPpYwG(&cKLr>zp#R&^=;zT(D?LvlFv+4>{vKqD>YZD^z>Lwuh_zm0@>jz9&u+boc zcP=gUxA@&QtG}g%!>)Zu#h6cDdkr$B+*0PQ`t>BmGRagCuTuK7nAa*vS(Gocxmf{+ z`F&n4;H#C8?sW%6yACWH@4!3M` z>G~a(O5W&+NSBO+mYu=gIkbS1xz)0e-SZ`&&o-OUyb?wOMK`Lt!Y)9Z8#d%)4H*yv zG+`-7Ik0~0ih7m~mSS#@UQX-?)>S6<3$XB;miWLn|0^1YEhfTS#BYT6<9|&B0j5Ve zeT#i(diClpcMr-IF;`n{aeAB0V0ZiXwk3H|)%CXCp$l#Ch~H|KDk$CT{*GKO-7!4m zo#^Wde}U{d7)bc>yeDe@9Q0_1)H97b)|*c`b1JPDJ+fm5YFV%#eO>y6>lT}Am5zwr z@AB=vLMzoff*Z!>b|f?wli4Db*6xj0^Ib5xJJ3<;2}NS%d_K2+eQwLD^^}e;fa>8u z$nM;@Z^vF~wH-hQHqM5opnK;DRXg-bBf2Y?Rh6#|1ZNaww})WOyi9Ofy40QvF_R~s)1o)C-urq}^a zz=or`vA`xm_=iooEfA0^e5Y{bl|NzxLJ4|)o-+y+z7q5Ia<}ACJ=UC6rHr>HqL!Rm zs*s_#zVn@3--GMjTj09=$oKMVGRv2*T$x#ucUqXa6TA|irC)IL=#S7HTc)&sp<4Tc z4%e0g7aWj`#>>G4YaSU(jlB*}SSQZ4z%$|UP^mcwCIvNPd4!*>D!BbRWneXpfU|!HYoKvWl zFEd>^bqB7(aw>+rS#}Z zrz4>8B2K9l#=RP0PG>m~cBV86nhv*HeF2xSE>O6oaqT@`zCD`@`xd9$$9fH9#>rW5kHr&u=f)Q*H7P~Sl=pdZ2 zhGj1nG<8lOy({7Cra94ky=g)b1d6rd@fXL<0iKafaXD&{@mTg-nP(q)f(V5)dRCY4HBp8oemdIYmoCHAqI6R-H zggqX+{bR(JcXpi#du4%~j}s98a)1Sb^KNmJANP3xVS=V{T3J1o4^jleJMMH0r$QzP z&<;^$tfgQlAsk*ucBcovLKOgP5Z)LNDdaP784w}~z1HPyT~32B6GNg59>w=bS`YCk zk4-Ard(r+&TGlF|fu7_2_hHWOUuL^foVY<|VZ!Kz^igKQZr4U_O67Wl9)0xV`|kUg ze0=`eTR~`koC_E=u~|8t7Sfr8d>H6Ly)!tN$}xYVwHBk>CT$giX^?_aPDB-vDpz}P z|6E>y$F}ZI6^p3{%tn>lE0#>4SF6?P@eITxvLdmN8Ke@kQ#QwTsM<<(DeanM3Orma zm+JI}`++*4HVe-YCwBOMhP$U%PqZ)zc>ynifojLfyKdfLt~}koLwpo_<(01iQqN(q z8-f!ZHCN6-tD@I-rPUgxCQvQu%xc@E=;f=oR=f0wL?hiF10}*X_cXKn2Y?-M6;~dyX|d8i4grMo=hfS z#8O=bAA=eIESqGc_g^oGoMPERgbD^ZQ4*w7!!`v%0X0j)R5+)7+5&92w(!$wv4p?E zBe9!*ieP)N58sgp#3GY1JlP&Mq1PEW?kw{VB)7w}_UfVTN2uGFISOGbx?Z7Nlne9w z!+{n_IWayo<@)9PCH}6`Jkv;~WZ9+h*#q$$Eo#;4V!hd9Af-~sUY_<-k}nsg<-oQyFNjh%Kp0=l6BQLJKZ0!~))+)8TQ_My>8KDibs) zQZDpMIYS~;b zHtH2H^$E)t2qBZ7W7ULH`$2WZNgM$r5xTZ7m`G1;GQNu5e%A2t*E1a1Oswn8Tr0Pw zMWKoAtEQZg)HG1qw=b60b``QYs`MGT#xgd4$UbLrBy6&IDBj{!YH{sLFNjpAd@-tp z3aQ&^`o~xDt$82=U_Ypet-}B z@DgO4Opsa-3~`egMyC$iiYcJ3n?XLP>K5#&;t+6Y>IH3>`kSR~O&}kW)G2QWn{+@A z@5|6_%%BYYL#|w07-*RadiXMiW4By;L1_o`8MT7>^WXl4-1oEDDv2Z}lbC;n#4W99 zC9m~&xCeI@YB5>Nhb6VR)nJGp>o54MER9OpB1(gtP-@T*OWF3P+J{7)3Ug)qx!bbe z|Nd{Y*{;lZmRZm$NZ8C8tF=4IVnHdAaKw@bb6UpZL2k+yiFZ@UkU^gEf+k|gch0O` z7EdRX5R_CWQ&}Qsu})7x?dkwjtj`7eeiG22)mSG1x(kaZmisg_fH_~+(aqt~WDk>= zIItlGI-}c<-8@!3s>m7|Oa%!g1lZa^N>auW3)~uP;1Z zxclz?fBh@cFz*7UQK-#A>94-R9q&K*VCPLKG=A^B@7#U&yX(VGyHCV(5qvH|-v_!sd5J#u66Y!tk~DOYdGcDhU1 zjhz$UIb*-0uTV{IUKm$ggLKxbHr1}1Nat-ABHv-BjP`gYUGqJjzID`X_ua?rz2_b@ z!xNCvF0e@ykV~=d=1)EmCfwcjU@qwDn<`Kn7hxs*EMfEgC{C`T@%4-{_BP23J1LW(ig$-FM!3 z%cu7c_ZRDc4r{nW?MkrJHh>HPTa9c47!bJEE-YD@2j!be z0wuxb_z`UDsUP70Li0!UAL2bi?xyl3)+;8mx>&-0p-z}h!ec#L>v`z#;c!wWLhN5@ z&eH-anB%bJvPjFLE6<-o^?Uwa%o)s18#bX3fIs z#>AIA2K|e%JjQ(!v41cHIpRcDrP8ORT3~7eih&JnKr0|o;QRl8C*!nVVg)yFWE%i= zD&Sl!WbhN~ZwTXZdG8(Wzw0%#_V=pGx=VX^m@DmiN0!5Gm6_1;N4t(W(+u!%6bF&+9+cHN+eb+ z32$JDH7%ZrXv5?P+)k!i0ZdN}hHn--)D;;Pi@2H^Q)-%Z!<&nxo`Lo)#{w@k)(XHUJt>kx%b8uc=5uk^&Ewg)FKzb(iWz~*md<5D2Aw$HI{iI0FFq{BUS;UPeVrbU@z8;4`cJ*F2j{;p-W^A9ad&cFU4I2gvXf=1bPW^Q(vuQc2 zfq5D!FW73VC!}I(X{MxN>cMqVgouEI>J@odG}UD|(G#bMIG&0Cge0egbfdu;;6&gz zOiBr|z4FQzS2*(SLt+kJnBlj|PB4Fc|9zCy{#D$4WmH;hY0} zQHOhRZDYv`B_ucWWMwM34&9r{CR549sPAml6qr*%s$sE*g%%Lq zG`m9c`zAcngdpq2D)xq(U=e0^Y?DHp)cP-?%W)7bxVxHa6+xmooBF;8_FY9G8KA9S zz5y4K?|1M*v{1kEyy&ko|7$u2Q5lJt@h!5t~fB(!&0yY4|iOm!8AXE&5 zMw}lw}37g`Y0$`PzHiI8}tcHB^)^^Ez_eHk-7^m z=DoKviMH=Gp8rZDn1`y>^w4ip`;xwZE#wUgd)(vzVVfk#E;boJjzRBEARVw3 z>M%V>Iv|KRX`p~%dncenO+W=lGjXaY4egw#Thw74Swx9C88ym{>bwgU4G9)LFIU-C zDCJHj{hY>bK7^3ikhV$%NPeBoXnoVKH{>D}leTsv`a2X{ppE#s!D{KSJFRV_bLK8e z%S9#Yaon7#4Y6y#EYJ##CVq8qP72IPhxG{y#Cg9>8^an+?7EEAr_N}dwuDxZkwT;6 z+AkyiG3#8v>nBLdyz^!@UVIzUQ@uKU@+ykbE2Bq`+7L1dRZ6Jmu^LxWj({WJcDxJO z0r_0GSi5%VLfNt?_<*ehXZtDi7-mD(PPm~fx(Hr58z>ON+9X*JTWF^T{P9i@joQS^ z$s3>v0&bvy-3?9>#tTKX%W5-Nng1#F&cx-ODms2uHSf2d zF+P7@NY&Oid#%N5tUYg$qc5!vzd~+Z|MRbBJhnnCF$b**(Pzv}dGxzHa}!#EpVZz! zJmp;~Q_D4WtJlq|ZK7m$rPO1y{}TeSmyV@##aRwjC7gC=C6dp-l=E|GnDL{JwqA)Z z7Qk1=n2~0zJ|7xm?nai5=n%*#94G%Ns{mg`i1u7e@E9;T-5{ID>ftld7iu036_P!u zZxfbX7-xb0y2Sn^)CX_HRcvAS8AcFJ{|UCrczCz*52!aCPNS>X@%q}iV`EV+N8+%! zoZ~C((Qv}X{0TTl(cDy;K(U$n|+o(K#zV`^~+3YtNlu zSo7I>n^0zf3`V{uA1%&&<-!Z8ve{+w^|!6;oftdQ!RN|>(ab&hw8iQ$ghNh;%i^y+ zN7=*SjNA1!5^Jj2rM3N=FaGGGRv>GmNEM95jHQX$=&ilX3mXr7^pQxb)zA(;FPe2v z4WQRyY8;L)Vl3-PRuST53jvSWJOhsefyis4#svhC8(U8A6F6%&gn7wEQSSA*lsi=RZ!*I!4>E69ajd8PEH6)XOPDqGF=QoLxfhA-#y&HNVc zZ6me5p?hR}NkS)V4cw}=GH2&i!d9I&bTt&fY4}0~YFWJ+rKYB?U$W$VyGrW{6>{kI z+t97IqTA5LsbV@4kHU_Hp-aI`_c)9(6e<-Jxm@vLI{jj&#V7A~Iz*LDW{C1Z zOg!jtzYFR3MJUWp*TBSLeK%FmSV_A*-u3AsQrqo0(?a8Wzt!nwBXpaET-fcd2 zNn``bcID^`^4 z@i|gR``yN_(CWj7nU_;XqL+u5jnHvQrHyz3R6i8vVt?WWi2o%a8ndi{kDN7Q*NlBL z4uO`yTab)Hn=06mZ{~y%?3#dxNd=LcuoFN!^)HD5GTnobCTvQ86|my4gss=?@7>Gn z?##{(_nz}DpKsR`nnGTeFDmZs>({e6FpC!{H4eScBNhRIhxt@2Bx2*3fj(@Yu6M3= zuCUS@n_TvAFM{N;0`wH@w74B@ql(d<*W^lH(-7>J2lne8*ssJ1mfuLj1Yjhngv3F6 zu!M+aN|MbZLV!_e7+LXtF@9Tr_vMsMRdytJI#M2R*m;Ih6|Tkv7WP`e|^+Xo$<^c0w}CBR$i)2O~b8 zy}5sudweK2BQdKO_5?go#mVKe^x7O!^rVZ?B^@pK5r4e7gg&I_pzCNO{dEBGcr2G? z%+hKD@elV_0G}|9i;!t7P!)jhp`PGdyMB0u$!W)eoCqkm9o*;>ZZDNikbEXAnON5? znwVD9syB(J4&7qAuCqmW^+C7U5r~ZRc#b#*3rVY7V)I^fRrA0=2)Dapb+Eag(yi!^ z?f9w$N=Dazy{bbVgeEXBYwRptm_xd8iLE;%8@Hv-csuv z$8GJFMhWMcsZ-AKceH1EKL4CDy!-CQ$oX1mXeh*C^Ez0(*7|}AJ5!}-+TUa?<;T5= zKpEX7)A%zQg;c(!c=p-FEoHh)|KNjB7%Z1z+>!|5Jd+Gu`<>uRHj-SSTAm5EL@;#` z^*~4rYX}8i_y64+xP|&g)YHoFM)Xz(DsQ+-38zJCn{m5R5-&b%BIp{{Xr0JJB8Ip7&yUny0zR7JK;N1$}mp)9ihY z_Cb|6mrLn1*(df)>GW5;$=u1%$mY8zd~8frjfRp%r)t!pYc9gHg&NNIdwmc(7eHSd8fmZ_;y5E@jhiwV~M}_A5xtmZQE|>0bV1SpOeO z(d2g|(u2t?pqjqP-BG4wx|!xAoccnp|CF%n;0HnICFH-G+5^n z=;&Lhe}kk`3!7t7+Qkvz{_c+4B%*GX2E{>uW} z*dgAm>V~l0DV;ndnxxP#NC{28pGRrr z9c{hXJ?yi#%sjgH+#7sjJ#Et;YeAoxy(OiC&)ro1wgtt%r7tT7=O^PjmA^D~W2_tt zL5D4{RmueKC4iO!q+c!=#++l|0@D+oYtEP{C#)0h9Yg#8gs3oK0hgXC5N~YlaqlY* zAH9PO;9*N?h<@w6haaw>Y|Fj({<-?7`(SZ^^3u&=zX zL&eo_6zPs~WRb6#`gBo#Q4SkWjlt(Tda-lG$z}AEoV~g5%VqI)fX%;(dt|s?|ZJws1m&7a{Yh^n9`jQul2a11M zRDIYxURvHnUAuq(O9u|Tw0}RnaPQt%4jg!8@7~2yS&vSwGsR{#>8)&&Qls(}3+VOz za6sHA+Tw3DP%l9AGWRID2);b}C|LZ>fS>M$oSb^@5%y6sb(0|R@QL}6z#)vEuxzP) z$Gl5{^9Y*zOr!@WcMfz_@|I}0P zRoxvENgVF&Ie${4^q?U9)N3nOUJ+~Qp8w!Q7rnk~*X!i#u3hm&CDTrSh{BN6s;!iZ z?zlgePUOaNL)lrmahFUMjJ1|5R&7&m);YNY8R!FxJ{$UM*3)C34CP49H164ea{+RA zh6r%_32C0Vh=_P)Yqtxx%4l9-&|7WI=eqa3teV?`H3*p$zSxc8y%&u;EIa>kRW;e7z@8%mprq}bf z!2??NvmfsuMOGhV1txU}K;s}Z^XQ%Jit;NK(|IjBTKBiln@8O^`-=0h9okut2f2=wkUzc=cno*S$I%l~TSc@Q zwD1SLQASV=0g-_^sKWRdn}jN7y(0<$N~ko#)JuJp&yt+Lq^(Jre@Bjm!osh)u_F{ zpMDsLdeZ}cf(QM2Dnb-SC~79~UNcRwbilkq9&qWf^LT0TQ!FI#cbvF^zq4Fp7MCDoG18jimMxIjclvj~ z|NZmNm)BYz5~Z@(sbq@Z^O~wI|}XgHNAJ61f09K%o5KcVZUcx}D?u6%Q%dBObRMHdw@4R~O-;sd}}JV5qC4U9Fq z1Sk~H7j~n7-UrEp4X52D-Ask4R5Mcspt1x9 z3r?h0;0U4|K?1=EHYQwbjaBO!5Z?$8u@>Ana39d57eh_f#bbSTXKSIiXX-NRvs<74 zntlf53aFia_@B>jxb0%xRrA}s;h^^jg+aYH(jgT)pNjZ>fs`7?YA1A=qS0Ccgq7(| zq?4)Hr=jV+xdjVyz4WbX7o%^dWXF!cqbhAIxsr}?g*;ZhXz`L|Yjwrw(q-#_C-ME^ z{*pPs<@N4mUAWT-M#Y$xFffPVdW3EnBtQzu31!y1ntA28lgx3+i-{8dJ>E5_!6lHI zWsAfX20{~8C{!7#Si6UQqO9>Ym7DKSH*yUc|0n0i8~Njv&Smb!^r_$qLbpt(Z$m@) zp@P|yrRpDi#?J6Ex$@1CsVS&LPf`|pcz!%I)MT?5jfv{^SmN)MY$e`&!6R=g#9Vb_ z?Bb`b8o_uhRRqBBrCBDSKcc!znUO`l7I@q+KH1Ti5>>(BkcuhhzJma0)sptUC@GBm9wsTPz@y!b|>;0#OA0{DsF`O zCNQGDlgjxo(qotKxRNS|D>hV;MV*?upE5veTBtySDNNn}rVm52So|m<+2O+ByC{n>M>Po-}tC+FHHj6t{#Y(8~KHw@{ar z5}Wf-#%1W!BX#)y0c_DxrY20$uYHblewmxe}38AJurBcHvms$Je4)Wx>!e+DRWl z&Ou=pR6!b!{&nUHRCo1(+vlvyc2_2$YtRej_{M>rL{!dVj3w@-50cxp%wx*ko?tI&SB z7u{FFvr_SwFOiygz;%bccdDy4P5lb*NHZ zwtRRsx^ivW9*9N8<~uKPEes{lHu?jYdr^A~3h){mkA6D&0@YK!|M%mwsgtV*!xnEW zI(yXNSk@F~sn-@Z7x0`|ALwR03kstuoI%sz`mbvvJ}53`iJFV8uAORGlr`+aICZod zVyZy`A)%4KMFnM>u`+XCm z8#r_j)kZsUK&6)l9Z#u!)wQF0pl>1n5lG5r1BuzxiI-YJaqn;`lPYD_Bg>-V`t`*{ z^hYll_yS5OWIKcknbwTuf8D?MO?Le)^B_)|x6 zgMHijSvT?R;0l<*c`AIC9v$a(2v}gR)1@IOxOC@-o^aczMrO1FZDJjV>!k7e;5i-(8czA&wE@KohO8A- z7_3Y^0tT-^XwwU!a2#bLG9Tcn7G|eGFryY+tEoXy=2#UR&%Onb!h#7)M4!i#EN+Tl zvD(0}kUR3K7h1r%vRY|KwK(g7`OG3dM$ds;19yiOg_LmIva{5&^6U&$m{!@4G|qko7+{5PkA1(L)_4nHu~si8We zESLoYl~G$cP$wspjxqS3iO&J8sX;KqYCCzX#<(CGGUdh>WvgN2`sgFDtUp1fhaWCH zIWX|#n{OiRzP)gOka{)0502*!!a;xIZ#vgyY%gCobxK?BZ9L)3abP$ND{=It(buS4 z^_e%LfmjI6=2$*IH#P?<^`dBFb1YQ-=h35w6XDEd@K^`jjc`1{XsTtS;YPVtQK)O%Q?6*#WQ8KW2<7PJpMSo${E1MQ zYjPW`1{(_aJ>ht!k{gp}qP-)b-a={?{8!@Qbdy9YS8R$$QuMk+6t&TR;PW~l8n2;x ztM`ple~08qC^C2V+D#sV%j53MnVhC(txhdd7HQ3;Q%Hv?NQLu5n|PDn1Uioc^6LiR z&jcXmvlV<0w3Bpc8gCO7;^*j)K9C4nIKipuF5Kfi)| z9RTf5KIz;~v3RnkOi0RQp;t=Ze~IM1*%bQYKBU*YxIi4KpqRo7P zPp4`QrSwoWfM75eg(MQE#nO{WE5tIX)u)r@qZz$cYoa)82~Vw%8eUzLsk2xMBCx~C7gRulv!cLKGgAKxlgtp!_od{hV-h)kpw**mrvzdgH zTFD%fp>iB|xYh9Yu)X+G@Fn3BKK~t-S*}bf6|T(kBS+@6EV8feaC)t=W?|Z*&bIsJ zuROA>+3uVgaB;8Ly7w~CjHr2qA(x4^TAHPzZ7Yoa{47o~p%6>MVkwua<8r0qA@)KP zhN}NgArPfm=#fc8a=^T7_7s-EoOh!OV*G;JR2qkQk%wF+t5#uSQCe}M6dSHiW6GlB zaXUP2y~{h*B)KV94ESTobhFm1^LvGMh13~}w+4$jeW?IN6cwXfB`*m$Z0MorFdJ=I zHSY^&FbDlNuLAy+%bf}(Phc1Dl!}%``MY7(FRP75UjZ(bGhMFQR(`+xXB~PAZPZAct`VVDufJ2QW@!A~?r0-v0+- zFcvsdKv9ztuzbXj$L2itN)WRi=&%kn>YfVfKjhv@6YI;>Z>@?%WR+{8G!6RQMYN{W z&N4k#J_WUxo_ns3zPgN#R4}Y~_98gO$@Tk>zBNiXhGGZwt8kVAjOrS?O#(#zX0!@Wt7D$%ia8xoK6ghD8L2nWa_wKhX=1-8`ooQ@@TKqpdyvzDM zemhsC3wfgkHP0vGLD%2Bkni&tV~t8jT5nJ8FJDVP%Tk*&O)iyO0P3TJvZeb<{2niG z>nWwLNX+a)l|;#b%okQOB@}6>xKOMd^u}B!iI6I|9q{44RMMr+=IlCyKWhr<>=BtL zAI=1V>6S!MZ~z)53Bwi+OVt=vLAs3NrBC(FLRYo@=5QeGiRttzy$rete$&@S4>a;S zgbd*kMjoAtw4igjrqE!>>9Y|G5`{R?h2VF`6^O%7?*!f>yf6Sq78Lb9})-?)^Y9!WLE2-v`My=GFWI~gM#TO~>DHLu}>RhWgrCO8y z#Wz=HgY>~~z&Qcvrb}WAc9y4JxQ)B8cc^Rsb)K+CuT-m9Z0MuCA<^UwL%nw%<^w~~ zcfgit@asiFxzCSro*sI^9)cWsA@mjO1nEce7_kcu zZX7xz;3a^ig0$?~S+^Q${uvDIfQT_<#_vc%YdHKT_{8Ea;bathnv9OXKc}_$gJ5Y1 zuIBPmM^vtyyRq?h`pag!H=LN=_0m8rtk>|tX|;H9`90`X%>zo*X7X*KfXixe^Tj;w z=1d*y9c)8S zN);-^q3rTl$TfcnJ$2DeNaS_aJ z{~3Y>Q3$XKdV3Kuf$28wjcXaI#h?k+3l_45knvj9R}!NyqP^7*Ub1uN`Dkd@h7J3n zz-N88O~U6=t4>8F>R}V#q)VtJVn6AvyqYt&9HxA~n z%$=Kqe^=(IP<3-)?Y?qPd0GJ-K1`qa*EqC>L98ww3t7D#<-u#VEa)5So8CV-=(Q^( zt*lmvHEoV3wpcCddEC&1Pl!nzHF|q#{gGMzlDA{7*N-XDa+wXYI3czGhyGytu zIFHriF?ULMk`NenZDY13#D_5ld^C^w{9|PrXxXEWzWj13vY4CI>$#_$M&%K@4T_*x zY%WhIaOtzY1izdJLIZjp#QW9=f`RpspstWAWVi2-PRX^TzW(~L$Nv5AiFLZ~-?S%_ zbG}=-3yw#jqseY+Bu~FyNp|rBD#!FC%O1>nJGSN8pu=eY@W4dZKrA5>u_!?o%z$u) zy2?`zto?rb)-#(THfSv+P&Sr%JRW}_SwW$ErduS;Xxu3wUox0EwKLZH1U!IFSb#k) zg?i2;SgD&DcES4LJ7v;ei4zeRieL!5Ft9AJ7B$`h!yUJa(?SzjvV>qa9m6GzdmktT zLleMj_!nt#91)(xe2Rq`aU8=tm`{bE0R9*fLPrmqR1q*W_}uxU#wNZ_BG5uYp+6dq z?io4!e#l@6=i_erb2O_29c`|&TU~maq3E|6EE=^8+TAQ^$qE~lU>QxHaJL3CkyhJ| zd8eFyloGrp<{LDL_yE6IFLA3p9LaUrkL@oc?tc=Ttx3H~A-_)!c~@wD*k-pnjB+78 zGQw?xbT>7M54KLr`mTCDQ*t?+Q!4pvY&__TwqzwDiP&tjLFT?)YccvaUaiqvJv>fk zZoA!LFlY1qBh9_}$U}p1MWYCs0V9s7ZO@U(T>qVUtu=X;-fs5V0O^x`XaZV(5HR4< zh6@@lul2fQ+-_RzbRz2nqzH1ZzGQU?h`AX^6dSzITN1A;Rv6e}o#YY706qeSuE~qX zr6hS(N&b*2dc!+FwimX?im>X3&xUenVq(Mp>n2mSU{Bk&Odk)^uc9e1&pg&RciHcB z4$+30S1Q<4qey$Me*d3uxFL7#wSPQ2vk_5{i@{5YG;&*OBiEsfHZN&pi_v@-CCU}r zwMlJLEG)^SBAPH)%%NXOESZ1i1DUI@9*=o=A&W!m(;7TV+$_n$rqKGc1*=xI4H)K8 z+giZqB6K%38JnX$c}UpfDrGV?o1K+NdV^ewOQjuc$y}nRCy`6;-1%>#U6Vh}ILm38 zZugqiv1MJ27HfB`0GL4x@s>_5i)AfNnb%S>QNDqJWQ&By54oKFfCHKqW9tjXt61X+ zw;;1Z)Uw* z4Y&ye?=e*ctUv{lbLHsP;h{O5D*BolNnCDvjG$OgS`mYoj7=w!3v&2_O@6>T6Fv zS&9^M+zqF!Uz?JHm+90B#RPZQX=OGFdSKHs^jYuB_CMR4h{KKWlZ`?VM;QtSQd&N{%jf07NUfl3A|tc8 zWvT$WQsICOTAMa03&Bk+wxVCP2^?D5O@Ptu5XU$KJh}jO`vTbS-!@!{Bin@cYHU=C zQ$ywu^J-n=)`cnNPux27IHO?!>E$_K12JP`R0){?z-)xgF%5v81_x1no;2QlZw=j$ zeZ_CVVi^dDQE%8Ch^-meUh@6wEvb#^4I9!MQ(F+rpE&JCwC}E$XV_dkEyb4Ug{Dj8 zCJl$TTpJ^;&xtj(7?V`fp8czd!lUePv3V!cUm;~X?5ck6oLDZ_k2a*BySfu)@DZtFnDg*O}f`@8!)23kflAAZ;|Hazm+FgiCJ4xbYYLINw+ah_jpXPpk% zl1rxKLVOo0iC~yXEv5$>a017HP$bKW`Lr%JdG3^((P`9GkGv>F&UpOVrGa3kPc%@` z>D+z}9BjypuD;f~EI*hndddQ^mYM*1$Be_Nj(&$&(k&ak$_k*Z_W(JE$t)VaSZaa@FG{mxhd1bHr%trkqZ7)MsF^8g=G%!oF<5WU9L2ta+JSt0FZ$ zF`8VGilY5ZW`liv#M|TvS6eewjJ%E-WKg~mQ(NgheThPcuC?VXytllDb+9hKXCc^S zCM!x08Mby<6@Vw$ie-U`kC}nPtO(pODGG&G&|-9M0VjklLfZV*S6BB`Smvse`o1~? zDGL{0j4u58>w7oTTXCNB>lZ%|XDupKr&MZBAxAn2xak&<1wdjrb?4YgtUOFXz~zwV z^j{~qzgkagS=+KU&@_@9^107GyA__aoYs08I+s?WuV_Dd3qQg;R-ZQBGduLMHhrU-4H|r$#TO_%bA06u8_Mg~4>>ICZQI}z zBpHu3UXL-(Ff&n4ElX zxw_b0nfa>6)6|uZ?u|L6y-In~R?QJG?Qf-;rQ*}rw# z^ohi9Z+U6uufx^%;z@Kf4U>ER4YbE0y^POc=^>Ze03J&$iLgw?aKlQ~X8aw$e2gPO zoDls6ek5|e$7F2H(GP-$B+=lT5NGWWqfdGR(TCE!vQcZgsf_%ytK6i=CpRVaQoc?R zh@kGnWv~baIx6i+DL-%37mMLSetD@6l}m>YQ?mYGBvO^`U#s`@-~IWd)=1BQ>oj>3-ihljKuYR*yebG z`UUi&kj!(iL6rseJVSN>0g*5~2a8eI6|5rMzrH!(8Mh{^3ykN*`h7Gu?8T|0%uGEm1h{O1}mbrEkd)Q6-5HO`bSOOA64qhv8%HkCQ@ywh= zF5-0})ijKCjHv|EiB}6{;h0y-Kq~mPW9hE@mQVTK!s9Ec zgfn4ThsS3|+ttZnf+f*$#eDqR1bwqXYcQxJ3YpgxJ!Qyh&}pclMmbl&;_zX}RB4c+ zM10|)O4z72y5^_D5E=yeHxAhBZdh+6Xl*~pKqe~^6ihAe1TUwWNEb%J09s>e8?oyi zvche%a%tX>*GS)v23ro&zn6f|1v*nYqwZ$EpFVhQ3zD{+doE?KemP6f1D<4c<&{?| z^Z}JnE^!!IL!L-ESG+orpf0H{p(bg_^#z?c@yjEmz5#cFZg3Nc6_ur&$szzT=WwdnKP4pcPiA$b`lvBcukrSw1DpT8DCoG+)HwQ7DT0QGHM zQ2QlY`ecfpht3Mqt1S@F!7~pdWn;+`Q;=ahkZ*+6Tk`ZqxHUDVZx-Ae?mro>&B-Cy z1(P!4RlsBi7Y{HI7X8F!rk-uVwLtr9?tI81g=D^xR*7~z_s{C$T@}PC@7jf2^aix2 z<*Tu2=2^qOW?x1=oT+@(^7nXxx)fG+Car|M4uOyN5f+DFH4mQG3CM*s=ukov6Gm)c zUVt(jJ4UCFcL1SH0 z$u0DOc}V-rM;sRS8}uy{4t@3K3#*HJpIKGOIf_!5LYo+_WCx(J>pRa?_E*p)y)I*I zP40&LFB7S^`JXkxpc*zG3qf23ixn;;Tfi6U&Q|(o#JRv6$pxV_wIETR-umjRh^h`Q zuY6u!z8uP|UY#6B=7ECM`!@1}mEf(E|Z}o8Dk{#vvmdwYC&vkVN{0RioE5 z6_V|~rnDSiJ$>)ne^#_~PFpZ%{;Tt%3JHcbLN;XxPV1U^|L(SIxoieYfVn|d!#O~6 zv#IZa$8qnQ`jl3@(*&{-)ejO9=gzVW)qC52Qb`q(e^Y(*4<;8n?<_ipo@uMzOZ6?x zWYASMgVltm9aoa{9G3p+??0=r;RUQ`sGkY?Cvt?uPKl&F2FXDw*NYZ$x+;T3n5|@8 z+?|X%Y$}C#XRLW~e337Ro`1TrD0n}&KfpsjJxCr^>uKit8x!A+)ZVYL7!3`u5) zDW${;h1h4rW5dJOjZcs_@D8x=;q3rEG0n|`m;;+FQ^_@It?vFcB@Lv zV<=h4wih~c-SoE1E3Y&qtZJtbm{w6=+e=sv15Ut`h;Z!ngo<4XlGc?3;+!yT3=gPc zZmlMyZ=31!XB4t&O8T$^>0}$xOS{Zow}Zv2AojWR-Kd{#I8a8i@__@L;8miusx)Mk z-us*EE@VXe=+*R1b_AUst+Vt&J9Rt#4SfI>d4F{kHAq`99>p^4Ua$ditwvpofEoqN zNqQAvn~3;fu=U5RYZ~G}WbM-or@ZDiW;0onb7`6W3Gu$C0{@1_;wk78BGOsX#R&b6 zEtR7cs5RNqT|7tgo3^f5^o8i87PP5crgyi@4;fvimUi9VUGxKZX;F1?jr42RhW)w> z?c=GNSiU5&CC->jwTeuF5ajl{wjsK$r-i0l=FJ0VXUnQPX&QlP4Ua~K5~+N3UN%cz zQNelgl@CkwzdgT}E6ln)0&-0Va;1&bV$^Xub_?RT7_0-Qrna!;1@w{Baoh4F9dB9O=;{6dW~?~Z#z>E=vCqiep@WGbh+lk|EH*H~(eK>iaxS`Z&R z@P)RPl1dixM`PYSW2IY8D>$5P|Hyj!^_9iX(~g2jAQWmXnc~Wol%P9V>~yu2Otvze zo!)o3@z(Q~n2DNWdE_R(6568}IlO@cC_8XZ>D4)N(B*d}=gvl7(zD-3J^ESw!@a*f zr%cx6;Rpw{Hb{ol$-K(T_^* zxXPTA&z@G=Fug#(37I#6mcG_5uT?3AdX;g&pSfRdfjJZ`q9bvxgA%HR7;gsX5TC+q(KR;x*399F|t)&brdKNtW zG`-{?;x2u+f+oxFzS|Fuc^<_F;cBFuxF&>-m#M9%A};-(+pE8Aj>7=9w?E&CCs$oIFil#epc`!eh9tPX6HSgBHrieHpNv z*cn#Wz(fdtofb5*WSm(Knl;6dIc2q~TQN}@D_ufKIjTlCWT=(KcwEcO(F6zLM_^TR zt9^ERyuZIVHs;mplr1f1g!)R)miqd>=1I3-?0IfVM39#=_3AIN&(8E|0zd-dD=@5FoI&#lV zUsE?$@0vbGJ9qlM%A0j}{NZ9mHKXe5h-!kbREWy``OlZkuFg$W&>!f|oY7WrnV`3N zYo2OuZ{OCM>jVGgB*aRdrs!jItp`c%#@;jRkE<7tsNw%*vXgMwpOJ-(3egf_gKk1k zSLlaONByya%b%T(`Bvl;)!FA15vO?0Io7?i!pqM*L@ljeOf_xJuPf}OpW*T+(t*(2 zMC?>xL@=4LYI}R(tl*LbKtC9NT?X@Z&A@AEQVZOGd!t~M2O^FG-H`uV2>~@YiR*$y zR$%?`uYgbh9}-*c*V0?Ed-sL(6{UeIO|M_&9O}E_H*-c~BZc%r-?F&FlvgY4jWe{7 z*`9OWPvth~vhg{+NK*}s6kaKgjG(_2=XPGEArQ!_V$`?yCGeZ{j+H)<_R9riMYQ;ecywm#_9jpwCB2Y*@{ry{SA?Mkb!0|Sj5_rwh(kG>*OS^8qxio-IE0k8OC>7|v110p_%>3di zJl?KdAwd<4Zu%o&UJ1w#C+S58x{1lZ1UC}M5hchFj@Jzvkk;a0J#jBVGo~710g|FIYz=UBEfldy_2hKSZ?s>_Hvf`tKQAp;+Qh2hR?ON6&gi&Z*f zL9n*6*wEoOWdb9e{!HZ(*bG8}lv}Di@9;^rem~6P^{N7fyj&vFE}zlo84P-gmrP8i zi@8&m9sF!=;r|MA=O)#ScAFt*wwirti9mdMPg&br?(Tn_Y6S()qny>(^5`~~ORDmC z{d$L&-goz7f;Ce{7p$F;Z)){;=s;?G{1WxmCof7|P-DbdwCtIF)=!A5uw|A zVU8>A$7cj)EABM_Rq{A>ZO|hs|uuC(;v+((2XP zxHD11}0{{3(s-7=UP{r8FNU6VhLvm7A#m0(xn`7X?K7a&MNr3m_fkj;a@HQ7zr zJ|6i2HjVgufd2_O6A@cCj%xeJp!Ux#`sS=&tFzD@$Uz%VKdac+V-L|{p;(e_6zg9D zhn7AWG3e20gXKc7)ZE=R)61%u-=%&<%_?c#O+J$AAJ=)k%Ve!{<@{uATnoF=Fj zoHt?m6plAPE~B}nk3Xin%4odwE*yi9rL9J{q3K6|8D*KK=BCq8=#reIpRevmp6X|; zlOXh?g_ju}_atBe2v;9NYc*;hwn!~&ibcZL@C42iDFQ`4+PCixDtha|gU_!$IJR%! z*um;63@sFM&!O4Z0S3j4dT<^dCH|QwO`V%b5rCQb6Jv4!!|7O75&2yMsA2pH_AfsI5d0pU0fzPH zHLDiQ4Wu3O=1eREQ|@b|ki3#Zt&7S=wO%eYCsQ_=L@dd`C^!kyx0yzQ-bmS(j|5zW zl~yFy+yv>76xwA$k=?>Tm^_MzL79XDf|TIS9@@x?p8G z+146pF>hXzE@itVxnv}sK72!aQ>Gc56L^uH{z?1*hafD%%c;k*~J}450xBhER;VpDDXmxx2<#Mq|Q>&-%+gIAN zXW9b6f|KL2JQc=bq9{Hh01s^CceqyUtlcrV{W(&3IlA_?_cx52iLq$yU4=7c&+g1Ao!otg9g&>js&@bacma#cDGx>v;;;TME#VFa@k9;7`UQ_&+!ab^!mj zCRBdS)U2&Tp{b(TAq@+PN+IR>w;E^GV*tvQ6lE+V7QD!VL)#%Y)b^79(VM}&z`P)E`Vmlfi|!*$Kl|1j2IUfNkd4jC<&C{x(yi z+_L3Aa$Vf2eruc7YV7Sx%8#63|v{|3Zriad1Od9+D_Xj6E@}O?lJ289oVLLsCrc-0jm6?zn$JwBMYoSusst2 zp1l=vcyN{n&cmMy8LM+)HEXKk__g4yW}peiM-CnT0ajo{=uI<;&2<(^6V9%MW(eSh zw~&N;_+Cev@Hy0l7nG2^d;y5ay(Pq1kxQ#>Xg!xJ7WI2{;;AvOO78W=9D18uG@3NB zgj~!@^tDQz+Uzgw-HS4i)du~S71=KOC8(R?a7)<3zT0Ovc@yn@&}F_EndrZMaN47w zF=h8&x>Fm@9o%|mXJ^DLTNFqb)rq(r(iPgIn)0SNU`e~j7O>Ixp*QG@=(9O|k5L^D zsvkGo)vAhlucAj_R8`~lFNtDN6oFhagD<0 z>l*hp^E&iKcSakmSe$h~GV`WI>sVH{c8kMRB_$*GJ2qJ$I=&lPvUd&RA|LW)nR? znG?S}2)WdGK!H(G_d6F>9zyLT56um-k73iCHwP!id zZ(2#m?7P>j`JrUCzk|n2y(E*FHVU-rRM%8+%mRHHdwM)jmk54mRDEJz;qAh_dB#LI zorrUafpE+*HyN{WcwO#*wJ@0Pkc#wyy$3V`5sTwmn%t59O`p4o>awYfO06+cofB## zo-Lg!d)sX$$QgvhQL`>=WOEwXEECW?cH_^WwZ9)rCo=x{>}d68YlvS;1{j|14f);tnU-;@?u_}6 zoNl1MlZkk0G5r@ThXtfJAndKqNLELIMpul8su)10YLWz!M$0=!gqgScvbUDq<^zFvY?N6iCIkfdw zqf8rh2ZM&LqX*GclU45sw^9?;e-x;_h3Y>jIXwjpWr*xwsimw?~c zQ^Q_)hO5Vfh9MDU%6^%E0lYVJY;n$vZkArsRByAoc(3V`8_ z%~owjE0s8-ytv=&a&y7Xqv$4z{oT`Wfd7Kar{BJm9Csqhtx`k-o<~fkl+!?Wug!gy zTf6oMBqW1F7qNZ}c@xe^k@&@1Az#Ys?CETwB-xqSx!IFu&z>Hl-XK5HKU3@}9-EfJprD|G1xHB# z-}>xl+4bvFh=S(!DSI{KLWq3fhx)E zAYZZ8!OV~E2krxxDG{eZ?WS=$4!q1@MS_`$ROP{&$U87o0hj&HC%|XGy259Z$N;0r z>h%#PE+o|}jVxvqyAI8RLC6cEeXm0j|!eV7X zAy=wGO$EDFCKP0#3xF)I&O|bmMzK)V6B~hcA6bK1AQXtdbgKW`4VXcQvSe)*Jt|8A3OMMWZ9#=jYSVyo^;&dO zfqogqwv3vacuAw4{udH`@(CEV%AMz&zQvqtUGu{a$Vu(+L_+>52{fM_i|708QbF7s zY|25)Q;pNFGeDeQ>7s?$)BLBat~w=8?=OsSi+pu!DxQQ%gS;ePB5906Ay^5V6O8`B z`*<(JiA3OuudhhmUk4Uf-Pyol@CkWN?iN%GPKl{|tm=t^yc7PWwxDFySozGvL}rry z>Bk=nUwnbbgMLhjze1i3c>enr*THdWX3m_<4En)Yxj*G*%|e|61F=#m_S^3CdFQ3O zQ(xdP;1?+>Iusjw7ak8Xzox(Y6>UIdIqsT!eI9kt~_MjDQwlM zgHZpNi;agqGVj)*rg?0RWal{?fm1G(N!gqty^q5c3VBX_uBGf!>s+7Rq&GpE2=Mgs zv{FU1Y`4(shfA-PhKJGP@~LtjO|O81)5(U^549rt=*+&BY0-OrLfZ;m9UF@-AN^Ob z5h!HJ8x&Bb4NVSZUcW0XlqjhO=(+!rsdyq5MRxD?`G5wNeiVcIS;ZI3~(us&EkGGZ0h2uLFB^kO5nMjU`e2zcc|w@v`F z%;0J!HS|PLS+7mO6L45pt}S)@tW87xYc-2ox*f0m9TEip{2u##^x=km`-*oLuDD_Y z`Vf6gLE6KA1aN=Ze>h5sldGkrg%1jV`IFaN^WHVroObu!i-W;v_h3At%c|_|Y=*-J zuh0GQ)tON^GOOeF`y|N_pOb+OjozilNd>v4PMwN8MZjOp9#hsZ6Gn}l1n19=cvBz(v43#*WttI~s8%n)saSBddn!#c_9s0FhLxqtYM zkqy=`_yfR?24avtDR*SXtA#fQ2HyNT{TigYqYe8dde8z^jm1z-Sx&Dcc6}lH%K}Z9 z&B@uCwOZE|4@(8?YrlMT2Kos{;{JT#z!$(y2M*kJTM_!vdYwL3T2C8JI*IuGX0Gn& zSe;xGyGkV2Ce0SHR40Uv9$H-{ZIDlq6$N3gkP>@cT5}|6kO{7et*QPd8qHZ*vhJsaKe`R8|Ur5B?Z{qoOG z-<>TFraaQ_y<_D79F8 znNe4H;{CE(S**BqEzv^0(k-Nq-tE%|jy&lW3MfvQqBhebCCXExN6=09LHXy;hbGv| z)~(YR)6Ssb!jXdq=Y>C$N+1V-0t0z-5sL#h%rOvTw15FPCKwR`8KCll(Iuvbp-V{) z`e|~rap-LfI&Tn8ed9xDh&=#wm^IUH?SUNalw;`yNQoN9yy++)8Gc5C1?$@ zXkJ*DG_Jk``2dscL6w5IgrDF!ya%u|2NUzLBERXSg<8Rvcmvs=Z&lSg$4SJ$^VA9nRvE&QBHA>=A0Fj`q9kxSK$HlwcSb*saT;+V-~)aX>ytO_+>|M3 zs!Gq{nCMX`HQ*q`R-;=IekX+5ttO+pE!Cb`sC1ep+vrym(oLyF=s7yGD0MOqRK1pKBMWq_ue&{-mAJS%d#xXwk-GF8^$zK4YmP?j)|!TLJOTh zLIQ*m%7p|-APETxB@o)B-J1XjA;fcd-#%v~o8-O!Kk|%5GiSzg)?Rz{^{w+umtA%l z`@?dHxvjZ~*~&^d|K}>;wPss;xeeeLw3U(L3r$nCY{iAFjcZ1=7XnTP&_Yq$YS*1f^sUO)s=48EKE%u?fqm;ZGP>di=KM^_S>s7 zO+C%msy2Ad}|kdUc0<@ z#~rx4dr4*M*2)(4O_ptZ{yB9e*sqy+>}Mz~uT<8ptE^)mn5_T0hBA@6=HaN$#!+RT zU$LT!KfXU%z2S!HWR-}jRrc%Z_xWep7t;OAEzKRwLcac+`XdyVKMv{93vSY@8iS>O%L&1WG!ie zm*_{)w1}QNgEM^TQ~z93f0L;JS-m&hZMReq=n{m35r@BDQ1J%Cy$MI0SUXN?r>(n8 z)NBrWLNnXDN25a-PxSNVp{~#V7zIjo?0ef6tkl)~p$e zxU=5yCkCA_4e?-TWh4KzdF!p-!SPu;F#5*ogk58a>tYsXy5ukCd?&r0lZt>htTQHT z_MAH2NOcG69XGR&FS)6fDG6$^CntKD_C9etqjPtcO52C}Ys|kOtM4BkF^T{|KQ6uN3?ku>*?q%*$uWJ>VJ8igFI`xPouEL=p`e(AsNGl|clxxC?Xd==q$= zq`xV@f#Cdh;w>5OBVqTfdiRy9ODNP`S-$#}+6$s9%NyiYlQ+?8K+df^zjPt<^Os%% zJE?#5tJ=E@nyE$Q7b}YvF*cV+thJO&5m1_!t0nqP;hVZRIPsV28H?keMYX@AZ4F-JAqc30aE+kVpaI7mRV z>V96wh^KWquA+!Js6eBn5*30Je3pfhf?3O5Jp%mHmIWPXsGrtDwt^xdG=YH^>0gve z{Hq+rS|?D3#pfUl2hI#rV;8649r%xEo&^t#fG*%nV2CD|3rCgQv6W1LVwf7qYG`PozO9(Gpsz}G{V%-*SS|Kl{1b=SYvJ%c3W~?C3PyAuV~a7kw}&e z*XAgUn!#8>>&Zs}6{3*k38{iP6$z6iQVoXiY9n6MIpr#udS<&=u9oKe=43;TG<2oj zV9GiWl+|~hv8js(u)5@w+liur#q$A}_t^4!Q>eH3cQ6}2` zJ+VTf@DB9U#?o4ugqEbZ_pC~$#s}w>Y&x|_W0Q|ftlV1ety_IWk~4kRl9C7|LZHZy z-fp?L!tB^+i53tL@?zvyA`jvAe*EjtFbGspRq-?{lc-zrg{tikUL{&kp>dAi=M8EYJPa+s=O znpy|6h}ne4^e0aj{#%aXETVk+>B=vDL3GV)hjJS?mbYxlxU8!EJ9gmFIqc)ZgF_fp zGoC8~pa++hr`WeLrSf}+ki8xQs#>>Q?=@fkK$V~b-^$w^Pfe+=1 z`KVC_MRcJ~;dI&4^(y->u|z>^g$}8t`Qd2&=jlk$S(Gb*pI)6cYeVz6rK6*6bcHf) z^X6SJItpNN_AetN=Z-RC=Z=h^=XsQ*fg4d$h}g#>9=ki^(y0BOl|DW~^o*+MA^=vJ- zOt@&5w4RF>Yz3FKo(0vGU(*U|f6eaNWsb(_NL}F)oR!kxk*5|8q4!1Moc4Js@bF0mu(-%YdpsH9L zv|Cd-tK*Bxnl~0#tBc=QQ~7DG&fL`8K)$}>ij9X4|B}^Qnb&IIZlXlJe9t|A>%e0Y zwue09CMO2|dE-f~Ev+kLJB!8*#r!Opyb|$v2Lm18GYOZ6ent>NgqU`>@o^;@lWPMl zIPEeOErufsMMSwn91Z|-gK7)d>8*ZxMLAkCA_)3^%dvMKBn z=Hgnd-Zxj@Slal*RZHK!#p+0V2X(|ul(w#Jw3STys}55tZMXmI$co$D3kN-Jhx^wx z_E7D>(xp$B91?(#Iee49MpWg{p;x$jGJ0+?mW;aDe-$gt_t#!(JiaJ5&};gUkq zE)d`Bn(6nt`-A?<_^fg?ZM2yjH?SK}gK;&$8&r(J?2_q;+kLos>&lfY|FU2Kis5(~ zeILA304LGhAN3noz;E0!Z3j}roH~PiNMmrLyY7jF+4l!04CF7J;)Dq> zTqaYl5x8K*_Z^O6N6zK}WO85uy9?O+kMk!~;)QQ+))sl*) z?3PV7z4Nh*L!T|zn47Lzb*;zI7mX*1ZcLOh=<}UU)_S3+3()8r9cq=wn;06{WwGa@ z1)C#SVeeZ&et+}LYzKG3Djy)CGkuja==02Huy_UlmFBi2LrI&B(VI2quo<;pbdT~n zOx~)&nTl-kJ_GhC)l^)>8nU`&%W=;gYt5N?EycN$AQfE%ZgV130+f_v#5r?r?aeo9 zpKq-_4;Kk{S2stoH5-c;`=chWwOTlvytH?_+u`%ullHD!ukJ!iSN}fp9(yO5n7T3d zAeTBiTDoZvM`0XC0cC7m%*~vF*1W30{2sG~>}Jo4DHKX|(I~fDZIKSUBTxRA%a*cL z%1>h8rCyC%geRDjmh-lVlj!EE`KFoKR}kyndx2 z$k;dM^P8LRABYugUK}GqA9MA}{HFY=%kmZG*5*oPJu9Wx&g*4bLnnsi5}%%wL1hn+ zDrlIGF65N8LAMAmboj_VqzG7ompN1?Q;Zy3A+G%2g^Qq@-CAeV&7G5tSC}H}Z(J%@ znVsfXSHPtisEw$rE>mLiV%AJ{G@nD5>d2R0ES?!d{EW+tr2oRpFf#EWB2AU_RtiUO>R-z>^ zxU+B$I0JwNN5clkT6)J?g|uIv*IjNxX(u>KB$O+Z8L#!a@l1_<4_o`7B zH`v9Nc7a;#w8Lf-sa&f&mpfDdMj}rewF$jLtOYQ+1`YAZGUL_di6stTvH^NbtT5)& zMsu;Nc+VB5pI$okRH8oXtn%4sU&(H77m6j~v{a&D&*Kz5xygH*(wO88-VUYjROX3EUL+cxI5Z-=)4uHThz-!84k?9I!Nq_y8x9i&bmbxN&{Lkgg?Dg zdl<23YLbIeTojPo`gFd>G~<4@wEFVyzLEo_3Kyl*vrXwdh}IXagz{3V^A;kG`V8dX0HXXm+s**yK1C4<0U!^#tq*?VPJrDZ=Hvc&i?NMMc~1qB@(3j1W3UjoH*KDxh=Lx+@ibrvbej$6S>)Dv{1;7B_E;@w)Xij+3YB)3TWd7M5#ftRV-`JH@2PH|ge-X!>)VO0u>r2S zlgm|dbF|SXMs@m8Jw2tb@w?@3f6Li6Pt`AQpwDj1!dbhN0Xur64r1Y0%YJG9- zUa_$x6)R79SuYjEO{$_;CQ>Y6H^Ohr(ZMYJLWxAwO)x~-x+*_*XHTvx*IlUC1TuUm z5teje^=PgLxv^m{Qx^&My~0S@dX9QzwHPEOW6zg=rt5r-T61O%DJu?72PYy{rpQF`DTxB{$6P`oihyWy{Ru(6K z_);W4^;;-qnNI`aq}|c{VU@UmRYvw1Yg?^3=l1S|-jfd0(=j92xUlIzcX*l8i{C3g{WR{L zeYW}0%$dxhyYGJIw%a~?^wBRKc;E}}zN9j2R7)-2c~7`zz#F|aGMTVyT#-E&PMYSU1$3iz9e3 zpNPnQ_(O3zlFx@zQaoCeXI~Bmj~t;lfulQ<=}r$0rl)>4@7}Rv2l*cqm>e`&(-s@> zkn|2CLp0Pu(Lq07C%8c%0Z$h=@9bzqgjVWPS9=QQ`3-!8)^M8MMR*#&Gd%XDSy-3^kYb? zY#zX(Fa-pA1|251S2ht^IeW=k#p3OgX^qo8nOg$b%9}CUjqFi2KW!Jv%(ZycC_|`d zQL`~O|444`TxK;Y+698Ye=jlLKfx-=7uWraRX=>nqpbenVCwd@Yp+_d;^2}c55u0E zoSY$+X*X?=_j^+-P*z%$N8}=jC_^ME=RU33-!<>R0hBoXb%eR zGJWL0CC%q^Wt5Yjh`gHi4?552jET_MbNvEw{;3WeGs8H>_U`Q|(&IT`!=8?Y`p^%g zF(RfoJ3XBIdjwO zdlzesW@q=R&Lx&&K}d{@xfoEtg(3<}3PR*qz^ zc%+eaccz8O4o|wVY$5&|QdP&a$?hYo3@whuv-->$iv?~WH9XS&tz8hr{h#zWq z!4AV(bB2Fcb6dAS-;95cEV}wvh4JBZtY~ z*!^S9EpUaJ`+4i39uk~P{N8-+EY z6wX~6e(}X?w@{i1+BSn`XL2;7olDca1trB<+8j-|cngFj=gVMqjWp0gMI`T8^K1wq zN-Jo%($n7N+M2+?o~U(HCfC{Kyf-BwLKGV5?ed&c4h_-n%(IH3h64O7^ z@2JkQXJg5Tg%)(I?d|2Rc8%6}&;D$z;I=xw-~Ya?=CBJ$B*i??mFdf7`!Ze4(U?K) z33Xa5o$*tKVMo#a=F_=rF-_O-b8}RuxS)lU?4_O*)FOHnsNut}D1IFqEu)U_)U{Lu z8n&);`?k59jLGWCxy^MQd$ZB!x#?b}{k{(k(dJq-Wo#keoxYt&&M4B%>DoqvUdrb| zxw2Csl`0HMi8*>^Tqdv4pxQ))`Dt^2+?-FO;sr8s;J~sB)nMHcBvM|lE~hmx)AZ$C zRD3P!z+wAvCJyBj@CdD~!w)CtDFe~+WRwcf5HWDyVR-(B@j^qsRA^A61u`C&(!fgO z+J!a0$9M+0^|lY>duCN$eU%I2zRFy|zC_gQXRkXpI~~_dXthys&Y-rlUzt5lt@=+l z-So!w*T2Et|NIoUR^ieqJmM1v-5pfO;PMX&1YwgNQ`F?boFM#uXFA)Q#XOizcfLO_ znh7poL?wy9gbI1BTtdb(nauf}ot^AIof;`33O&+*fC#w#6bBDdY!f;3CnysDgYg0y zQEXfe<{nUqjcr94&`kM{kMrUrN(;nHsKlgJ2erWIi@0HL(I!(gobZ2RiRtTvOr2)Ot^{_z|8^D4dj9CpL=5l6r^m`eAtA10#3>${NdVGTA#ADW_J5Z zDD)GyB~knQBS8cB0~A$+);ZAbW>5u^1I`RJzHq6nKe^gRtUk5T1eDLG_!~5vjtNsV zXG$PQ5VcAtw!;;=EDzZ)p|?KVo;km7!A>eNiNoFe|DICql?_!&AvebO-nS= zb;%`Nr=MOJl8ufIEo64qOJ1j~!|n};>WFzH1Mv)iYl4l=^j1sGsg4J#Y#C9}ZR|mk zd3u*p1&p@F*Izf-cZ|NSpxfc3Ix))CGvrax$k%hy4k^0YRP9WEWx9-5Qto?W0crdK zzi9aw^$l7s8LEI_>u7N*U0^<(&4Yyt*38Uz)dp^0Zw?1*=bY1h>Z)Yc{)dkhqMA_t z@z=uLmxkwW?ZJ!yzc^x-$<#_VQ!$)e@p#(j%s9k^4%UgXe()gqX~rA&Y)P`emdW)b z*Z9}Ja&B*+UQLw6l~7e#Tbix0tIB{6=|;RvPA;j%*|+s_WdpOE1`}}-XMgkMrNAib z{N`zHmgtPOb1+$g?sEL_YLvT1Ts&P&n*E~9QCx)(7VBuA5I4uCwL_?BO@E+y=ntoO zKr71r^iybj*vNen9rSU`M^TzlMcu{pkxSTDNM|BE=2VJ>%%S_-RYR2toVIG^!`9KEI9YDWUNzv*&krIPdjupjYbOI=op+cmAX;*Iou z=``evsIB{(r%)>^hBW5!0SdGW9V5T^@iamRSxzoMHt<#sWHBWtxQvrKTtftHO%&ra zY%6}rTBH|9ZD!q@T9>1vc?sF~o%7E>yt6;=3F>_0Jd?+tAKMmOzi4<%;MM*6&!^R9 z=kMSD1-SxB0-cShcqWG#OC{UiUCC$PWdxNK&ur875BF9FN6ITl<_#_AEe|Z6*PHT_ zU#uuDENo-IVSGy@93Hs?d;fF7XFGqzP?d8ZrCgKnH#pK_< z`Ur8n{yO`Gpu~Rg;fEx5;NydDym9d3riEO^K0|6<94gm!L{!=Q+(e}^@!aML$zOQk zk{fSqzFS>i9UsRXm0XdDxcthrJdmC|? z7OUcgJY`|nh5w1s#&2WhuR{%nh##8I_V+OL=EGNZ)Dm|wkp+E?bmR0Jf-7c^CD2yW zAf~Su{q)F&*7iO%+b#&&>Fy=>_1Jvwg(mrb9@VtGI!KG}%1!3uFVnAu>-D0dOHGtaDr?X|;u_Eg`j zo_5-mPeV_A_p-rmNzgOwW`LvjB3mBiE|K}(o7};^)n)YxPQR3sPL*%IsjzPu74kA! zTV9Q$%wAVsgW0j={?!X}@mQve29nCq2Nhf+$t-?v{5L~QHt30T<9fy1n*LZ&hc=$- z8d-{ITtHF7_rtPKS;OIf@YVEKR}_RnVsTnUO$UxaWo!KmFON`NJtdQ=={QBBe~-R^ zHDEqd>X^9yrVA;dtMx!cmKMzbSQbc$>m7|&sa6#o*2UqZ(GVaNu}C|Ayl6sBJ{yqyVHOc{V@ zi}mb^pEDgD$)aXM)zIq*1_!jtx^*Vx7^Bh0fr$Lqvr9c=vrqdeM_Ed`5>M+*g<5R}z}rnE8#c}wg@ZfEdAQK>dZj*lXd zw<1_nOLI{d=x{DLVr`$t*vv`t&aA>XyEYz+=R1lHL!66cOMtijbJ%ip zHj5mQq!KD%N8nekMhuBpYiMSLQ*=z99lc<#8G&*ZCvrJ?HI+)B&rBsB^;9NM?n7T6 zgj`E{a0-xnMR44L`H@&;;?fPhg*iW(v_~)@&hyn**Odl*ubz^#o&TVt(R=8h&4Zop zNT?&_4F|T%6qShI7YfeLkM%~aD!s-VhOz%$_ID&!B~|vn$c;xX**2P**znPuo(`{X zn2Z&Qx$=)rpAa@g#yy20Pau%nda2X1rCc@!!b64MMI1g?H01Dle<+s5oEC&)YwZ8_ z&J5U92DR7AzFJyP8W_MGvF|&}bMYQ*H`oMWV5UC_87e?-r;Xsc=;9vb1ZX9S>QE?h z)-UZ?r6k~#J+*eJ_JX}k9Z8M~oLxw*2pZDH(}+#;@q}qhDTfc>-p;KBD9n8%tr8`> z$os6F{gG6WE1!C5<>Zq)d*qd5#1l{&GroAtHx)B$+JQDB7m6&ds@CX;i4ry#RdwOMcUP^nSqEFM?Yn)Df!F00F0MB7s+eE01!uQNAc_SmDH zDf_(f$y#P$$=nH5Eq7+F2V$~IDj?@ZlPAq7cIH!E;ZW3NRU0fGmCWnRy1aGl z!sIu10h6`N)%Rtw<%_qW+?O+DPLJ@Bf$b;&NlW zp^nYnQh~3N=h_Xb1s1g`7?yTKJyy3+B0}SOk> zgEntEXlt%9n%7@`dF8+XpI#mC$rp`YPafhh_;Yd~)}ncc{UqI)4d=-TvwyyE1RdH9 zw6Z%p1{&SuzkR(rlPUD>V2MO`v7)%uAF{hcLybjarbYACOC&)OFGGs4AF0G*wYD5} z*VidD!}*vZnKwG58XH-0f_}#~svA0hvHPf?3SOnv7(Av!H!saNj~ZGK1mjF5>QU3% z6nPNf;tgGFVi1u!DSj>Qyn*wcCs%c|9}){IUe1UU+mphaBEEo++fsa{4dvog{!Gm- zrD0iY9Gdy0B8VC?1~Co5QoRpNM_C4vszn4U)F?6N@BgqN4_kDRk=bj@r*3w9=cLH! zuwTHm*F^12Myl0&3JINt98gMST}Dhx+r+-}L9-!VS$+BCVr{#m6j;1+9kc(gyFQ{# zx1T3-DUT^781gF|yKA;9noBy|UUZcSj52wLtXHL*JveK`anj~x zXOJCsoz56H>PiE54J;m3J)J3zNfhRiLafuN$aRB{4P>}m^X1}be)jDAC^FMjuib!} zl_Dq^=Yukct6$jdXv zpi~GL%{rTrJ@7q3GzyJn=i3rOTWMDLxVwkjmgu%9-li~1ya^f6+&hJA`wB#=f^ zL)GA_RrQdw{n{&;8*OeOA}r-w%cMPIb;?WTv>6R%WcT|{jJWv3lxP$6|*ehuXcU&B(Mr{(BO+OpY&8*FIS2KF^e)AWCa1ji)#XJ z^Orm=Iyh^OQ*RK=IGc*(DS=!pa1xzx6yZ%`%293LCoYf3Noy*r>Bl2V6l@85vc{4O zcZx!ot!!JJcAZisGPU**gRP=gndh6V)H->5S;U|ZM|MREfHCk%F{Mm5JDe2yEmEo4 zAyLcov&R>rt?(d*Kc>1+ne-cSee+rQ!zTbMZ2P75Lu-}V*(W;B9(>K?A=~5#Q21>j zRGf>tVN=^Cch6bS{3|om(UHiQ-0o6*w!vXfmJnccp1v`anAKY`hVqr*sHBduujX8D zyff)ze^Fmr9UH4It&`?5yJ6M}vr;mi&*x@omDyN2kx0+VjVe3S2B#qia>MqngWmVS z7WDuHn)Z$%1Uuz5gU-AiL1~YG7`Fu};b@@p=z_GCgsTtX1P_D{-H1E{07%Lc6{?a` zl+#dlSi`)(PHu!1P|cBN7KO9Yl2hqmAIeinLk>234GxhHgIEHatY)3|1i%$b?$J83 z8V$f_F87l0%U@yDc7xR#>`F#b-!<#Z#&u3xjPbA!ksyLTKybzs`$M4HW1u#nI=$$6MYb3G~z~y zNbOJ>bD~0z+s2S?l-#W!=7z>Q?=(I*atrT;ZMwAnVC~v#YY)~h+{XE1qB%pJ@YsK$ zP_i*_8hQ1enfXeOk_nL$R};hOBCUj{=x+m2KOD z8X9VoqIU-N6{p}C@B)94yuL>ws3ouiW*QhMQnQOv06$_86>WK_brZLHIX2?WDEevK zqn42cs||0YC=&dCL)@SyAGG}U9WS_W%}}p%hTC9sYs$5OgemGI+wW63#s&kv&f%Vo z`~HTbd8ZvcECH+4ZT3H@H_M?r9rk?8rn4J-Zi!KL_I;MEGv-u8?ZQpBI3{YnIkmjK zkSLX$5x?7S_qjEg4(W2P>KY!3=uFX&TVW&uzkjTceIJ2|r%Anw6Q0-nS90F0MYmzX zkE(1jTGbVFuD{B@7Y~NBNsA@in<2k1=NprLgnxm@sFJErb^2Y|%FK?42=k$Pke3k} zVtq?XCBHYAFe}9~%SEfr;aAHAX01_EkON)C{Fzz4&WY9A|ga6cxM=E4n`R;EYvK6!vmRfBzDMcMn8qTLiNHCA^K9+9kZ_1%l zMhFEJ>3U2=bo8(|NNtZC(V zyK4utEh}t=82-o{WZo|nFdp1uNV3O~2>XZ6ANTFvJr-i;P;~p>nOt-;Smey6zn#x%puG>K42kiVQj~&7)e4X)0RS^7tD|I zYVjPOGCsBxh>A+29Jrt9uwje6X?B>iOEzOhx!xRJaG&2l>_1Jh{dk1O3%K34&#@;m z8uluiO_F4DZN6PxbNx?&3x9T`*h-jm`Z6GLlV4?p}G`_AIZK>398y*p1T^;O2m zfB;{Xk~u;)JX&|8}~zs23N@2$dd?PeTIJ2grX0Hg%Mjs1IZVYyT)FDwdlrDW-% zYO+CIDgCWV+DiXu+>XEK4U#;DdZ#BiyE}&{6rG7cwc#eNA`oa(xFRFxl|ax?f02qR zDszf7sYH1le8byb$M}iI>&m6m-bS03}K6f%{y;5sP~$alok)kB9ODzPBrH95?koddCv>O)cmLiCk?`~yKS zn6YJ_!D3|=71U~h&`C#;7ue0nMrD?yaJ)8`9@vNCwgPzR5!f1Wt^#Hw@Tg7(B2^nL zNTKqAcU7oYP2*d1D@|>rmIkD9#>hdu^Fx8D%KX3Vx;z)0$AVBrO2a-=Mlp?#;U8E3 z+Tu87IJ)^n571KDiz10;Mg$cw%OGN2D`3XX4@sDPmAKhIbG+ncrABN-qvYKpnOg=@ z^i>pmsLG*6Tz};Jv*lvBZ_}eHi85=JOK({2P>V$;LByd)PJNA~z5RxFaS%;VAd|N{ zgldmEVoRf{Ngy63IsfzZTrVSJ>zJBIk~;<4{MYFZi?fRJ=HZUnk#lNHs+>io zvqcShk{3tys$8yJB$Z2IT{6j~9hoTN91+w4Q@mj{s`hB-0_7_j=Ae$D5s^WTX{Xqg z`UWsc@EY^`=UMCPUwqN~cJn){fVinJ&&v*4EX)p~e!chVZt@^I(bYxn>>)+=7d_4Y zCTnH}Q-S{F$r<&=uCDV);UC0%yu{3~Q0@KhK0igr8u0kJjb3Xa>id-r58!~XM@3UHEdxrKQSm0ame zwVGkyk*k?@tyP9^=@~#YfgHxM{hN}^=@tj;Ghks`+6f43J()P{s0K4I1xi@q9 zrZIGjEQQ42>yfxlK+cz-;(?FKa;q?9_+fAe+;}QAsA{E&R6YU%wxp;?xbCUs8)tfB z`VgL}0xJ|Ybk>~r)A+VX><=6B5IwY5n@KwSgTadi3)LC+aIn62Z`Y}W=eIGsE_N$SEyNh9vD-x!vUiXy@)6e^d2m~|$LEX#>Ip(l5pO|^0(sD*98DGPf2d?yM{$H`p2ksD(X2J}N7#t0R6 zIEQ=x6t%L9kZKaarZo;%q@wLh1)X7`Sd1cB_7`Z=Hd<`OYB-8nd@`kyOrmE5!3^>3 z`$~Un#%@`q*O>`ph>bhU2JHo5qrS7$@srtkr}>`f_Vqt?^>w5IE-ny{=?k&3F6Xr_ zhc_7;O2&d^sml1vrR;~yS^M@aK+JGZE@1r0(HV>H+iO-zI}#D^()o**Y7&6Yo0xcb z^~`zcb*VHtl|ApOr$xQ zL2m>yiDxjLudoi(HJeh_5L(ID1`ZE>?1ETrj6EAto_7t;=mmK%~?}OJE z*e+Kiz8yzTEs_L6x-H;!VbUDs0c@t$T^?NqG<4|YT`U2|Ctks|v%32VC#51Q*$)Ey z$0lQ_c3NPN1K2ggkluC@J;0uq*p})p&wT4+kw`2jlJ%}&II(u`H*I^WjhSQHduJc) z@h4Vp>u2BUJ#IYcb2^!IBk6KS>w7%~ePBUWkM{-n4SST`X^9Q@g*m!%I{@Df4qO=D zyO^b6Oj@Eh`s*U8 zL=g#Sbb)?31{7aQdI`CV-2uHz+Bc8^41>YcrI;i>K!wVuL>L+mEONc*96+qz|mb@6^@$Q^giP1mD~ zsT{;LBOQmXfL@7|+{#|lST=DP*}G$FVas+@?r+EKB_NiOUn3~RvU;JP5Rz^wdstpnS=*Yy0Y z-eEI$Jh{1QG>}@*{|?(0NRp^pWhIL*S-N3Hpl5lax3t7OCtHcmE3mhaGm&YOsW6lK zw%&QO&;K9d4f@5>PK`!q$d6WMHVQNLux}Axz@$lEala#KT~-3c9!kXx*3^-II&S@D3!(L#XtI z$UsxX)ZXLx`2TX*RKibj25%bhssY1|K|jt&ptYV_uFD${U|37pJoACa?+9xR=RJ`K zFYtGCF0}hK4n5*(R}L)gEN5nzQQVQ;F{tR?nC`94*Uw8=I~LaH3|NSNX*`eR}zLax+>Cdyu;X=m8!^JTjVg-Av#$O&K#OI3QdMA(kH zvEX1C^7BdI$+~L694#n!ftED3Mlr2=;Vl@D)ubLe@&yD|63B&`Ze)o#a)P>W$3QK} z3Ak1~Yqj3X47TC-3x0MDV_u)bDiV%)Qcak#h|zXGfc>XXw+ z%(S+E&L+onKlt0c8O7hX(!snH0V47{l&2aE-f~e70Y_N${ZhS1iBC%*=^xw^G#b z^Ya>WE_>6bpAt=fa!=1QAAG?634i$T+e7~N?6ZHw!D!;i&SYoK{-w{J3>Ir;zu7v+ zxMd{M*ISCl&0$MvPJb>EY;KQ6Xo(e0#Ms{*%u0P!~?^%xeWd zMZ8u5&y$*G^opr)(tpd4g!G|Y#_u@nK3-H&zlm2tymaDXyi=Z&fN?z15$|+|y^qV;WjER^r26t@{#dY(D}_4!J4XtmE3JBqVbnise0ZX<<5Kh(5VMZ>Mo7`4o1)}TvF&#D6QlIPe(~35y~-4 zStb->B)KJP$vPZnmEym2=;$+|twyi#ncaG=v~Oa;b*>w3|FLWYnKvsVZ+=!VXkY&B z4Y~y*v&MXSeQ@9Ud@+&lvROL6^WDa><76ky>F|bqhhEf0qXU~)Tl^Y@!)a1VExJU^ zd)W@XfmY8xFrzx7C@1Ywu}(cSf7W7fi4gO*Y29q8Rh6``jUtsyTOgcaPA?sF!#(sB zM>sw`I|aRmzGK=Pe(VqAJ0%)u$F&@Qm1xUJutiwbdY`*Rcb_cC}K1KvIa-vo2 z?U)sF#cdNs6kj>La+O7Da)diPv7*Wnmr82#B&I;1f~vi}!{hNOwVq0}p`TwFss^(K zwZd*U6iw=oL6LS@wMZS5yR)UJfOI$<*}BUVcUtw8Re`idqqkdZLC4y;o&$3qaeHiD zm)oD}s4$t5O{3^uN%o6nDuyvBUGnH4Y??Uq@Dt$u7(DX{#6rKt`Tq~zpXTf!79LB} za&xh=v>E|^R1r)MjSVJ%`^9TxtP6w_*63S&-w64Fp3v~OI3INZQhlh!v`8kP$SXKRme*C_9QjL1qjEz0p#1bEY`Us?+G#ZdWO)LW%MsRqvP%sZR1X%JES4 zAk?;JWc899^@)VNf-q1>L%EEZM9(&9<2s9=(x~n}g%q04h{a-wRzKgNRg1}L@LxMR z(qv-CP;Y5~3X$d81JSv|y=yA-kC!ELi@yA6rFXu!1D-{CRk_>lQs}NX{c=4iHt zoyz_x;tIG2%6rf9PK=R{cCnAe)e3?BF7h#Z6}f&_K93T{uzzknJA2WbmFyZWqF^E- zh+t^5Byc;P0exxyA&C0o+0bo>ZSsZZTtps1VVcqqBhYeOrkZ9KA(Ls15Jxkd!>PGx zMF&%oXIKGGR9=xGwAhbnq@*!C^9&{p8u&x#Q&ex#gc24W6pgQcR^*Z!)T*VkGHa64}Y-PaHChl|0K6 z%BLM!Jc=zz=@Un+<0jrF#+Zui`a*mrF&k!E;6mT{=YXadFJTG1Z`+<2i4y((8i4V z?RoId_-rO0TLz6nn8UPAISstXp@@HSC|oT{06_wJLtH0wI z&iVS0kk&HAIN76oLuqpew4wc*be_ySPR7HWryGdI!sy1~FO2S-=^aLygL+}$4Ql<- z-A3)oqiCtf+#tL`Gko-wd<(CQ*TaP(^Ww#&Z^{>6%znpYi|RBwT}LUjDMv}5)~`QK zr2rZ|Q2j4f^~hBwi+(n_aHO{o@QM8)hfT@OE|VK}?`A(-_rvBB{N_qo)s#d)t{_qN z^}k#^Xhi9^&1Ea6Qp@urPOC%MKn)0(=y8-|rF4&y zPxsNOmp>ijH<(=|<0;kN76hFh1ZE-4hcAJ3!^hXaB~wC$Dl@KjgDNymu5)D#d|H_k zO`O=Fh7Y&3INH&NyjtKtDNKcWi<9sh7Rx#5Sjb-+G8@ds^eNrJWTq%0~;{j+;#1A1k{1s(2?HGKKO(n?}uKUi6sHQO6xF3__ex$!5z&L7(vEoG0uzoif%jkdT6L`z^zkW^CMR;n1--a_8xR9|<0=1}{-&%)nP}=cI=84u$t$Kd zBTb!haTPFO7|+nNITqz;{qHt8@Dk|>9K#lhdQQOMxzoJnoZ05dN-|N6#uk%EFm8*@ zldnE`497nK8!u%)paZ+FzWPq3&Yv-&kL$-Oi9%VlDCIw41nPoBBzr9w@lZ=W^zXv1 zs?i=2iX>8FXjqNl83qTqbybxDsJQd(MCRob$Ca!!Kz zE%%W9IF^{NO9f(`ofThar)n~l@EM$!i8~7GcBg|T&X7Mw9F~`sYMQO{`}4Kz_EqG} z8KeD!oOyoZBh5#)?lATzDtW3eq#QrfSuT7&U)@6D>_JTgm*b{8Uoa&BAPcC*IA-=h z%c7^^fHE`_yz$1ZJ0MEa>`Pxib5BOFqTC!PpUnSx zN;$P+O?uhtMCr2YE1mhA$tz3em+skm(yF4z#&e=I2u8%gaCMYFMIGhkZP)T%@zl2b z4{mAcOxpf7wLv-C73M3wWR4xdv8ZtI(Jjld93=n;AI?9)wSw^x12Fkf7N@{J(`?G^ ze;6wqTOK7Faj7--_`2w-C`DG4Rm=!D&Y4t-gjK0(*MWdRSBOPrlX?~AH4CL$G?u<4 zHy8R&2!9Zbuq2eO3I8#VT+xpmWm4w{XW7RK$cr9m5teZmFnbozT zwz5!AtX*mv@ATLgtPrB2sH38E_$yU+cH5AuoPE6dTe+al95<2(7!S-JJ5N+CFRcd2 zy+MyoVJRfKg4SwJt-7;kNZ5lNU^@I?b89PSEg26FkIz^bi1<4^sqvkQ)=gN$?YmG@#D1;}q-4~)J_n+N{o<@`!Qd;-I}wGtdt<~h%TN(yS>v_c&C-CFCTvVjxC z9NS_w@>A8ucq7$ol<(;x9`ljLA}4&NML*|LavU(t_JWJ$%D#$7;I*g}`ObhwtyGRq zR8HG&4Y;GtWh#R}Xgp?hO=>Y_sLh_h`qq(kKS`w4pkzzO!L^xnGR)*wVu%Js)NQ_o zHvEOrd6Sj#@npw(MlPq~Twf^yivRM*nw<&p;cCZ^l+TdMWjf=JL) zSiZclLi-g}aG!pP3Q8Kj_$yg{L*oYgLjeH$zr_U$isS6RY(|YWwKcqf6HG_d=~+}R zO{q^#E^WmpE?QLhV}bp+IJNMRC3Vb-Vo3G9_Vi{7z(ZFXG$$Hex3uZL;9BQJtccN`vDxvX-ZnM%Nrq-obVLU=0(|< zfLyeUhVvV#T_J=FqcWZbbH%t(6-(o|q}!`B80{f{`54J86r=N0DB&p3ODw-7@Ba?N zX8%-YKNg^8OM)0ksM-AROLjB0EGTIh>s43%?$Dvn_V0IGbrt*dnP;XC9TIeCEaqIY zBj1wN92pduOf*C3FcTxe2iZp|r6-=C;M;N;qka~Lk(v^eG_PVv0C{=I4I^X_cU(`w zweYud)17m4QWdSapfvgxNAS9-MIMezKp2ymC)>0k0SRnuoyiGjTF-@#L5UWJB}h{? zT!aFoqj7IaIxha*$a`~}!s|21-k7JQ#7c4nN2w9P1w;GE(^u(jeG#vJPQ#T5mxCwP zy8UKX*zNSVO@kx8kSA)`8gTd`A~*T6`7X2c3RLzDH}AJZ{CX8s-=#~I99q1XgaI4a zA6&I6wP<~^c)@LDPSGj0k3-L`o-gjMKmBw$7`9l_Igi(4cjo$gXKeMl&jog*y5zMh zk%lRTcAC^cS|`o--dkHPZyY1Oi!O3&)z?0L>+$rU`ca13-D{A)Y5WLLfX{BAP*aH#k8DMfkp#<$al1JIUD{PMRl19vGuIA-k0e8TWv!G+> z0;kTR$7G`3?o4-Kz}c?KdrYO~y8!F4R5N80z`6g4NrXrv94-6mclk776NX3I(_*f-3R*qd}|3XLqza^j3?v(x`L-xKCUVT5aWy zZOj+!bNz)zarbuj@f$P6OgGu-H5g2t*}{I>6P+Cx$WHxM%o~{7WpvD0GHdN$de7M} z&nd>Xgtc~4qU!Jmt)WE2Wk}>BciaKKkhYb89dNs#9aX?f5&PbNoWhQ&nmukos1eSq zSZ>qM)uE9D0dE%pL6jDCQ$#De{is`r^Lh6XXmi}yFV2?dS~rfKB7|Z!!%W9kqoq(( zVom*~TaS4`=P?IA$t3KX==dx|4%7SE!Kk0IqZAS=+CL>AHO6Tec z*V2*Yq&BBJ=@Fx#Rv;(;OA_q>5wIFHv9No$%il38IIp^Z%vd})yZM=1>wIi>@b*98 zVAsBggIvcydMxC(dCCe!&8bv$#H})^NZe=#bgIUVtd3L;7u98PmHTW~U$8fkO=i(1 z^qm0k(}f~|NK@_?v=g01?lcA6c1J3fiPD#&`CV$gJCW=rJ!K*(UPbS+D4YcC!?1Op zM6D#cg+cwTErhE*)O$kF9MzlJRs;u>JH|cd_8PAfI132MfTKURbFo+qmISIHn?Gv2 z8mS@AAwIx@r=QI=7Eu=q@P(Y)L`SXh!jIa(R0s+p?Bg}^Nb?PwN$Je53$BACbihHP zvQQ{Sfz|0kqbZzo8JPO@tEy*R?ii`{L6@L{vtg=rPKD`<~dl_X+NPHSkc^v)63!S~czsOOlJ% zC5wy8eTyL*BfW|(^Uv6E!Wy^$_l+#sym5V3&h7Sxa(bX`8BpA6^<+-f<|L@5NnyUl z9&4asTa|0fBEo}LzM#|uY)OmZIpakaDh!^hf5!SICMJFlL^9yFMX?q?Mt#EypyyrD zc2nDJZFjca+xAe~lWotp{krXsG$xRQ4LZFE!j!hOfCaNq!rZgGVDz`LY1jj_q=WOF z7#?)RMC;^R1+X;bMt2QXg1%WO2-8C2LJ{jYT_lSJ2Zmc}y2YqALrj@d`1BpvJy87o zLesV8R2k(Os_{`#){@5@|4?S2LY(^;`T(ESdciC>FIB#0?wr$#gVk}(*=x2AJ`l?H z=h-Gn=PMn?G{&f?Drq}HD&%xw!ew#Tvhh@hNvDxX^1fIQ_%s4T(rZu>k;R;qqT@6R z_u^-1yPSOfj?QTbka54g_=q}Ri5BLF!WMTfqjy?tUU7R&CzoYrOsBRqCD`3b9XJDL3Y2GJxEOF2ImeIv7b-z3Qsw7e)*&Nvl*^y+Er{sMQ&@#?dqr zm@0zw|Do)?1LUg8{_#C`dfVx}_uhNmlI^?cn{2Y#^iBdvfHVjYLg-RL4m0s@L6iXtl55Cs%vF2B#YGn<6>{r>)V@8<55y>~L_IZv+=K4#?+VMan&nGzE{ zGe*mDxhkQHV4j~ySQROirhwa4t`_GUG}2k#{1Ou}>j>zPz#xem#?U1J!4bJW8V~yL zXPX`Eb9r}k_o`hncT6Yb^R#hQF^KG^`Q#T7zBI?s31KYqji*u*b9+M(U%C?&N`6z+9dxUl zp%H_lGZ=6Sx~3#*;}-t#EJM`jv1;;T)Xq)Ox?ASo-0VJB^I7DE()0+55>T3t^>d*O zs|6ea9)AU}0v8@_`a&-PCnneYZpA31$J$gjQux=urtC(SLo_>TZ9XPb4i^H;>A16l z(R4IDxL7%n4o;aZ`1?Yy)ty*K!j1dJ7InjQ8HpW0{oo``1e6+xPr(=3_3DS`jCB`E znRs}QH`AT4XxJAgF_mvXW*|=R1DaP8|LR+?_oC2dWem0UZJuExWO#IL0C zf%gFXY5!oB{emp;AxTZ4i>_K3Teg-rDZ+3j zgg_9lL55Ugo{--|e6fX{vU;>o@Hn>hiTZtB$KnOUJv@QbUGkp3JTylLtYTm9RL{&; zyj$in0`LuTj@j98*w|;YTD7=esv~H?TQwUV7@1j1RgyZL%Fz65`3~>)4TvLT(Kqx( z$eRj$oEuSjxd#6)vb2YZ0Nwa7>(Q+jYF?x z_U)_OhgK&*@f(PaveE;b`mjTK^i%+_!eCqgp>4VT8Yu^N}9>nDxT zTBB8RTH6SAHZdAyZ8!#0-xH-AYj)AxM*@#d{dN9T-X2j|XQp5pV=3Rf^@?p(CH z@{^x*SJLUz0xrLEz#o6!Iv7Y;@`c!JxzcK-MdIR;RLtj#w=a~;kn(lgP!(L`i4=8D zAfIwM%~JWhFTM1yY~M+PORm{Q9xIhbEN)}$K19xGoMHgJ(pwQb4+qE%-3hYQPh_VSG=2Mk%x4~&}d*h9tb5pv+v>E{h_v(Jp)aR zwoSSl@-Dm1%AQEBOl9Y=vzb(n+gRy8$C8Mn760sj)2HjS>XF0o5PAGDLe97OeDl0V z_z!)Dch?zwk;`JdKSFqG*WPsV-1$nS+?e%i;*3INFGYI-;XuMx<8>9;n{GO#GBqLY zmGE>cD|MODQY=_IF*WK7zr$lQ#H((nTO%{5^je#m<-ZFe?Uk}vL$_yZQFjr}*7-{J-# zlgA;|{PD<Ji@8sB*NML5Qloibgz=S>N+4l-!NNIHyKqv3~Pw?A4+wD`dl zW|cu-llK(5^9>AuIyJweC!bLwAKS;ioY!bX-C`p#B6wv~vj5CK@kIW~bLsKD-udRV z%O4_qQE$NOI6yA!ez?K*dL5>aai1Y!a(X-c?im!<`RK)cBkwxsQfe+*iE`Q&rqfD) z%B#_&me#HxjRl}y1PCsoy8uTtZ*H@{LC%ygL@KW;WNNarc}f>^`bR(hF`4lYg2@|R z`{_^Vm{dlz|AOyY1OkiM;dW=AZT=v`TwCA#Inu56on8HWGizwkT@l~qR(uyF`hu6+ z)sEOdxKB8-LA`(?nz=w-QuJ+ZkFlTlHb;Na?GDmwxC^P@kT<(I+)YlG8|zAmSW0$X z$ktFDWv{c9hEuHyME&~*SDvX-ef*Vze?8NE!` zS8*PM*47$m(1$Br;TRF9jQ$*hE1px}A4LQ_2k8K~l<=V7w)08w9v9jt_yha_Fkgzy z#}~JA3@j=L31w1b_w_y;OHq-mYDdHUdfuXm5fNI=EVl#!@cw9;| z$3RJ60c6xsLY4aItXRr_l!P~@IoM6kkZ9``3E8!tMPCniiC;hk)&09?vP^?H?VZr? zQ^Ul$tb3%^m@~N2Fgxm=oeeufjv9HRu^}_;3XU}^jilWi8Y_1ppVt&&hE}e<$ryCh zod-|Fu$f#sw>HPVxn|7`H&hCR$X#Zm#p$sa4J9@5`7AcHG0dGXY|u-jnjCp|AZ&>& zUc7pZJ(_^-%0~|QLF8!GL5sSM441>8Ke}$LH>+p3-qlB)eB4qws-%?3t=T9zzQirx z;@kj88lx7ml#*I zIF+d8hMT)5F%dywpC1@B8&>}FaBUilr`4NXm33wE_J&+8m&&frv6nVVp}SM{-cZ?@ zwOKvhY-#-sH$+wqne|$2k;IFhsAsq#Oh>fh3(@?MGiAp`fAAHE%qDxnSFU=biV$H@@+M^UrVAi%5f!+1zCFWEh%$ z)XHALGms}1jQFNd9&asJZqHcM@i=?;h6!yH&T_?G^D=enu~8XS}iJH)@%J8`^rgw^Ni#&92c_H4PpV=b=4)~ z(uO$HDD;!hUU`N6{H2$O^p#ikk4zcV3f*sBdg*q(#<6rwHcL)WDdmciJrIkQl)9*f zSAoM#RTRLmH+A_+V?$+&k-d(&cg=>w6-QR5zKMOgXYABd{Q zV~D+QnA~NvMv)xd%AL9gGL!OOGql5%z&F$&wCRk~04NaJt`RJh^Lax2Q}W`JIYx#%b-3;;Sgv@`4{Vk=o=a(sR7|5kj15Ltdxl39QY}3ZZn; zKGZ*!+;;Jr(1vZ$bpHJ6tKUbB`vR%b%3yM^P%L#B^G1ukplJB5wy;jIvTecY(@cx}brVPYLS3N8Nry^M{1rj~-ruuvl8YGiY|`Dvl`T z=(v@RoLyd_-PFou_HBjIvS@f#Afuvr3%w|}b=u)`{ZJ?zOGGqFOlqScDN~NF6(!5- zv@Tsbm$jb)>;px2LB<_HY(aq<&+(2$@MW%m)=f#bjb`3FMIu1u7};{kA;2xQkE;IdeZ8tg7-a@kO8`at#t;VBILA(o@ZnefR{(8`}#I`wE z%Qs9z$x>jR`7vhZb9{!cz5CqG#(j-P9>K4N9-7If$!pUmbINLeJuGFCA>OOpStj`C@V>3W*D494NBOCvpYIx-|68 zN05&|=U|q&tTgI%;WE&s>)v75wG?6_Z8rd9#JVXIAmTa9q|bqCJSy>8C<#_aQ@3cT zcMD)tb>e(KK{c!gqrNG`&jS5D=9Nv*f%s zrpnup2emv8jV8ty%zau!k_KO(8m z(MsLkJIm|#X7((Z7f-Q1;EkxG=d1e=d&nSeJl$iW)fW`H5JzD&*hEbNPB#Jvqd#=A z6{W90kn?vPZx$b4yh526k6y@K8VC|S1#l0u;MDHMrB{fP$GPVjipnH_P-9Lpr^QXiL^&9@Sa3g_x1HX00dztbz-I5ZcRIE1|RXw z5X%%^z(UY51N}?x;7L$N8Jg~H=|6sP6bMwi!MZTUy#*5PH(nFk@0|V#2t)M%C85iQ zBaA~MP#*K%;-Q72r7@ecd5Bqc7dhp?53X*$zzCWLm@O=iDIECnn~xkKTdx~WM1?C> z#0w`fzsCHJC1rOc>q@7z=H$L}7t4BO0?YZO#+z?8O60?n^5tSSzoEz;wqfqpicX0% zuhU2*+80h>4@Hc2SKL7-s$yL^_$!~mY;j449vuUGT%{MqT~1%2xxFpki==Um065Pb zwPB#(aHJ0fs6`KW(n5WjaG2)|^)^LOZ2$!r;0~M-K^ZpJ9H{l*2*~pugT<0_>&>oL zFr$@ma#^UV8~s`-qnGj-SVwf@>UA2imjTO4Z$ z;u@RI`skJE^49O&d#_B*qkcbtNDcdQ7$|tt6&r?k7K~Hh;5F+$Vw?o$=%}yDF>8yWtB* z8Gu+?4^q?se@&t1!h^73r#rK3nR>7leb^oky7@?7;ttMI6N2 zHU+)K3+6)ocnGRO>7oP!uOAm(;^@Z>B81BiXa;Uo1TO7W{)P2CtYW`9tn?%`YWsNx zsZv$3E2NK|Gd+iyKGr6?>ViS6wt1LYUJlQj8Cp_vo1MAPf>g-1*%WgreXiiW--xGT z1q94at__Um9jZg+HSR`oNA-P5Yu<& z)`k*+MC_}ri==&0zTxCj0;%ponKd3l6h2-nr5Z-BL*aGCNq$C;yFci(EE$f6-A+TN zMy%%vyZDLH?bTwsSfYIl(~x1$!iOXSl!K@tTgmxIX)eog+CghB7GQ>Wc58gwF$X9h z2v`!ZX4tXE`V|Mkw78Ao^s^%RAJBmJbkLi_<0t`eDS&bE_6x;+vpwxK%Q_wGi(x@7 z3MVhaRA=2nn227LQ)?x16T2#;H8SHQHhGEU{0nAeI_F2p`hBA#C?j69^q@<|K3M5K zdDEua)3s&SKg}KFe5HP>R=3V5nB4uZboi&5uZF*Hq0@5Q;mW{GkL*5`;=!Vrm~-5sH=WkyM|vQz$&;lL}@4ZWC2b|vGtFIP22Q(_NSR&Nx2gtjuFO0@B@bS~w z09w4OEhI*+E>vzf>oK|4TPa!ve4R2BG%y4CpRJlZG<)#S?%=kodq-xBtg7C3UzL6N z8hU);(8Ll6*P}n|$qtYR8hX*5|2Oi^W?0zLbjmvVDpDi@FPPSs^eu2vy#e|LH>rbD z{eibMnVUKBdiKn_o_w;aj>x^QzNzc5~+x!+Yp@ny-oBX4j#INY;x}xjii@W}Q z*(IBH@9>_se%XGq?YFnDZ}ujk+_DAoqvj96pG4qKH}N)OCXWIhzhhJ1p#jnjJN_H+ zsFcG0Kiv~ZUWu+8-ZS*xV_igMtjZ*}oIjuZX$p=RfcIPFABKAHqpS<99T_@lhUWO&;dD zg-~jMhR1PSPR%=}3$_$L@RQ@B>h*2=CDa@b=Ff@w+=MKT?_Aa!oG$ zra5SHSxSL;DO9lNW5#>JGm@L~-!xn6`KnRR{1zjce)Ersb)lYYVg^hIq1@Kvl>k|V zb-8X$^G0UPAgVPrF>*^Z_VD^um9kQ~WvDcI94j z+M9#5mlOTjh~45jAZBP58BfW#d1}_3X6CObROTb^;YQvAz!y5zn^rM*z>meT$F`o! zVJvkE28ALllp}LW>KwUq_9~ZANI5C^1e^qJico&ae&bUBA4%Crc@EKML()KX2~X(IiIpRz6xM{OE|*Ay;CmFp2MLUd(J~-(eq||7E%!`n^F+&iU1^ z*gyU3XD__)-|2sAW%8g@^DFG%JZEeGKAd%BVqRi? z;v9)mpE4SyQZ+$IMkc*?Uh|i>kWKsxOdWe0Q}uTGd@u^6Tz{*Rc|1tr*6g7q{PvZO z9@MYQ`=9j8S zZfU+Wi4K{}RQKRC3(8|bEnGl@$+%PoT`rz{M`uZ=iZRk3aK%IuA+w`-a)T#b%Km*< zt;YVmRy(!H)EGm?7Km6UM~FW%*F8Scyqw1q4$FlG=J_vw$y?pzEnzy?4?kkRmC2AL zf5o_vI^}7!H^_~=$Drd!;l)I65YF)6=$Uddq;7Lu4cN|6Fk}>B5}etJ+*LhA|6@OG zBM&SNt$T}{=Z|-I@6^VrfO2A#>o4OcmreRYg|&>Qw8h)b!}J6#67s-cUAN*1HpT8xK-(L<~E z?OT0FY0rb6)WoHdbZwZ;1{|Xzn8TcbXu(Q1lTgvciXa7A#q`yb3fFuKgxt* zv~vsPOfqiKG%5p!WJ`U4J&VOr45dFr#%yN<0-?la$~n3OHM1^^?o7x-A?%z7ursTx z)rZf&)o83@h9!{-(TUE?-11L$KDm#cq*$BPKjhA8lp<)rUQ5|%HCnFT@@_W!?v|?= zQ><$ABDsc`Mho&*uHQX6-Xe0f>P%>@x z+Uf=E#-Y(Uxn+~Fa%%L0xF7DIK(;<`@7zWU{tV~rUD!Zlm}Ft0`gH6pBS#8|XX>1kFIOju!GKf_nfRYUnW zL7q~`Ubc9`IaMb{1TjW!`7e{DU2>VMv!vJhneSb<`uy`(U-!Yt^SvCco(WprggGoS z*yDKT_^(H}&hp4BFk&2Li(K&=i-Iq-#bKuXS5%j$qzZnc_g-7USac$*u;Z6l+)jnk z&OUYOPuOQFmHl6SJ7c%`E~+qprM6&Cx9cKTrSdG>JhxI|#=o=Y)?4>{hyAO`~&QUz+xt{P^|QaL z66)cpvcFhxV!D_u+jgI7i0}d-=fTQ>$|V)%=Ecr~2j4t!;N?p$A$OZSR&TvlOsM5n zo3^f~v@};yN79p;6V3=aR8i^uQ9avHt<_jn%V<*0H4I2CYmWH5*8Gls=x=A9Dv99I<>}HWar2cBFycp zJpCH^^-o_&hx|&N|Ih^&ys&Q{GymRG@3>>@UCkwz^=)2t#jn@To|qp(9r-Gvx^&Ch z!F^(}Hnh^cI}m?ziA*Dx-h=@c0%sLTfc)iwzx9_&{VxU3h&M>WOO`AFOvD}Iz}aVb zX0D!136Cm`q=w-&i2Q~9Zu^aU-ma!A=_SlB zOpsMG`_QTK3}A=B@xnEc`kGAOD&BMLn1rb1 z+`$8$V4qt#eL!H2VYggjZI(=aPo5BJloJ}eKiruKIt^}tQoxR!C6}A3dWqx@?A7Gw zccVK!?NFbPBW;04q-sqnY=2s3EhrVHmtm!fDl!4@s}9;ELY)0`wN@=xmAaB%r*V@R zwP(kNY4#o(+nDF=VLA-9k|Cj2v%jkp$>MsxhaFPtkO7UQ+AaO1{@HUip~$REA?m7& zmZj22Q%P8LX-3Nin2pVA$ve&0d<{caCIbB*c)t+(^CLVRx35R-0@^JSEQp?9Laisq za2O1z?ee3A9LEEs2ziTXudP@X)rXmTYoC7l`dOLa)qj8X+21l9@BiQjM0}Gmb-_D? z*F66>^>nRJ|Lanx?lqEHx|IFd)YO6T@hC*#?sI3ZV$@UqCw?CDqnpFho~r?tfG=q%JAd`YVCbDj+-XrRIA z8sawV!Xl4~3s^wNl1IKa6{kgrUhp%D1sv^)vJkI+AUZycJ9#&+U1Q#9>dhv*O=~u6 zI>+!AM)wz~Q1JN1Sb8ZdaH->|Kq!~2hZdH*VrN$guSO)*<{Kfj1X+@eZjGf2Cxpj) z((LE21W{IC)k(7}mV5*zjf5lx@#E7_3WZ!(xMrh(klT`IZJM{u>?k|np;JHr^& zn@bjBgZ1A2Yge9s{?aQ=GP!y<5@1u|Mr~VQ@x1)@GdU3jfx&XkSe;AqqX|1a^Z4-5IJ*g4Ig3U zY3kTqd%KpYBx`B)f-V;-8F^Eayom+86Uwor#f=-6uM;dEOz_$ zp3>a8cdvgeR~b8hc?pEBWzK4zjnU3!#_SGcI+f-<<+DoPc>jH3CKIO>Ph{`^M(M2b zImPCW%AYuO@kl&1xkNU&HdgnABNWGzz>nvFAALvXS)WCD7!n&OH*ZIW;IDyF#i0YI zU2)z3D#EyRP+u<L~uJ$KHuC>YKI*NP!6MxdaJOO6R=rnAf3Hdc%tXk^IFM|A=;RCqfd4Ct?;; zg1i!Sdry4!@~}hF)2T9@(&dF)Ft%_doU!>>tx{|x><+U#*KZ8E17Y)x>#n?V-HlnR zfnQ)fF~WO$WN{=H?=DU3+&NJijTNJdHbj;$7(4Z}nTy85(OWjXQz&dG6iN$5tbq{B z5k5C-_;Owu9U@w@3uz<4R?L9|?_`@Ftg}o9Bxu&^}xgKGa+S5<7@g)aS8Rm=1 zq_N+D>cpk&UgoJ=-~K639^Y#{hTz%rFuM-y{OCJSTB7+xv{8dL~BNlCOOq>IuJlbJm?qf>exL;ji%#3 zgqzx>AY3Gz-a=zIw7d=*&l&kf5)=v)iblS1N@M5HyrDuZXHaN6ZxdeRHkJ*c!@i)} z7&kqt-7RE4bXv-4x%x!kq``OCN39XAWV;nbG&o_PxgCxjC{ zvqnwMMdSWR%$EpUI^HS424>DLdbNcDT4W&=AC^AD>E;(PV ze7DN7->dRs%p+&u*uK4RvLrv_#QW~Mp^;7QEGXjDMnq9Km3?p9`ezqNs#5FuXfyA8 zm+01Sqs4bZ&=!q3rJ*&Nj>f<{{6GUr``Z7Bia@p&r=lx>=%bvU(+dB0?iW$WRBGFh zLQw&gRrW1RY~Hy2jxufF`0A<4Q>jQblr34#G1Ut3IppE#!5sAQXYexU$s%?>^KZ}> z##TJ`7`uJ)dyO&j$)8pg=d#Kf-e4l*4Ei0Gq|csWPMs`F6=1mtIy{I`Qa;fQPSKjs z-GOKsHA{{eTM`d11$)WO#xgf+(8a3`$ z%16pWa~)SNdW{H-hP$qy&EG4}RfwMb^M3YLwVpBmI$7wRYo6?~=wj?WiRv%Pe;y4l zVq@ejdhUDfxvaP*;W?Lo@WI_bP6l(`g9W3DbRbg+N1^dmht-A=@J@5V<31;+Q;|j@ zP^(45KP%mo|7HXH8`-bG#cSXn5MkAGkohvnX?$A%4vI~%E$Q~qG+b!>yf{6}}5O5NKmcej$P^-1oFa?I(a&}&kv0Y--D>D@o zN6fn?G(V%3NQEzb4;#wM@|gpdFk+!3XiTdtdZ!dk3BXACOhTS$zFA3@)>qaqca^l7JxhH^jhXz;2N*K zSRw)|ChwS}>|G0urfex=*X8u4#jtrul>++7$1t-e;Lcf)2`f{esj@`Omyo(hs_-{2 zq{TaYG0#>l7b;uJ+vY7fUycHMS;3~+F?ubI|L}!2y*?caEsm#GZJeJ;77Jt_`?M9s zz^C0>uBGi4jm*#@Qa5{WYX01Tq4Ftp-^emcy;O;2L-3!;r04jS+WqfTQ8A+@+Yr>b zl}$3T?|g?{x`=TUYyzHEXDP)CYWZPC-MR-Ko0UR!E20#H$w-0FCjF-sSs5uB*OG0|JhSrL>oj&(F@MGax3g2JP!+4gy}4qz z-rMKVs|4JqojRR)e1{txRQ2PkuJ<_Kf@nE=gd zzOK5!CZHN)9xSKLXJHjl#)Okk1q_A+<0<*a3d`P8KVfpe@_3R4DEp~EqgFmWvb!pE zIWZVUI2zO{R7_w0*o5WTGhtBHx&~$%uiuEm0rnA8HB<`aEtQie7;*hT{hoX9>&E%) z>N;a=UQuQAqMwqPe>D^hnNmrw4#y1^eK4N!o!FP3y(N~JfVZ$$tJ0PfhM3!DE0!m$ zYKhwr7;|(k54@Do8SD*0KM@t-e-jZa8-ag z-iNpvpxN!bjqTtZEiIqsT(p)APyma8v>53_p)!58ZAhpO)Rv?^8S3NKQ6_@qS-j<@ zzI${^Y3b%IoNkLR&XbRiM@2&`dxm*qhHysQPQL1>u&DH?eg2_NxMAhbWpCbp|66a} zaYx~LimaqAHRQ=d51sd9_+!SdP{{NZO)e9{U}0pZ2hyQTiYJiyQS_PJevpQK|B?C{! znH~FR{ZuPT-9|c^Fx+xILa719LFI4;}z@w7k0L69s}Ca zQ6~o#aMX80soOJ(sQom{aj7)-m&HhZB^@15G$RfIch*}A*EI?Zo?o0Q4;sFjKZowfzG^{!_a-M^SaY!CSl43t%qOuB)iJc6Ulq8ep246J=arBJn( zJhqh{rD2y&jeX#w9|N_w3LPov6kKfls1FQmw@u0%qP9TY5_BTUc`47O7>wzkTzV0= zGk856A12w#^Ut4qE~?Cq9Jy#-z1LMj;};%TcEEMnvSc(l%XQInzpuRf^3`O~g|9W= z{V9&A=bkG~l6i9qn>H2Z%<7plF{MHkpOAn3n$+4WXJw1M-hWM&PDOcku5??mcw6a_ zWgkE#XOQ!E3*vz}V9sk8FJu4Zw33(+{Qh^THX2 z&1E~|dCSwM8MOx08&b0}7z+Alm$NrJ*2kmjAHT^j^PjLQl!CHA+3EbT)zxJ=UE@Ov5*qL$`dNvPddO$7+X}gin=el4(0?I6xxTOJ$evGEApU_JuE*%-6aaCzTg1TDNQe4Y%yyxe9%!BC-<0*c^3< zuuP&Eovmb?jCrKN4DvQG=&;MaEERKx-NH_jKT;lQWM}%v8aBOFfe`|(z?#t3aF9H& zHhGa%Ar({vW+$qv;3h_nMTwscC-bJ|{m5Iygie!V*D8^uV#~%oTzpu_^{xLluLqOn zV!-Ph@}X;pX`r++NHaaqSD}c!ZpsiSPo?Tbi?_Dz96h3?6t8ho^`Acea9iaBaymMw z7^)fQl(*K{C&(a@bbmpD+z`1hz91`aJA&gs-@(D$!anz(7hRA18MpAPA;;QUbJw0d zm0r^5Dfjo6d)OCxD`f9raqLGpDK9ibKHrO)0U5HZXr*29n03~cDez3{yg~e?1){B0 zaGDF20cD@YT{sp--SrgFzTW1L*HI95yIqyVTPw|1wq-VwGc?+Q3ge14G?$Wby=?4KFP*}Wd-nia)m#r8ZaGxR-LlO{s8*7S~aQLCk;Vjo)*w+!qJ zimqA-Ivly?sl24#b?9{5c5wU}it^a6Yh+=^J+84Qyg81RNX0&I46r=H2+M_Nfr~mo^M}Mx$CY9`=48x zz5;+1#s$PV{t*T!%SB?XGwd4Qd9wNU z!H_%HNL@Sy6 z4AP@!D-d^j64xrdv)8Et{Fq0KW}xJV}8wQ zGdZq4qQhL!N7zU7M8|&L&?zVL1{c?653T<4t8K}RWUpM8M>Q|Xe#z=P*(q}S(esw! zg~NR-lzO8(WYkLwBDKb*LQX&claK>y0}?6=803~=e}srtv@dH^D;3PU?7b~WU4U$l zs92`r@$%mf$Aj_6C3%lGNA*Q7WYpt;iwC@@H35?2NNp_#U;$^M)*oG`oAy_M?Gh|@ za4Kc70PwYf;;kA3YF^?Nh$$+Mk59O!!iU0`*6Y&%PPWWyMLWc}7!DVIw(BttqSIVkKESq1UDT1>um{_UfyRwarh@Wgook&O593Z1~>y z-iX8srcRMj>ohnR#emNr4$d|$9MeDblwv60u~*Gnql(98AH8Dfime>?by!^nW2d9~ zRR_Bwuhoe9B?fZ1^5BD&O?~BZUuk5d^y`rwC$BIUotiu~S2*5vGis5o9X;V!)3vmS^nmdaz8_ zEC#>6iT5(<#Hf`qjB324nEkUAU9YAsH7xL$bAeJ5rA9~??t#C-$RKn1nFRQoLZVbVcGL|G#vFztn zsaoqH`L*F_P^EFy*7$Wf_L*(@jpVFAMb1M4{4!SGo&VNts<61=i5p;{d&Q;=|sUCB<7E9EmPgLCX zpu>_knMcs-Pf!(h&mV9{P1{HG=mbUHy<4kSr{wyC(;q3U8)HB14u^U$m)nNDdk%Fz zPUxHSxw%+dJ9c5x%K04^cU;zSCH50(?lcP5(r~DOM{OsLMyMSGOA38eIcD1C4xALD z{1FsFcY$M^lukeZIHzfhkLK%KjKpZ5TSZk?8X;~euYS61*mL}Daat%t+()%c^Okwu zGS$f|*=%vEv{oVg4~PnILA?AE!ME8R-xNN>zR2TCb@gOfBSAvqB`S$DnkZTKeB=Peu&STon)Y^i+ytE&bJzk-_`5PoaStD9@-ygCB%>l4+b$fCHLZsxyVxX!P(hb zYV{`P>SeKVVub9oYdYUcsS)9JLy)0+3w z>9=Q%X_gEHV-CN^I%AH*Yp~l~d7Fhq2BZSfb=A(b|Hv;|yB0DJIz|-y?m6aD=-I7~ zuMm+GtTZw}S)13#(F16cgD;2+TiM@9tcFg_%k0Cp=6>X;t}2UQz*v)Kz9GFot6o})g(zR49^axSdea^_?&@5YY7LgTZ9o)uVNOrP6 z{e5F2oTe4#JI&!Uu}e(9DK={&IXO{Xp&RgT+o+REOnTSE_}GH#%)zd{A6y=BR?yy( zd8FCgh}q>o`SeN3&uKm$U0)Mc67^M#AX|;jA)|Hyw@NCeA&vg8E2X+NBJSWP0-*F! zr5$()^_}_T#s(ID^$(c^^8fqt+0RBsu3sizNTD!3FmggTsnrE3oahmk4NB7_d?E1p%c+AaAPixLHTc%QW{#q zg8u%O!-69<(DyVROshR0I51+4f+-C;@r+A`p-C>>wRGfl0gfLEcS@X`%gnWQg15C}Np+lPvAv$y_@VB9$vGa0f4G9VBRMa*jmv!>*7342C+){!?8*^teg zYQ85`XpJT><_a%8Z}jxDpD2Z^o3rILrEi|TX{he7_s?eDXKGgKm+@ev~iHA@ZWyFm!%4(@pqu^st=IH3GY@p#3jf$Fe4isc%uk>ZY(W6C!P+|p zvlUy;B?Gq2Dm@;lyu=0AXb97zvU_?3ROr$lua=`XHSf?r=B=GGDV(hJY*4KoUV#2D zyN+g9Ji`9k9FMwuk-&n0zi|1Nzm&g><^qbvCYvW93V9uNk4OaJ$dK0zue?tzYxZJM zzcBmCUNoAj*h#rS?nJJ|ywZxoygr-B8VL8_x&rDjpt?}Ij|w-@!Y=ou8=)Q zEQbs1KPexi>wN~b@HSL(EJJK>9k>KEfmX-M_R_I}mYPQKl%lnOI_beHx!?tqSzJJI zAG7Yho<({5+NU)eZmI;wDbIH}*PQY%*${@k#tUJeBm9$)M=sJ^oUtvbR01`Czhl3U z|LwZU8-pmrM5QE$0+jW&DTNY>h-xd~w=l;!48xFTWQa3C z5v_ux@p(Rw&JO{j2Cns)Jjacv@fj~aCz`&;f=;~?s#v>RJiWCnf(Or&_CU; z6fg9il&|~a6On|V&ul{!eNiWnwk7zB(~{+JiFCz53K!9Y&jX%3Y!HdZT@={(;SyQE@| zR$7v%T?#dy@18x^b=JAIv3ZVLm{k^s;hvvuNvRaVE~&%2-~wX3sr=ia)!uIBMWxb3 z3Sn<#ZYZ1bH~&2$>XI2|d?XN9b+S?Cf98m#e1XKMw09#5*K9C51$@81DO%?lu<=Au ztGQER){Onv6)?!nLSFE7nU3%*Qlmu3*I5k4fJwm9bsqU1guO_Tsb-xsq^6LST_VLa zp-!bC_@u-um0sf4%Vj$MRXQVINb#tO{_wPiO9|6tTmCTGKk*DIpSbOAB^hx}92$sf zIczvDS?ljK`GSg0P!jC{j{W;zj0pRB9N~f!QS}baoK`k#%cV>yk$U?nhvJ^!;?ZZb zCG$ROZzMfr-=0ffaO49W5x)I>PXIcj~Wr`rp^eZr3sj_V1fFH?C^zWPiVR@6%_Vx$vs1)^ShPs%0jVArbb| z**#&sjo1=rZ`yUTd;8kur;$@Ig`-pL&H9V%zcUH;1DRYRj}vt#cv~Nc+U6PW9pIST z-|znQIwrM#eYT1t%Wg+55qZ7v5n|Vr#|b-n(Ff~3#Ky#^>I$X#T%p1i_cvKV6Br!7qw^x|0F z$!=nz26oOw)j%Fu@Kq@18NJ{5+||v;0|tkwRI+z5%-TBp(!;l#mqpxtC;ibzPPYD~ zF(Vn!C~UqTv3nK~reEs7c<6qTlFw%UP-9n`Rlc#}tbv1iD2)zl&PB5BXKRDGh*n|s zF?TG-=ru9sWZ#55{wiW8TCN7W4BtGh)jGU?+06IZaimgV*2%z;l=G}l&mO7rRKmJSsjebc&PYI7HR2f3F0 z57UqS8rQS8o%(6>4jq}RDZhz>{nu+a7)QqU14HDV%$s}m+;<U$Eco@~x zxutN*3J8MoJ-mm1SULVge>nY|i)9{t1qig?)o{D&4g|joM9dWQx)p{2zhgL|v)yLy z8ri#iym>V_OJ&JNRpg{2UPo${)|PeYa<=Q6?qA$AHh*BXYt86_S%=;>E^G|c_FV3~ z>GbW_lMjCE*}ef8OWltQxx$_l`Ol6u(^OnK^%S%fo4yjY_mQ2R;MWydPj?)6TVWR>hrjN5$(+dEOZzJK{+rI zz~e3|U*L!J};Dh-$smV?$Zfx3uRC2MQ7N;EicLxA>)IA-r;^<3sHCpM@c0h8fU zx8MS85en`?Rx$O61|d(V@eDQv6eBty=?Q0gjRLCJdQ~GjK|r;d8eFG*tYH5 zxtj*vA+<3UU)JY~uRT&1&QH47YZ#tbl}yK!bYSMYle4cX5n<)3s~BGd>n#oYXO?Wv z-uFkVcAq2eyZq8`mnQQQ6Zy#!TbO6#nvHpeeSe}hTs`+>@7A4# z8NK9CQ@(ghCJnp!^)C)kToNNbeu(#7(1;QiVjQ)-G|oQ{)-&h-J*LY6TWHJyK%o>5 z^ru2vkIdpWrf?9#dpu5jPNNS^$#S>eg1eo;3vx1&8A-=;8xv@~SdA}Y!}rqx{P{p4L`)@3bLg+ZqlbNpc zg?IUeX1uv`=g!(?mt6)J&>4h}pf-eux=bHvr2&5mA_4^Lkn19Rbl0iVKtx+cQ*Hsy zf|r4owtMsv8jET%6ENMQ9F=Rbfcv;Vk)=$}YGkg&qc*p#WV2!Lh}mZOB)wpMzG6iY zr^9cuTi#1lEKW;|UQ)I?Odj@{dcGXaB{J3SXGoU)FVn}e#MkVuhZ?b2l^KP6y1;zD zUS}tY2qhI_nX<7I4~8SYP|CMq<(iGgLQ;qPFK;-#aGtxIY+q7L_+rIc-{?%wyuR6u z=4-?&a(AX0%=pUn9^Ooh%@zvusyC)0cE8bNaztZuTn>3vq&E1# zGL+SiKc2l!XBLR8Qc<&7wa6uUZ-~M$ohp%sR@cYSL-^7s8 zMd1r{Y8=SS?CKdCa}CU#UvIt%R}P{rW|PxZi{vuFjZ+IQ6Kr(VeCt6JYKns?1rLWNZSVm7sLOd8WNe=M@cnU@-pMOJkbzy~TE$ift48Ivtfj5&+X+Fc0s z*Bt?8TGrduH^n}&?Kbu@vSm(RcW)l8-Mk~qiN3UFPl^5K`k6kj(??{b;Ou#!^Iwq7 zuLOD?qA+!GFukV@tSAMRD-fn7ik!dmAp96*SmLeOT@$cSkHyllP>w561Z%DOsA2=a z09waK)Tev*9)%9>n6Bod+8|sE6tW1E92#$oRC^Z%lfk@YLEJlD4LbbRq{SKC)ji8> zO_U=^Th1sWcd$JHs64M}6(Ta)n9L9FZ{}Mt3e2qbAR)@E?X{7!DbJyJ-He2ddG(6j2P0oLO=VK*CGr^QQCI*W3<^Il%S&4MPWbkkLk16)? zYDQh!i_YcZMcp0@SQq>^{9tH%IxT*ggpqrdF{tdsiXtQtNum!!^ZrWG;Av4}=eHz8 z(W%Tu5ds%)QLCJJG&y?;ZL%x=&}!-^mdF)3t=#bWmk*vknF$rEy*Z_V#}jv}ZC+R0 zdCi(k&*!t|d##0XB%bf8gqD)W^Cnk|3XLiTo9q}73zQ18v&UREDG#kX;Ty#w z5tPL|fyQ94`^<^**}HFcjgHJlh7RO}8+zz>frE4g^ajLETUnZ1tp&|)2Lt2kEok}+ z`nCmINC_2!0~~Y+3KGf{IWSTXagDY(K_Y6gM$kSw%^Z`S{NqBZfuI5RB8LxqkSjve z0>m`POjUF8%ZSjyV}t^e!hC;~_@pvT#BXDZ8`7ymQYv_tf7JOVy}m-PW9}>_*Sr4YxA83Og`U zJ>yCA$5yR5qiZtAv+TCvluK(gZcI6S4m%s*j9P6oL*7eY5X3Tb@}HcKwk%ENipk8n z6#*tCbfdfCq0rvvt#e^gx}1L5tIEc+=}ah{9-7=a!`j{4nFB4W+JBkh;#+ri#c;6$ z?peJD`u9)+^$Id`C+dTV_g2qJgJ}v8-UM}Qw0Sx=XZM*5^mi1ycJQGp%2ywHh(M6f+CDLv=;v#takE}q7+Y9YNZ0Gz zI;s(i+tSkKpV93MI`Xc$3!)G|ufkv)M(v^-s>oBl;RYR4&9hicovMKJ>5|EXuA$G+ z%T)?rCQ}Tmv`P#8YLLZ3K3B=W;#jqEX}3u+q_FAFu@7N#BB8*G4qwzybP(vqv%pAs1R{)_(Zmhq(6D z414O`(9?-g*(O1741%&)Khdv%H` zSHKgYPwt`TqeE0-Y#!|{6wuCX%Q8z>Y}nJ%yMh-9J3}q>kMw7lN6tL{9^(#z6@MX1;=+pgYmVf6yN?yv?ct zkQ3&lS;t=oIl_ zl`G2!0cZfQU(D#Qwb9L$A1a$S--4(n2)wh9h>oDQ>8sH7Tes4`Y&y!wm%un(8IMy} z)Rs`QXeF#q5cZ>I8Ckee2*|MDx+u));|32@DKwsgjZ^1$;!hEi8@?)dVxU12&<+5O z#v9Fl;qqke(fF`S#^ZqN*fPVpwzU@!U{RM^sF0RKfTi^;{imhxc!oQxiFK{JCa;b_ zeOoK~k>(;#AQ@OVE!H)|6Y@s&!D&H%HWf{GS8OV)9V$Kol^rVzk#z01RAlP>)l+8c zL<%Sn=Rlv`HH>?K-(AopHIrZr)F)$t9_E|?uZCz5xejs_gaI+)5`!q}6OalZBLCuC zx>Rh{336a88wP}P>p0*nXe)Q%xEyuFUqvlu`tm93mR>14b7VCmC@b|{TXVL zs=q=!Wk!eIfAxU}iud3D%IRu}*gJ5SK$dabFw?JBo>$`l=8v++-y{V%R@7xR`)=v- z{q>!95Ob>1_VnAAT=L9FW*|R1pw;k7Jh4Q}WJ-BTslU{g(|KQfQ4!9ewrYCOqI5Oo z(W~Ssi%T^#B}qF{IXo!E)Y%tkOX(e&^kc8mJN-dlcz{yQ%R5`&f#T5mte8h3&dDPv zfhzDh>k+EDMF-hyAQ{jF(x#xd5r{6%6+m9Lu2t})9UvKTxMAxfn0?p)1|b_Sh6I@> zh8v9UAvJ!CdurDsDc_nh>7dK#C)$nfulRJL78eo4eBH`kslQ%}-QUP#|~(Z?Ut$EhlP?04sX{4v$}^sc+^+Vym8 zYBEV}tL;K}mfwE6T$o8m(OWYMn`guSIso>}VZaWRf#0hGKctTRaiSv5jmkk}#S)%wbSz6U<9D|r{`lvF41vfzUhX@bcE-x!}b9DpPoem+|`C4b8FTVccT zQ8IRap(S4$boiYi?e!Zx28XHx9my_>Hf1_$i;OuQk5bd`pa4F9kedwFZg(3DHX0j4 zxrGbQr2bTIj(+%IdTbUt7cuDC*GG=f+cX-Z(`^(Ac>Jt6Yc+d(iM9OzCMNrv3eBVU zLXsgH&e2<}W`ibP{(cU=VZY1@7(fp~ZD52%PBCXrg3*CLJAU5Q1$w!Dr4#qqP9SyZ zIAcmoYP?>W#?{{k^=SWrwDe0T3m!am;I6xh-xm)bzM7ykw9ro=Be3d%nsi+fiqma5 zAy~CCH20_Pzh4Ou{y)C-Qgb$y4W~ml2A5<>SJsl?jUfpBq_Wi4^aKBVj+98HtqL8J zZO!eBUIDn*un+K{zUgw%T_sp&UqkBZ7*2rq!GaBYBF_N(H;5UmGw$Y`xRQFC01WFH z4-Vt`bM+m=LX63gmU!K7s|I8EF>}&9NTPl@8{Wy4xplAIdxNaQPf$4VjXckG95$?st z98BTEu^hPW@lZH15+E-}9??5S0?x_uJp@I~uRTjN@CHn{S~XdZjw5F!Oefk#+#?1kVH3pp;^ z9zSyER-rrK_VCc&!CJUb;U_{$4Ns_X`29WT%pGXw8R&YXxt?+Mxzz5nsY~A$7mnR^ z6UBd+zTpA5l}c+{TR)^%V9?O|Nj;np*Ij z=-;P8@o+RrAH9l_X1q%+%n}^O5OM4-$srO_fiDIp*uFC*A4DbS8y!6Mpcgo`L#d-1}6&ay!Y};QU=(L*rUf5IN#U0h8kE1p5d_ zWgrfS50GE7EyKnP`m3JS1MLPA!}0J=7!?VEN#ca?g8CaOGI`J{RC1$o?IOxwqWjUk z^nT>X-+eb7YMm14bK0rP=eFD2rfA8nl)7z`m)b_dj-E38C)5$F%#ewd$o3Cowrdya zq(5H%;<06Or(|5dK%BZa$CF6}0_9MSK9_{Oq#rmgyl{SU+QMayu}Gq}-s6^QWadYo z(u#N*xzC-p$s`s}HU-Wp%5ijQ4x69NRyvulexP49wv^0KX8}InVaCP47fzD7B~|-4 zwoIy>F(=nk77Z{y8#WZ{^VlGnft?I&Jd(66C@LZ^;7(vwMc`aXh6_5&;6q4nnU>g zWZV3FAkT^El=&&s!b_L+mqfYR-yzxFEr3yH$G*)flrK<)+VfZ^aKN{_0_IjE8hvDV z!XDP`7||K73}0})!~_cpKCyZpCz58Onx`Qy@Jk5tL4pRrd`an7J#g}e@~H8l*uCnT zEPHRgwKSuA?X^dsa}=+8gMibgu&AoVF>;PkON?@ZMX&qECWy}fJkb?o;o<-fRltFoPl(1 z5Ak@(b3%Bu)N4hh{?aSx)m;f$vBehlRIH&D^pO%eT)V2jpYjra>f>A){b=(ToNoN` z3A4)f+e~&Bi*5B;Gk1-7+-R9rrq->!FXOODG-{;zNuRCJi9B`P<*P4uD2f5KPG!w$ z|4Hu-g-{RuAoVwzH(aG!!Nid3Wx4>pGDmgl<$7R&7Va8Mm9+B#Jm zzAJDp{0MKpn3ni(0hOt*i|1k3hVzO~)!v3qUy(#xo?AW|b@?s5=tcTv4qt(;gY*%_ z;3ckJp1zqn!Gax2{U0@;It5da zSy?vVbhEZXBZ63Fn}7Q0P1y={8SNAbxPtYmt@M(jSev|$8r!-xKLoctZACHqP4sJ8 z*Vc~9-Q{8r>b_bp6(__B%Vr$Nf}5tKfST-LctkfF#yZ(4=L_`R1}j(Iu2t&;(Pajm zB^vi|g9b3jSxcuYT*)!&({|wN;p0z2{~sItyJlF2(=d_@_TY%#9CvPr5r8*9pedxT zF*XGJ3RVQ)HFPNMTxmoc6EcXU;01!^sH;qp5pPruk@w?OY*-s$PvUoB-X~ghGo!;Z zGNwx@TSBRXR;TujOi}1mLcU)rHl|@Bmd*vE4)~g;c(;NJ!wS~cqEqtE7Pb`D=b@0* zVlxY2lxRi+duU)N+CMg;f_bCOF>S%Kajwo_4%D1erciIt?`5N-z3FZiFU8{tqzNvI zHx0m~3{s)ppmINXi%0KrSeiXC_rR!q&1kSm??!iKYY(MM)KKk#XS1)qilpnai?e95 zJE~C2G%r8rbp>3mq1ovv;uI933ZzL1cX~Pov+#z>a{P!ec4QFeZ^$%@T6>JPWq-n-#hYEA7_rm>9;$CsV7bRSOo}vV8e1V5p9bQ7^BBz^8R>xqOVSoK>1Lr*u~3 zMh+9kNr+H0AUV+gsL&s(^;NT08>X&ZTf7zD;8MVb2ps~h8X>QromYd zG=Etoucp$?0W?s?S605WGuxRR!q;Mj+MatE9*8ePXbM zp%bfeV((!Ek8MM&S+I|Uu(~48@s*vlYB+a+`l)%fZy~mP)aH!&tamy&KA(whEm0|@ zs`=vL{)e;*h)m}k?p1iY3lD|}c2zq26|HW|jvdX*LGmF5OuY(~8sEM7CjC=$PMs~# z?=srC3Q*bo=s)PSA&KPeBS*4Mw^Zp>b!W0@CH*c}$lwUuELwxELk?DHwG63|NatG5irw%kY}(+;^)ANL_Oj#D&|pqU@sC(K1>6Ag7(FR z7J1nadaMG#>cSQm_JT6wK6`8vl#U9{`Qpwy%hz4^%;`CFHjKB> zNQ+#F%vDwZvLe;PfT4HLUE*~}MG9{q$U!OifUBq!SEkWQGnJ&#WVeY`?vP9<5o%oq zZ&syaax8Lnw03@BBoEU^&Fj}^D;bwg&CE1sy1FvW*>lc$Y32Goo|#trw)7xO36Cvl z<_hF$P1r3HrZQPQubLkz5Lx+G{tDIYZNgjs8~*w~GMCML_kUq7zcc&abJpvH0{z`9 zuh2gLXNB7T3upc6x#u#~slZklwBrBARx3w-rWaf%$3p*Gz0hDZC?deDh8YFdkpFl3 z;TBZ;nSQW3gi3qRVVB5V^kk3CXbU@xR`);uou2rrfP6U^bv7GrMfzawXfPnrc%iQk zDhz!dd&m-V`UC$)T|CkU>H;bu;TliY<8g=q<-tm>g8cm7_4vQn;;#W*$p1%+dosK! zk5Y?H*5OaD!YGQX%H7$q}!Z*!(tZFW?1)*a?{XcuRwTFv)t? z^(O940M8rVfDH+vFYzn9@B|$Zb^-W%a1&6IPz*D7`CfzDk@Mv39-YBh4w=VRbJ@ZI zlPBKswobNqOuM@&yK%Rn+_Ib_R-?p&`D@&MpG+>2)1OwGtwM=Z1@jh0Vcda0V3+X> z^k|VX%yh@tHCiZ>Dkunuh)psHyGTLBc|I6-K6BY+%v*2WuGT$bl(EEeXn_%hk`aF@ zmh^@b23_*vgN0CH89$U6F?yW>ZdS!MX8QI$b+ zKx*CI!Ie=&3NVku-pB$Qr=jD(k^p-bJ_|EH&U?X%k`o(!b!UZfZ6%Vcs`jgPpQYN_ zT0SEL$Q@djP9iXC0#Q$Vg=$O{4X88s$=GlZIjI<%zT6gL%^~&t$J}kxgST27-ksU`|4> zw2;e6Z5=U4L^fln;-5c1GJlR)6Q7l9nUxPFmkXB;uhlS_N*T2NbETU7^X4s?=M1k+ zqtr}CFlsdGwe(fV1T}x}`OSSZr_QC*AeX?GDUO=Nhy-pBq=s)K>LyEDY8lpokc7cd+Nlh0C}-k)WbiJC9Z!MUKUso zSRNJF*>#w0AWpNmi5*wT7t_;&Vi7+- z8ZaxgGPl}b7r|`VfI=!4O&b+*7zHLrcm~YAvFbDtRC_W1MiKG9rj&n1RLvz6Nu_a< zR?KEG8T)Qtbpt)^s#W`qFCRS$1s|QdKsFoD9V;-(9QgyM`-DQy#<_lrO0SNk6iP0j zcFtz=!Q$|1-E%w3tW3-KCqs;Vz^=8?QeGjFEgRe>~g4C2|;yyU31sTdXq>msx=Y!%t9hIP=QJw4YjAX750Iv z(CGB(=Rz_V`x7%EuKWUAGfWG_oe@sJ4#Hq+_<##1xzf5MX2uJ9xyDb`x2OStT zV~kF5HrF{6yZ{dl(NkM%d(h|fZ%RuF^YXK2=jRmw8oXohoZ%4td#tx+KuzHzz=K?< z$~HqZtKJWRqoz1{0t?F0kWGYa%j1D3*jI6Cc4AFoAP!~*!Up(~C)zkMgMgn!{IYtF z2o7VRl_>bFwc1i@r$76a#|BJd0rc>^z!f5fNFk5-6v~_PU*_}Ka$f$&!|0d2y-!T7 zv~rr-^^ZOF#16QnWn7g)neZyL)IX&5G>-!w({lxjKPrg$9XS=Q0|56~-|q?1CQJka zbz1EgFt+PprayCbX72?vcuf-hzU;KK`!mx}n_U(NBvcEcQQ!j(#FOq|To3yw?%j_; z47qL6AXqM7ACvmvpYu1?Bfu1JML5g}xn#J*!8sC!1NO*3*6Cy_O#t=PyR$Gq;WRw@ z6S@mV!t#v(GNZFOB_mNETS5=-F(?a49`Akn8uZu!`Y3WU6e5{2s#G}d)z~w313mYH za;O|F9|8_%vT`tr7$zEhAGKtqO0H0gHg(OJ+2@bTB@j#Pkk|(z9GKAtzCrp~HuL0{IcEA!;t$s|{6hRUEMwRVN9l-8O{*v@bO zZY!SU)&>^c_DR10)SpAx@`j5Baj4IJ@&G}A*|8nquyZ{1ASQ4E*^jVR!(|rO$OAK) z;~^v<{yErn_#j*d<<&qL_yj^r2!>)hL(gaZ4LJV=@5Cb#@P7r#`^Fy;l~0~4@UB^b z%>lO34CBVuR4K83>TUG5hIK<-LyT6Zt!GFxPp$M=EVc0DOXUF^*TR;EwJ0P8Z&gOhB8*aN@uefV9ujOC*q5ET&GE+<^iY# zJsjJd>~bljV(Ildi!mL`Sj+~B&$f!}5CH_FaH)gHLmcdQJWpK!@__rsdI|FUBt_=L z1_f%8kQ}Q!Tsnmd=^Oi4I>VWWvXCetHc5y!fcg&7cnj)+q^|MfWe|NvMZ_)8|9tEW zM@vuJRFv77JDtJg$%;{@QpRDSz^jG7{N>I&fB*Z!(WAHD{^|$@v%O|ptyq#%!J-VPbl=Am2btJRVtgrO@ZQCHSWXxu&)l8QDV-}eTiKe^Z#C$yq zaO7SDJmM_GH|Bv2y?WACka|#Qz_=ca>W+{Ae4P6LgkNFLL2aTEa-_{u$oROc2 zJu&crG7X~CcTJ#zS_6qCkQ*2N47CX+s$<`#K~2U&dL*wmpPxZSKsAg)h@8@^bvmEl z?j5L^It=-dGP?{>2IKgzQ9e6(<&q3BG*4vZ`7nbjHbhLJPuY98L>=iw?yw?3* z+Nhy7Qz`0Y2F&n-cz=w^WO0$df5t`LzU#*}sDr_RgT;h$dcDqMbLL&$K~E%|m^C&> zqBbW&IiJWZl{j62UmTy5hCOtAAl0pH(Nd~Re~2u5yP~#aE|=-=&n#VtUJ11UYI!I+ zClnc#^0Yc-B*00!L%2Rk2z6NpAs3Yg8_owkXQPmjS`E3AZGb$r2k@WwLQNpaog{#( z5Vt3;<{&sHVtRNVChr1O18t3%uyxN7bVvQ?jf@FZ zR}#dZ0R@A-MZ9O>$TWU@CguV^UM&^v6^nK2>1Uw(3P@+uYMTJ0?R{q-1 zP(9T2+HEjw zm2%#W(t)IJz8`SgIPDxJ!{8d|XhxQ+^7QxF_cv|IkHRfa=O<5|Oevb^?^;@*p`HgZ zsjHwS{usp5#%m0xLA^AdZ$Nrw@SccyFg4DgcsB$CiqOD8iYIY-IyM-`)gvAoqhI42VRpcIXkKKRc~PdxGF(sW9t zWOzS%<&{l8qTGm;$lHb0CZ;Hr=JQY zlOM@4`8<7u{?fi>+58>o^kP1rp}+QjFuesaRM+)*?9_w#b(y1yYvNfr>F=i?mKXaF zmg5H?9?=Avbi<@wlgt4o!-FtlLjd-22>;Z>X`YOxp$6e+U?vdu zA^I0+i2nea5BOKujv%H7VK-36^+LX%*#q@Y!WsmA5B~xD-Df>LGM9oENfr64Y%YeoJI?~<@#3UFjB;-qUQm#RwR3yc6d(~@^ z3njtU_K06&x~;!w=BQ=Qy5)=dR>ks(iuK6ZPtWTe?6~*}=hc^V4^Ek9oSZBtR-WoQ z<&=1}tPMtUjzd#EF>hO&3b9UaR@efS|#-eD9AHD|wvr1Z_RjJh{bb=k6;mo9~3wXtlLF?Q@K zM!42Avs#VCV;X}x8jOanyDkI|0)@l?KihA2qaes8mQL*F#6g!m2s!)lS&wx)26v#s zcZCBAKk!|hpTLlHVm8C90z3nkMRw+iONPtE7Jt3q9$zs|0OBYZ)?)ZI{zKAD>Bb!j z^lFKmQXZdVO&^*%&Gx{fx6|)|)qlxg)B&S-mHB+|OE4?&0Ic_+%lVu0&qvZCGFEGr?OMqem}W{3*hl$_7zvKexH zI+f!1%t*=V2$)yXo=jg91NME?Nau5VTuEOR#&?N*oEYCxYWiRZ+3n#6D zeR#*Dv&eL^aj^mdkf;=XfJKITKplp76p#cjxGV$BnIt@jbEojpC(I*H-Bbb{#=w+K zXz)iU@LfZ=$w!mt@CN+pAjRZOAf-621FLbOzyZxMNmO#LBUtp8-FBDN-K__7#ub~O z7p5YWT?(+l>5+Glg7Wgl;?7?oNolA@#3^)5nPIzs>*j+xp;E15u@Pmv!{+j{P{bsc za@wY6niGpR`c7T7_h|#DI40Nfs_rpuNhBY3`;`W*GqR-< zJ-&1(zq}==mi@tOGiM{SE9i=RC;-*95jHnqQ~A;);4UQl&kL}Q9KiKd!CH>b1I4vI zr$L5>J~45W5HoT85apfy(xn}_MJdzD3~?hGD}Q~{)0`dRL7S17sYd#keNh+ zp<+<9gJSxP7F|}UN55+6=oqSe&g$to z3!dGMGb3gQrxII{*nPa)|#RrQJ z1Lj9lBcVWVQ~QvtS}5FFgaV?&#f1z69+Sf~f9kBsxTOVP;o&#vD!4m*7>(deh|YE8 zQJY0zWDt z!zrb4-GU#X2nCb$q@^NabRsqkGE}EHx_zX)>rnpk(<^hH`segOg41p`hOA%-f_@Tl+B5yH!%0gj_FMo;(`PNh#4ZF= z-YP$qoV|0|#`zk37JWTVMy#iCFA9!5rl3!%8FbPN-0_Z$1;ThIwwDa>6{u%Cn-m(4 zi9*7y#>5yPBqHtEB;bHOPAJSFKLswt!ScG@06@(|_56@`4<9&i8#?>i3opEb88k-J z(g+87QrZRs8H4VXkaVE1C_E=Su-DmJzVSnEp|{vmpyw3r!Qd|j63#fHVQwMC6pE#) ztoYc6+nQ6A-9`aNz9iS5&GzSJhP~myk{x!lG?#kvRrT^|`QKl%er@EG#iuMyrpj3# z75Mt_U;c9V>)Iu_BRI16dHc}N79O9);`fzXGn*e$?7z%(F{xW&18ojLtS$o4N)=!m zT1oX8_^8<31e==SfFeiOg9v^Tt^@}uhqzq53v&AMr~mDZ7;%@>J8LLv3POq-6HyPEIy)KGEBdl z&;PbFTj~;Cc`Dt^xaingSUWVS9W7zpjP8c-hpV?$?TmS7Nk<1>S2U?|{C>vu)E7{H zIEnNc)XPvX${zL(Qs_N;DR{iwl5M zg2S7W(OLAn$WLGJty!wcLMh68{!x9Xf|G2|qYBPruR} z$z2zd4B*4F)5ugh_!{atb;d&oeKg z3XEt!N1kv8(?J(Z)k2g>rdR2;$IgNRpGRT76O6UI6_%r?y~8EPgkr zS3@Byo7*h2)>dU>X`@POp-lO~Y=3`tFkgGUDG*P4TrgCXLKc}pYc%~iKO-}Fa%M*U zgBdOU>ePc$LG{u5bu6$J*-W#oEwEc+UUIs&C7QxBBH551dlc+kJg+oG@R3i2sLG^C zjik)@(x&R+6nr@#EaV_v1NaAc#v>*KV*EbM_qJ)2z-T54w| zJy{W#&sQ6e8-m|0JO-ur&6__1TqSiOT8bAVac?4+3M7?UZ*I@nstCQssn6x}wp9K! z#5$GFE{TUzy_wmwGrg$+Ri4d8Ocs53M%CBpc0I{ra-_vKU(x~yA49-2+M5C!wEk1K zDFRx%p@NqHYydOa!xHe!0`Y)Wh?rt)jG*pe3L~mcb)ah+>dBx79Lr~eLr-3lI>TXR ztvB}6W%dLc!~Rv5&$@DF2<@_*fG?EMPsQ(vjz;6kOAo=E55z13ZXvo-JU?G?0K&{U zjt@zJAd$`o{9*^8C6be!Z6QerlPJ_dMdB47|a#f*L!5KAOkDUU5xjF#R#`!rrh zwy6_F#C`IU`@&Tl&n)Vk96p4Z`Br4ees?0=9FI8sq411|x7Hns z&UP%Ag&c05Todv{e13QB@SSIC+9zL0EnfGsGguL`!06q^nY3p~K1sDZ9o0xv zwf3^tEmL{}9;eIw{=l?!C@@H$BNMc|_M9Fj8sNz(Oq;heaG}D}n#kuAn;@)R=TU4+ zWMD>?8uky|iH&E1LuC%+X<^48G$4JBL@A!cX?0%1rOpH^0UnWR#)V?&;ZPQxUX-cU zhB>0Nm`}f>0ZSssgCrQK_NXLoLrXIl6&D;^51`z`Nh8mlf+49(4@3rm-k5C=*m#PoK?WcoBQPb5Mc6wuKp)o8h8 zvul3a%+T;;`V6mk$Uhu(4xI8UTc9jr$C68@3*Z}Yo8-3IX?7jVL5*nbRE+)-smb{Y zTCLK{O}U&!rdQG*N88NSl9a?XK{q#2Q7o=%-4|x>bZJcYE709!~}1;dTMPAn5Li)*-$Taf8|w! zBC8fks8?a0I-aRMcX-;^g8lT(BC$+aGRhR<1Vc^Fo}1514-S^&E+~h3DiT*1I5M4N z?pWC~tHiuN{iF;gL$FW^@gGg-5!hCU&!H(_~ z%ME5rq%DIC=nGnd?xxTERO->1{E30|oOnXXmr5+vlwEFMgeV z4maFBh>!6r#HEEHkE`ysdT1jUnb$obD?=(jKXc}Gd-n|C+MR^bg+oC*R?x~$gYV_ z9&N7iJcb;MgqEm36G`|0uP+z=07XYK7br}*Tq-Y$#S*DAU^ltlDhAVFGx??tO`fS! z>tSGVeD6G`MQ@^)LBYy$`cf2oX(?tS>PPq7=AJF|N4s~5I-SO9`|}oyI$&tFLva?~ zkFQ}|!0-W2X^5;eCIW)jq^bk)lahGY5$c+v4)6(NsIv_=8SDBF(-V79ILv{LqMi^v zvq)KFih{~uGjlt_u6^6{-_mX#pUD;^bR4FTKBAI}WqGMYs-9Iuzuvwb1|z5MI`%8Z z(i#_p9zfY!1=eYq44OvYfd&&1l`bZouT#eRt2kZUmv3$dMTn*3ll!p#bHH^x4E~=P zy0^n5!Y~Qf?Gm>bQVBK>u3$Z}!XVFt6+qI*zd!l*Am-GiZ_6<9cy|x0_TM$>vpy{CQ3XT*2$n9n{3%z&;eP%q4cHB{Tg3IObiz<~$oz-cc zFXRJuuS?S1e^nRwgyI8R8{fL+DBf$>fFXu4!|{BIAXFUV$sLejB44e(jboPu8<{vK zQa3W;D`H*6!EB2;kJFG;FiPwFs@QP^rS2kX4X+2|nl%GcqmQ0>)ba8f`U^Da#TUz; zlwW%dx+Q)H_qHy*%xyQv;zMrtmkfqSYfQ_;-E^%iSC$+FZZ71a|1@W6Y}zbz@h6|$ z*aY#Whu376?sC}#5HxQiKPoJGLI^(XHL4Wbg7j6Cu_7$SZxp9+@ z&4r=0RhhH4QxAv)s*GQwr)I*~;1|Zv@6negMO*`fW+0wSOsnZ1qlpFH(S?Gz&g>1h z*Q5NEshT$w&oR9o|^budupgW=3z2BKl(cE(GL3hC`WnQ!sRp2VyY zY@2~RlKmMhHh79P64pW3R~hG9#=+74$=$`K&Y|ea{@y~7!x8L#uJ$A9qLTuiI;k;$ zqxy0g-GA&8w<#|Hv0GfE*!0UNgBJBpA6m9}a8^%nbmd||2olQQ+Q-Y~=Et1_^6uHS z^8r2MTzV7w)fsyPUCk}6z`+NOtw2McjIZYrhJwUgu%#3St9}?dbtk!;5b1;!Rn`3| zyw%oM2s1CP1|o_SGFI@EEHySjFiZb$R~j6r8|m9Fez~droL?FRypYpCovu`e7bRl( zbY<$Bwc`}}HZc%#1S~=P{7St114E7FpEd1&SpAe&z!!->ZGHSYzQmY!!*E_cCyRbh z|5m2*^*D_hu}pxT>b)-;(rb*`GkR+80w=gq+s81E#Pbe-t9}??fq6PlF@irkP@DW%afM8K~ zasWmJ${h4MsG(2Srb7(peVAhO4gec`fZHlMvg`$9T;;!CFnh> z{}lT^91lq-qhj9Q7T2+$18L$heyc6PzS= zWx`|=#2g-;l8ZYsXR??~p4`1UlF+8a|EYqyb)9TBn)(Qb&k=@)QXaKX4pr<@?Fehr zsTFkK*jL5X$W?og3+VTJUIyBp!0;D6i({i=p(J9}qqp8zG?qz1TB=pxXuc^P-(<1P zk9lTPFZx9$=~L(1%iTWD%sDyeBD4DMREmV{?zpP8oEW+m#d9BjT+s_ zFwhH@)_Mck#P8A>Of(k!UBg72s0JOUAXZ6?B~FFb?rzF3SH==neX+3Cx1(AR9eKWt z{`e`oQauIF=wne(mnZLnSx5?)z#^iWYMaNYA{7gy^ou_yS!ant9);HG$|Y-O&YnDY z?pd>jC#QN&TX`V=*kk$gHg4{uerI*vn*jjJgU{Gz_q6xgeaLb~{+n?!c-*GQc|Ru+ z-G_+>vdDsYnKwesaRf5EL1IBvfY-)1UU($vbPf;LLQsr=<6|l)Lug1C$QMq_;h15= zbExwZI*f~>3VxL@5bA5clnKq@rl-oklE4gw ztP(2y({Ekz)KgcyRl68!$Z^?!6QXwK|M8%O%~6Usn};kmF*PYC9E?8b`}HpuM=wxkUcWR2_pf2cbHL!okUxs080kfsho&&;cXU2 z2I|&cT{n+c{+_(n)bqvJCGcz9d9DS5b}stE2lB{!)gz%x9LAjZ9E#EIaJ#)8sfr`y zLwVh*pRMuHpRaMgbP0^!y@@)vHmRu8vU*q)vYtUDYOhl(Y6rHR7MZrVAH~mG_RU)Q z_Y6j#+wF3DC2B5wE(?A4zgcFG15BS=z`y4cEQBWT42JMZV@#C{?(4uaj~b@pxOyGe z4p{&+c!(wD|h#h*MkmKU{d*o;^=JMf2{w zvvgWv)@Xj2Y)1a@wr!=;N@&mje04n7XCGqR1G9O&AlcbQ)Nvufdzb_(UUw4+18DG8 zfQ1_}xc`!fIT8K^ONH1e)``%rt)6cos8F!e0>8kfb0T(tJB9H9PXFKYbS;ls%%&-( z1JS4_QaeK3z40^|V#<_xcaCc|snxDAwa{vFhU~5m`tqJ$$6(KJk^bJE%FE@5Dde&N zvz2NRh-!=D7Na+$5y4xjd+EO;J~tXWS)q=Dp6W#V(ITlhnL? zqI5f)SXWx1KjU1ef8!8utAP6?$2-@s_z;}(@eLVUEhOmDaAa_l9E@nZNsLp9G$lhV z#AJFFxQ)9-a)U{g2ZRfmQf-Ir$Sb^gjLMcrMsZN*+;mW|dL8{R()@C@m|80bxm7rO;~F)q{zctLC8Ns73t7u#Z3!AlTqda&-8Vc^T-1NV#9 zonxX*$qs;3W<8Svu4P?Kj@#&%1*qp^o|IC-73t}(9LZGLjr8;xrFY7o%asL{-e%;w zjW(I9_B~gqj(4GZbP zKT|7_kA9El<&?ySWoI1$ZH%W&>XrZS ztCO9$e~m2SPo_5I15t1BZ}hS4yu7e^*{pT;4Vd|#LYGuVA5tz@^yKu>!Fkr(o`HSz z{=)FchM>pmNC$>y*0zd73Y9Kok;i_Qboe6nIZ=P_v~O*(yiD|$IKb^OStWtCw#Kb9 zni3|1&P^o-`wYEv=(pGIdiUL~rgSjv^M*3H7a)m|r_gz z)$I_oAFmc0#F_hlH<O+eQn~N%j?~rUn+oMGrRoE)t3Y*Pa6iO)gjgVt+KI_7Ub| zTm<9d)Kf$;7`p^>HJGe-3dQEhGdxevf64k5eFy?-5J8XI&=u&#+NTVr zOejuSbjVny-$TA1DDU5p>DW@Gl*5(e4c_HN$`8XLr{WVZb;jtcbRtP-%%_?^hrasF zH;e1@`SsAz*VJruwh8y{uFDA{J}bKb+gBKm?h*1``4DZwxq31zm6qT zGhwi#z0=iUHc8Nd*5)&XUFFPBO_;&bRkAFzUU0W~R!v5)b9yC!GHeb3HVoaV5^z^-l1S0hQ2akI6_PnD zdWjjG&S%m0L({hqEn(F*7QfbN4CYwHEtiReGin_Q zxacgC0abD$up&3%3X_1>Ywct){nmLoXI3F|m`1%O;G-BCCDs{rTkIj|APD*4 z4G2)8Q_%5E44Tx5-4-Z5zS)kG9lN;^sO|?=g;tAcOtyl>(nv!l`Yp8AlMEzapz4PZ zz4b-7dV^Bq0DwOoRGT_-Nchb+#c#hwyko~okNn&^y@#Tp2$vrfb9h`Nr5Hj6CF1MV znV7?6Hk)(jK{$}zip}}+au1z)>V30wOP1tj-$!e)|An6VxmN}@omcL6ixXxKl-&}W z9rqB&!D3IJK;k95gV_c=mrC8 zj7tJOr_*B58H$$E5o|p zA<7@y9Vf00em&7ng!Ir84lu(XqaQ!~``;n+uu>q^a#r=ruaI2A-gQq+>HA8XZqq)f=W?a*F8Ovl>doa$)uoGz;#=b@>DEy=CBi zIwtfZXvcA|SYTVw7Epdw*X*z)2=gpl4PkIl3=@G(*xxWFBTBwtx$-vUp8?JKPs-1i z*3N*Cu27hQ#1guNzN?JhJod6$B$4K{45ox9m|dn!ht2Qs=)2Gmnw8t1Tb<3Wemyr| zV+qH7vf2K0?d=SNJ?X2_JALs|r*MT_+~jaHtfBMvzw|hkK`!(lKgoCr`ku_7-#dtx zI0tI>0erIlEeS+MJ#omHC=|s{PR^j$jhsdl4>S+(9Jo+qeb^i(Ji&dyWH$#so?y?q zYCJ(6Akg*>Z^)n3Y0=B^r4kV4WWJ4UT@{h~%ZB zKU!1(%?qF+MUlRCYI7*A(+i|l>Woi5Nz#8tD*Bfneu&39AgZdh7B=`zih6`D#$YiXwbK6>9;bOziJz2}BJ z_EW%vZ17ug#Bl5|o+yXA zE+zeCDotO8mQm|NURy|iu;vwu0PltVU_-9l-@hA$5>cfgtr?Bl>0i^omcjra22ZtF zf2jt2%$HO{5y{ya-IVrRY=GBkH{?6%Hj=))@JTF-w&QpvN*3tg1&=cWe9;S)FRMTn z@g9Z?vg(orYYYZ5c1m#P5O&V$&<827U&BE}6YiS@OW?YRU@vNL;;dDkX;vA7-O zn_Ncc#_Lt(=+I0HR2b6#3CKJm!*E$?NpRJQVNHLnMUbEZYN!?mbCdQ*gQ-@JbPO*heVSxg?^ap)W=ScnXfxT+si zYp3+vq8_)z2uxr0d0VO8u>S#mHGchHNJlbe;7gE$l4vHFpKhZ9x4(=ip+9ip4x6p0(e&&$XqXi@-Gd$+KksSo49T=SxMHr; zcI=#DHkQ%Z(NUMZ)3FcjU^2396<4m+d))5+Xos53WHEEu))X41Z%@&?I847lC=`Sq zxK+wziTNX1baxe5E3snjrqTaL*n0rRRbA=hzHcY@4h!n}~EzqjAHrcSWojzlQW}M+t`1HuTLLczFfFB9f(zn>9iii))JuXxo!K2Dfioqbo7!pdh^l+iVIO&ihl)HW&WD9*8@6vbX|q)75BJ|8h^}m-GQOL!Xnk+;3aI6 z{;ACVC3)I2k>V*&AZDe1ZyD0a`?3@z;ufA)RrK%Sm-wVXlAr>4qb5e4+gDDTak*? zTGNIa)6d?9LykLnq&!|2D9p^@0c}QwqTz5v8wpLa3m+ywW^aMWQ6eUMHV@f4I8dBa zCanEKBm5PU+xttjm=u|1)W=?g*iVi9IXOd^#(r~z4HKokbLNqvCX*s%C(j*q*67ZW zA|PijD&&z5xEI_W!gD9zzw*F=^10_;<7GE9Ju>s6?5J6W$ecmSpc)+!O~iuj@5!ZN zRW3u$xUE9eZ5Ja@$wYvDy#AFn@{8MUBUcnQ6;`gq1F5msjgFTO9tdqer!?XUxe{b~ zTT#Lx0|$*fQHi=r>WA5>LV*O>pO6I-Sw3j%?^j%RfDakT!DE~qvr*k%ySM0G>qL%8ud)e*6;O*>+Kci@AsZv>zAB$rh777#<=Vr;kko-s#pE~jj8CA z_KWjy2I)0EBB+DERfaw}6+I@;!u3IULU~YQOaUc21*jW+oY*+dr9l_(x!cS;Ex`Dw zWb<226Y9`Ch>Phegu5co^?2*93%5X25Kt9`{^oK}EamF5&q{>5&yy<7)+iC6A%P{V zDD;N@E1j0#sdNp8U*z)#+rO5|Oc|R(UR{<7h4p5oJCFsM!!TNpe4E1`j;Ds>X;Vcc zQC%m}>b-$-ZHj&0pz)OSN|kX*jfw8rbC**nG?*RD>|H}_B`8o}IH*Wy^_1+V8(aoW ziZQUSRP|=Th{*FNAhGJ1NUZ&*dLc1kG$?GT1pY4p(X80BaBNZ2f9mGlXR96V{MyOo zqo`=hksl299ab*SHpbXGr}E$%4Zx553gRBxi z$$SSy0KUTIVmNW)utDAz!W#N1K&Yembf>O~qSGx*f5*CZ2}fM+k{&Z&KA}Q+CzF#_ zK{eaVHkZT;zOd76vMN|V`H`qD{KylVv@VTDO;hYwC$g1XeG zbE1V==C8f%N1kwqvXE4wF!% z@=QlohJBB|{4!fYlIHnm?Lu&$1QMgW?mt6C)@pTniK3{I81z~a-NFVp?DTCc6PRu_E(k| zd9X*{eh`h{2vI9!xd_m^$B0g&RaIqTwFX1>7!Ug-rm*0iGiVfj>QHE;Z{-;)mrUjk zKY46(cI}3IB0ag2Y-eu_rv9*uJfhTz8DVcGs6A?P%rJshZ{Lt2>FDd5&ue%=W=&xy zqg_`fI!HYPPNo0O%K%@D-qLf2K1GkP$&@Jxj zusNSQ8jT7C7sgR!n(BS}33_iX0)b~alIsZ-ig!Gr`XF@lL<;u#t*uByG9Is?iV)J! z7aSZp6#qqQUA)%&so=}i4TWl=R?K=THYYhOgpb*qX;j-=ZPu(?r6P@DBj$0{@=Lzt z4AcPQ3Bs|37L9@y6N&`Aa&ub~bRqqrHm<3;^R9v)CFePVTHKPSqD#lz{_~fwT9p3m zD_i?UhSKL=G?9;G7R(>B)G|Lkl|8ikH2W$0$bF|S)vrxu+0&<|yr8H&jB>>ve@7*k z^8bTSCQ%2QkwY5tz^5-&EiE+1A#Ctx*PgRQ_o;fLyVg%~c%TC{54_Jub>Uo(0)8L$ zpr|Buw0UTzvYOk zhXya^T~_A%H-B|Y3HA{(L+$2O&>=EdPRda4vI|aRb+EHuDg$OAJv4dlW6r_hiT41= zyO${BvO)N*u1CbYj@+h3YBLt24fd^Iu~3PkZ_lFMzxV!AXKbX0B@@};XK(B!2G$~% zOVwGm%Eo?OA*L$B9$DT?-o5cg#*K;gK4Z0HJoWINvux*{wxa!WW`fUXg<5s64&euNCwmTK-*`a> zn0vwlq?gX^AKgQGr?24@C9fwT=iQ6!zKnS^TuIukS@u_;P)Ur&;>Ck?kDL7XH!tJ% zQG1Klnzkur?6(zSEj&K8T4`F|AU8b2-I&9Ji#-0oXggVEmidjUs4Mi^d#3fn^^40_ zlpm-vp3$Y6sZi+hsI9kuST)`%PVtw<+{wKFmxo=XJocyH+lzrzN9Rm^p#8e|IwNc) zl}8@a$5EbRaOa|7Cr@|h>bnCI^q|equ^fCX53_>b$^l*Dxrap>>kuPHY870X^hcA? zMXT8hkRmmOe8WOv!nPB^8~YY!CBIWA zp;9=pI372v@BZ>rPfE9zV_O$+c=kEuqB>@BZvF*&`IxJfa5@=WxIN|ZDz)xVzDMA- zUk|%_^9?qkM2ysB%X=E8uJ-^BDdr4&wlSaS%@040JNjj^Kk-HvB$5^tI%}hOpI{Wv zRVF7Z>=#F*TAAG3^(`Od;-xT9NS-ahO)NR#3eq?MvO#CcW3ouMx`TGC?Xcr1D%Qm` z=x=dyLy!A|Jwnt1lqzN!OvF6BwHVa>?a!|^+3z;*Z2qJD8JWtK(JGZ9Df@^Vg9=dC z37Ij&iY{fpOVZ3|+4ow7-r|An?k#(B)og<-k0$J6;mSLoXD`Pz=zV1UB^7xlw_2M? zBz4~9NXa-`!^-hyaT=m?+3e}0_ZyHITKh_Svs0OI0mtT9rkyVHXi5x)MP8#6Mc!K7zUW0-T9O9P$@cFpdR zOJriLx?+p2>W%F?>^=891a>rvm~-D=#W;)Gs$^OyU7Uzp0BI%@?c)qOY^ej`^w=o5 zH|+4a`Uh3#UBlM_lq-vpi&}!vs?xgBhBDfD@Oa*ff?j0*Xf*(>X;}`xl`^++J&40O z7YTpANK7xOqq1QQJ6;nl{iP}!I-#?f^t=j@J;iP5DZeLF4ScFKtkvj@iTUA6>ocRDk9~QR zSt6772m$sRLUvv1EB_lhNOe#4t;igQvA+-VQ_t@IeTzw7?;u(E3w#IniN z=UZNRWJ&M%_P~avBco{vT7wZrO%7F3mD;+E%XY_Np+GdgeuzE0QV$Ge#vVN{5NKL$ zTdvS6g#9XaduIdyW5%dZE1ls#2!_{9tz7dVZtPEfh8sDqVShnfYKzH3D=@6uvZ0AS z{-HdE=GA?u@)0mHU&0-7`E*)o``L0bp7L*B+*`>8O37=LiulbJn}s61Y{I-jtI8!j zO-DdyGb9QE8&iu12HEfSrAME@9Ve7xuMcwAE6s=WgMqOYtIT=1e0G}i-?-)NaA45F zkFV1*a$P=Q$+(XFLR#yh4N{xN9c11na9^HORoqVT+KTx&XiCDXN0F>jvtsxD<|UEk z@wD~XZy^0}{Q3IVzmD|duYNBehlYx6G(Bv!Sw_YlRZA7xgxk(OGZ(x5cl?!{Kitqk z0Xp7EMbKMm7f|5&aKz;F8=L);b3yF? z6MuDJ-JZv~xqwzLp0@#6BxrCj%+VK#7e>fau|xZ$3kk(df6!$T^Cur*0$o|!+6Ll_ zWu^L#zKT{IKv26Ho_kMz*#u|QR-x;FX?cFDS^&=Ec%hb!lwmN*&)ff!q9}Q7Z3hY? zd-TpyZNp|ww6gCyO~Ji$zg*1p%0%L)+0V%JX`_aH_kW&ku*u$IysIY1il<%~zV)h5 zF4~XGu}-hg>vbBgezE*1r3NKe>VN#kYHEoU*7NhHUs0(>O|p{TSb1sX zsA6(@$Nzqwr5u;`SuV%x;d(36xoJ)0H>aq^8{{r3OABrzAvUuYQ% z1=`1-d#=#a^v$!MaN(_9@9AsA#C#$DL?Oi$wp z^C8%&WJ5h*CXt>cD^%o1>Zu#pn_A@kcD_k;&+2dN-_Te}whWh=TinBoCVFQ#d{Rc3 z0qIrJBa$nn>`l_kq=#^4ADFv%%#tIv2v`FGktcoRNV@%J5UKt|$+zIF_TGN1Q zgtIgkPUh1@-0Hv@bloE^BI2Zqcf`4IFZ>GP^znxeB|nt?OHku!$e0c2N_2OHyr$9D zg8iaqIyn0g@)_EaH?X$GpnevEo#_3)Fxp8bCR?8Q6)Vp)W2;IQqt@`)w>74+Mj|Hr z+2^kGILO=K@IvLn33kh))OObo}N~4TP&|@~p@{5Ym zY9+pK(CxJcO8!1w!lW*JZBfz(+gJim;92-s0&uv^Scf9kVLe!kbh0DICZ$m~Q45CX;rp#b`0N z|I2BxI1^Q;`Sv>CcU`=m`s~p2uhwtdn20V8E{~c`{mZuHoFnRVLnHZ(RVPAs63Pka zX(`O@0};2~nbXPDDtC~1{lB>z_;`O~73?p!?G5If20LiYWMkSLjCm#_f!D!oGB^xY zOSzu7V1MQC;Y$0xWN0EX7BCpUg%&IF!EYb;VH_N&Y>Yr;a*tUr*H`Q1Y`;Bh@>rV~ z9lyG?sw*JV=eKUPmGtuEo;X;tz>U*A#XWt~)*fUGa>z~A45tUrvsq5MGF31$>^L9KJU4=N5=XN8zMPP6^f%v3IbiqAtVY+@yS3;8(3%IvLD9`vcsxgv#;f1dlbS7u667V_;dh@ltfBowZe@OhZ+ry5E&xa7S z)YYcsKANf1<&t`<&ZAqC%(Oo&?<%beA#ved)K?!iCu7^TAq?A9CQkviy!8w|JtZiw z&fzSBmI!%&5!h{r%LG&ZY$ZIjd0d8;n$TtI)NerDa1Ji@c%c+IH-?_p zla_`@jytNU_WVtj&H32+3D+0yzrR9lORN>Yu9vy{iIGIEvd{Za-LAk%tctWlFR*`~ z`5irG@((d5LG&eS{$pa?Gad-dgGEYy`~v#(W;t8$L+^nSaN!|vht^__+If&`=xW3Z9Sh|~ z%QouVaF{B6k}H1g>UYjlqJ)}`=ZM|VbE0gf0Wx+31Dd(*z`5W=`~TX*?j|wYiJQG4 z7StLO?rg}De)?r{@E{DCR%bDa^8O(Ex2)9{@h$`x=E8V*Y0RRQL}QWJUGppHRBFw| z?LFJ0ThDkbon~cn!}-2il7d3Z=cVi zw-qD3%!`G>kl(4$cw-*BIe55G_!q1of}$ab=POaY%@HeFJnf%kVk?4XwKyD2Fe{UV zSjo3-3;UC0+k>aBc_M<=gOLGOWEYt3N|{wx+;gor;&Z#(j|0Ge@XKhiClg4d>cBwo ze7&oh3f0V~0N$b4LdNBNBpKO`^r&4d?+1GC3`28%gkHDJ!j!Vf>()gwa70I*&4 z>R=e90UBfU5@*0mBntqwC9-k$gRE9BDkH(=v7bx!$>q9=cmCK|KwHaXU3Ih19Z9uU zqbg0?y!;p}7j_$Cre(@tpY5oD*~q>FVzHBY zHj(e&ikq;cG4wPa^_LUq%vz_0f zjut&1rSgdg1}PwhPs?*oIQkuh!i?ao5trGn)hiXLb+NKoDOC~Wn58v%jyIo;w{e zuu+a?vj|BB!spN%NLx{KD=&cuwVtAmo8Nb zW$mt7VJK26Y@U)EvRwj`4p6qH3?gezF^c&bhZqeNp;N$&~#2|5jF_B zKy?^>01uGvUYE=R>*9s#q2P>=ZWCNZ#NL80CIS)vqSo-5NH$V2te##{@2j=YFEd^C zju@m`U9=JKc#4qWd)bywi(Jc3l}1t-oUV^FCTIU#W6JE;G3*idHWRkjwAujqVr{I| z%Q`gHtkraiQm$0G0wFruOj$OXEY5h}{7eMXZfeBiRG4+a(f-1=P0dn$n7!+f!!?s%C@zAbdz0eF?_t-N0 zVAxhV6&gyEI*wGD>4J3F;bDC(^JrdY5 zYk0tS)Ibij9;*L1lPjd!2eWA77S!8UAt>9k)qL9Sbv{=kQFXgIb&`*Beg~Zbq30O$ z-E0+15v@+4><0({f1xzlj}Hz;fZt&@)w!V^Ub=vvXhBW!1DgJ0uKs)bPak}ne7pS} zrCg%O8XeCXNUuO5l4zG$wc1o&J(khxY#y;%$UYqS3Ug?7yHTywR_*akHD=uf7qD;d z-_L0Nh}+>(X|NLZg!KKJe=s#r&w55+{Q49<#9o;`S}JWFZ59_OK65FLu?Vu4TyhDn zulDgHg2x1EF2B$VYp{mHakq25+R+w{o({U zG4a}PuJ20)praPM@JTlVDi~nDF+Ug-nL;HDbJo7omK_)@u|MfYz3#7>bJ@4a*?LnW zkdxE`!QRq9BH*-!*n=Ls#}(=GxfVs@rylb8`;F#hH|PvxQ0_-PXu06BIK9k~U*pEU z`xb8G(uE>{P-=G7NQtIJlQrG~dQ6mt2U;a;!z zE|i!ml3#yZ-6PbfHd*#tJxljk(Z9VYu=iyLAHyAX!-pL4^T>hsW9|))L7fif)`j4# z@^*u(-Q=PQOB(WAzp zoK#J9en!<2zGd_)1{^x0l-<|Uqi{~AmK$Wy!Y>Fag0oGwyw|aX{f#y;R?0P2xvq zfDzNPq?`<2VYf#I6NQ?2_gVYrw-S9GFMLzV!FUGouK=f3jdN>8hSCjwLK6Coi|)Zv zg%ATRfwH0>X5d)ePOfvK+z$O4vBGD_i|yBk8Q1Vbm_Ri_@0p!D@7}QCmTlYEC#v6~ zZC=X;rN;QC&X~2Jc;z;-jlGFYS16~VA|M+Jr+k~1WAgW%d@wpuZjP#!=UekPZ&mg%2o;oXX*xY8u9KdYV#Sko%*$h_hK8 zWkWsZu5j=dhU?nGxKeg&)9=le#kG7qm=>dJ75gVswzA!San2-@3cf)yMoYovAxidq zc(^SP8-?weD)H>zP5O53ymQNz_Wl!>{|dc)1Njv(v%hWs$!Gw*Xc(F4ls)DPPc(*m z^W!Tc`ADmh@Od+MjP+&ffltxZ5QI?*d+)t>pInoT%aqGe1VPutir$IOBi4{(7sJRk z_i^kznl+&a7@~5De2M=zGz_~;(+6g%p?MwMGbf9Hpc_xZcJsgnG!%2J{)a!*+j}eT zRSq1$!wna)7d6RWnh8h1HMr0=-MpHe`L*`W;gMds)W$xfGpDSkyIaSd>Z;1YJhE(A zacZjg#8TXfGc$N#7ZDZv7n%aN&!E&QUGAL6TDRMqntbK5mVaF)8XylZ8PO~lZ$B!y zWcDob%2hk%d#>i}vIBDPEaX6qIR{Q){j@rqpQ=UU-4j<$@k!nRAt$euo_lUcALoN| z%7RlF&>_G)DTaefrAn9!rb;CCqaRg}b9J}(>dbko301&%^pdyB=N_=0U+-oAQz+Ir z7mx2TWNn6wdr~iEz$6g2Ugt0RxA)%rGf=FLmj)gj8F_G^v~bCY7S#|wG@bk>9Vd2Q zqmnBjkhSK*`M^NfFKYs$kgB}PTmiKy!CpP*y2)D16Qe$fAN&0Zcm)RRG-VY`p{xID zVEeRA?L-Y^2fL+CnvSZ6GGenkT?;qa?RBkYbWEe}(aU-VtpQ8k;s}{_ zXbvJ^l3umSr_i_OwV34(qlMhpzQR1no_9lC;miSYRj!7i^*)P{-NRle5~-AVLjDHKTcM~(Tyk}MkTD3e1cih2V%6h>~a=9ote0MpVePtzq|hu$#7#}cpdUcqGBqbUkW|a;day939T~P z&P6)s!&cGYoRbx9xTk)yIE`iQbSt34 zs5ut=oWZyJIplo-9rYaj_@8m~)r+r z?F-V2H-@T_Gn;W(h4zAL$125PaB{G(oI*9U+n#>q0*oaBV(K-Zdk8b8Emyc2yx`XSi8eV4P5 z-^G6BPjBHG*_Uk2$>gZTB!Jr~7YqOLuGZKH#U%M)knB&THpQ>VvlmL?3*>_6Q)pI2 zLhRbjyS-;@Ccm6u{}m|o>XjeI(0PUk;lUf)JNGMnrE+=9g7 z68lF=NV^(DZ>dzQ@HTzt=uO3Jyly?UlxJ?QW?I0(EAVe07W4t58i164X5JO^ZkYFZ zNcxG84@=N7oluC7LQa_Jmtx-cMG(^seYiM=>Ky!#OPkO+zxWj&{~mm*z7q$D#F*&E zF`em7n*SI74gUw#GW4h2=!nKgVJ-!A(5AfhRzo2Y@C~DOAsN*fw8oM%sMfh#E>Fsx zFAOw`+g58l2{3wRFCrm*sw|gV1&mZ7{ah=WiyF1W%j(rq_9y0TgBtYK>xp=b%SOxg zfKO#S95U*1&01+>`BqS<1w^h$L^5A_cz-a`XEbe|^0{SdreEfHH01OKvmw7HGJ9>U zT=1G4F7nOM{@`fP_w~F%txJGtSVP0*TvaSXGc0s+ z((7yko7mUT-{+M_ofy~KBh#&NZ?{QW$$Z|I%OzX#_4};DiE@6`Xf9rLxwvyheZS*^ zD(Z0t5%KfLB(0_ZY$>EGPB|+0;4CAb_@IidbLTgJ>rafWrFxEf7J$THPr2=acg^iF zS09L-@6uJNA?-Hzp}!@#u2oJ(YdfmhRCy?T`*n*^MNR zw&0^3lIxb~pRM}pig%|AKt(ve^Cqs>5oNjd!Q-6fqCu`Q{NJ;TLX?3+ZD53I7aCwX zpg`9Kmvh}h6DXwu)}3EkZoN*N68arTNZc80F(~~J$Qmye1~8+AIIW^=Lqwo;WRidnN%NaUfo)9(i4+9MV_ z1dLhXN~$M)srEO>Cie5>1?IegA^D=d(gHr`c3m=4&u8PY@%+egAWt&Hg4Ay-A?k1- z;-I};N?c73#h~U!z{-^tlFBs7H zPiL&@;c35Zc>B!uW{{$!vK*ShrMGW(?@kwI&kVRKp1|sgKWQu^hrFksLw-q{GS0m( zFOwh7FD_M#7U#2(GTE+B52fO&sV(hq&95b!b}zYe@Ab-S&f_mNXO!8k$<%nAxuLJv zTWA(1==D-x?R8+L9>gmY8(Gs~DDLCx#VEgvDoN^E(8h@!CU1!&df^9vIJxZyC$va$ zHj1=SpB!2dxp=GrbRz$Pu4&_<91fA=tr>o0&hnJX9C9kCl~TR=`FmyJ3wxY;Rm8W0 zIem{%6b;w~TK)8{>G1{IBBzblZ9$SSSnR&~ruK}+l-8qYUWy(w^FzMzjgcMwc56l? z2vn@rkosNXH0M#_^_p5Hk(G5)HFF>PeUjwwpMIofNMa+`SCP*~G%SHD3DxE*7U2Tp znHmGvMV?qTRZJHaE!kWOH`2v$e~A$-nN$p-_f(|)FeM00#NErv>B5x75b6=c;(AkU zxE$U*?T?W01Tt>(HE1%qe1r7Y9JCxMPh*(6JiI**XW%&kNw!8la^|wdN>}* zV$@2g=(V{#B_N259A}qk)9G}%iCa0{p320Q2a}PR+-XZ(S(`SQ{pW8{g)STtdsNkM zBIhfmBQ<7!He1Fzioqv(0yCq;h_+HVsnj#xi3|XIg*5RMsrKSPFA99Y6A(aj@fCy{ zP5@^`u$=TrGyhKh=a2LM5}eY#p)yQ=6Yt04qxg^T{!C4(>>;H{!p$BR6x$e-&B|io z#TQ5nDDGj4wT0$NK9Yazzf^i(%14g89Qf zTh|>%^S$|Y)Ydi7>EV~qXDtJhs3?^FjFhAnUcfNA~y)Nrt3cy z@BphZxbq6V&*#p!UxUB(`Rn0t3DDKr?rtT{)G#*{am!?J%e+xz@5sb-mbAmJk*3l~ ztJ)fh2gAY8LM$(O{RJ^&mC7ldX~NIip|v8kQl0j?nMPy>1rS6G%c+FU!cwyV>E!hS z)};%d%R>bN26l=*%lp^wYh8&^4DZCSIXpbtyV6dDdpO)CsKjDe`z|({3x5L<8Bv^$)m1fL*s>!!(wIh>?pg904Ex;}PHtbLy>$e(g zWQt;gBh^DQ%N=JAV!|Cpc=6QQ@L?xb4r&r<<}MK)WwQ`F8pA>eVS)U{7VrB}%S3&d z|5?I#(hKA~KZU_qC^t4l&YW0myWF9w%MCMj(UWBL4q#yt)iqy`iN*G0StMoZC})~t z-=gfio_Hcy_WsxL=CDex)}(Va_IC74CqyijrHnEu`@T(6l+tAIa34uujN7C_5qfV^ z-^gfT@upMHe&~GRp)-!&RV=366Vt6q%wk|avfI3y4M*I*dz5k%cO$7ubrq zEtzX)0NU|>z`vC%aN#TLK4-(YE$z~hs8$vj-+sHkV+Xo4rLq#I)uRngJDiqC%q_ho%X*`@A5oEK zI2v|mF%fMMJ1J8(6XD>3Xq@~xlVLxQNMkOaPYnZx0p?`U)Xg{gj$QuyyPwJDk5qJq zo<5Q1<5nx05DT5|yU^T;)4-I}<_nC+kLk!lg;1kb*n-}^LG}-|OuT8`w@u&}pn8SJ z!Jh)}v6su|b=k3d_*#CN^^a4D#%6J8f9eUKw+5$e=sr;Srz=fsu{f)QwS`jQR1H!N za9!#4i0L+mieU@{qHz=lF>?_#B>>O^K|>?@nU<5#p9>CX4Rx1JDf0M(w>Gb;ee?O} z-v_>BX=Z9h40I$Ke?cuXp@G5d@uf?T|EL|RCoFPVMS}W!Mof-9`|SJgw;vDNOB$uh zzD|+lxnW>j6iCFrWrx4<5XpTwl+XwhxsXrBChlpLR;RoEjwO)NNLMv^cya zyS>hC6I}JUYLP#xiv(mQk-*u`F}LN0@4xo4bClEN-m%s7WaT#8$(f~_^H@)y&xkFb z04^zkZy(@#r_ziM&tIp$0xy?f(9x5?Egr%ldI*5XQ(`7vFRDxM74>hs2ZR4T>H+Xx ze;1vlt0tJ^c)@u-5vdmGMsQofd^2b9gq=~(ID5OvQYsXUhPKjN46BIMTTSn9?n>3$ zo1{{KP?pw+WQsQ#ri@8V2iot0#;-2BFo|HYdG5MHw*6blqetjXXRL>T^@M^?KmEP$ zJ^E<#x#{WW-mogQ*0@apElenzP9{mXN2!!?D;cbFGk=nj{P zE;8g3TXDj9or}frxoiGS7)UYQGYCH2MK~eYEKWml0R>de2?j=2EzvlIYo0?rIP4w& zJo(i{jiW)0CTEZ-t@+yV#__bnZ2&*R`0=25kOXqEnt$)at5=)zEhuvUB)sw#4gmU#{aEHafdT55@`@pMG11s@p&AU|9jOzG-2f6=~&ocrK|E5<5) zBRadUSU9+n_&g40xix>Pl$8l^cGxkUF80TFG~ynj(D%4~!}beoY&G`5!?JJ78T+P0>P+4hHcs%}SK`QdP7<022Ug&mNq#Ady zQDy(qTx=ipgm9K9AL$Cj$vR{=g5dA-+=B-Ep9wm{7wUyOCMUZSAr$PyfE+4EX-eE^ zBt0$ZtCcCU7Q$E&WW3NM zPtJZXYOOi7#_3*W+jGy&KK0bI>lIJBz_?C>u5Z6aGZ%P>^whZi4GT|7L9BXHyFcIF4jNZ+%(w3>W< ztj{JCy9{Up$V9w@4uN360nVe0tMzyWI2PY?n(t3PgQ#RHSNqqMLEfLicbuqmdX{Pp z?hX$JR7Sc4oEoRmG+iWK+hAGfH}r*wK`5xn;g9s4xWJElOHQ}bC$MrpjXek5s+2B` z)$Eq9TJt%FN$(JU_m8jP_D|N@M8gfEL7y`yw5l&Q$rTSh)c!I}3syWDBjbDsH})hG6zhd0B#F#5!l&~C=?e3D=F{+OE}M51_`W#UMcm;; zb1R5LPQ6Ab1YW67jF^)zO0uWQ2(Sb-oF}a+o~Nvdxk_g)4;X1^&J^U0CkS8Eno=_% zh`5t-T--J{?b4stTn~%_`@H_a$sX|vD%!|t{iTyGA}HZuUR)y zFZE6>sFZS}Q|pZitw9&J89g$&NR(B|oh7}EFmPquLV@VdhYz=JgC}$SS%tv_){7Ja;t7SVEFFLEe$ECLF)9LW4Xxdct#|rL^+2roQp_t2~)cF}@oSqTN zbAKH5v2s}B6fl0;pJWDHcfNuXeMfkci|UmVJ@I%GO^*dSyzyl%yvz#_DRK6drct?r zK?^^ysr>2asu+4;x*iQ#@q4k%TJTG>e`>#Ur{0$JOLb={l@eVEgMFyC@^X6<@F0ma ztB~rGB>lk$-ymn0jZnu^OpoUXxjn!?^KWMMu+3Gz*9LwBTc9PBL~(|QXEC;c)5oFQiKv>&Yb`%op_29 zdVVD-4#FLYd2=Yd{Hozqem!SBscEDN3PR1<>yGB3a3IIqOTQLOWl% zfRxbL2^i;uTqN90E;@Vu;68sVnDB-Sb|p9p60z2huBGDcA}lL0OqwUlN88t!75#es znhjnz(BdIg!2iT;3QvEZZqfX{Y}6C*&mL){iYAB2PxLRp{PMc(FTZ>|Xmfi){UIka z{a@O=ZpoNhsa7@7cRpND*@L|I`M_~E;pVlXPTmVWCVgMoR)ymNHf8SU}I>%27(cp=V6f$HKpdBZ({Dz#6 zRs5v;vVSi?zy`nqP=(*s|5T(oGMaIx0<9B*kLVYzOeSoaOtgLZ5%!@g-+VJ<(dEO& zUFFoah3=VTc7D50PK|9E=9q zTnv{JXi3#3t0tZaY+Twt9W%qxb_@h2JrTT0o_iAgSt3PAGZJjShVid%wP95ww&s-J z=0uVFn!TB9rFT91EEyzMl}ti`PD&K)-+Cl^yg4XP!V;SmtMq8l5KK-CkzeOS(@K%p zXHqX^AA0Gf8{kTd`qAQO-=T{Xmk+ggPh)j!I=VOz5*W(aUiM!dK27N3&tu&ZSV8SP z9~htg@UM4scs;)^bfo}-U}YG-KpS{Tr{GEGpHoP7;#o2MJt~=mCqITe`U1jcGIR3` zp>Ux=c*ROdZamAXliv_r>#!l4q|`b*R`&9U%VSap?=PlT2G)sSfBs~gcyv!`NYa9=6+T1`P8p3*I&7dr7V&7&CnlaxnY5vB~el|3f z*fjgc=5Z8a$>k-ZnS3wcwgkK?4fuoRvddx8#;-rVXjypC&ceoxh1uf~gEp8%moaaH zet(u+ua~&&mObqYh}S!{dTC-{>Ew3hn(!ho$yYK?C+46yjubzu6s_TzEFojHF)qCl zeO=|~Wqm7nON9<^hvLcS&%2mIO>UfbS0`Si>-LEhKZ5Bz-`x#T2?vb|EvGl>7j)@( z#oOTrLMY+ap)%GDkOyv{kpthbNRc2bGv?cT^YwoMq3R%vduQ${NvABmpcPWudT8iXt7N^;O zePuOF&=1bQ_3kOTG};$XI%~=qRAKaIHkp3=yI^$#qcn4a-XEF50)ldQ|M zMQ(^XT-Ib|tWi$RoMXCSL)IY`^@zl3|48gsv#nxx+Ym}|HuAu;&$4%dXZXN98nKwG zlEq(z{vV$CzN30_XV}*48t z=xnk>scMCj3Pw2P0nL!+X52Vu4*`>;lVj$C#njd4bLKi=goT--*_dgCCfD0MfKH~I zo}t(y_0Un>M16PIXPT?&96Ib7EHiIVK}5$Z>FJ`b3H^+Qz9q^veDA%F=ZQ)_ktE|K z>OMlw3V6(9#qV6~??{aO;d}4R&XU;dU#pC&ep~&ahw$*??7pC@sMd(3dh(BZ?)ll> zcmIrg9L~4$6(THHH5XYd>TNkFZiZBp=8~~gDzN{m;Kxonu3lhA!|mSU0B%M0*>f+E zFX(rA2UoOOt-A_^0?vdHIPJ^uL!z9vrd?;n-dvhsF>_o!Z7jZkRS*7R#w{?@oPz7s9WJeCsU%BqONr@w$ zwC2SS@ZQSq-A8AS*Oy8A^_+dLWq3rY1AC%9CG=h~5uGNN12q2}R*bn(qF5il8k z?rTAl-BG_wZLOg)Jd)WvHl1Q3ODB&w=ZCVMp)gpLeq{Zd*u)ImP7L*KpZ#Nl+vfiWD)!Nthh-^9)H`VGH0S2r|GZsC0Fx4Aw<}VI-V$SFb4tLb=_!fDG^)gyd zuPNeb|CRlYJ6oPvU(8xT&`@jqV`Zo~dBIlh3;C^eE_Pp;xqshaBi34&xrO~twPEeh zfO1i@vS6j&m(C?rr>uRkf85zW^xvtfrBo~=Z7Is@r8T)v!vD^qhF}Ts*JJappLhGb zFH*6)683mP9Q~o!xVQ#@>XrwaL(Ntb2iv8n4-TUnMSimpgG5w zikQ@8jiKUnihO!ikUS9cIcz1RLZ$XaQ&pc9tn!`bm##faAMusi%R{peaoBfSVJw!RW}YIZ7dY_sq8OW)qa0GnF_elvzJ+rEB0nRE_;N$ z5smgTJzC$YM2N8+IxC2_3;AqXV~lxA8gnuo8z>8U^pzFni0{3O&LpUd1Rkb7zp(%_ zdlvfaKG*!ZZ*jlN=ZqFFx~Mob)*Sy5+h=fB+!hg zRMn}`;N%`?iT?3YY5Zr{d|@3h^CJ!1$~D}|Z3fIing4-{QDz1Lky1BgYgqo32)di$ z4a@(5tTsebXmdw$n+0i%Gtv6(JF;m;={C!i_M~mB7S(DbR)a#i)MfDzA{8b!wGa8g za!yk|0p>w`e5c@B*mDW6%_`WP3!y#tqNDHG^A13Zc8v`pV7k9_7rI6d3Xp!#3<7WU zkO$)@c;RS=cp1U|aIzoiHI-y<{z2I!xpczBu;&yZ5&vzD2 zHyE^#Utgq;n>SY-xV??)*~rqx`v_m0a52$4(0_o;{n{df$CGPsmx)wWl?0GW3SgA*)XN zw|2WbjAJLb*boaxqd`iW$C#~dUXq`*0GDgdNu%BZAN+y8#T6h2aVR=I1h=#$Vvkle z3Y1`a+fOk{mLdO{p`!%uMl2*rgWB@^ishkIGmMnoj4HrK|0{R> z_3TnlJk0*oYD7uCaT)r4(j zfB%CIFt=$;X$lkyL6hP=ECACh^pKw(`T5U}{6{sPul`5-DZ%BBYbF8-?aW|1L4KSx z_qGNVpB--XWpLY^!i~mcv5%htKeT{cb|0|9jnKxlR_hSrvMXVeX{9f&llFPwG$Xh3 z@LxKhF9(EC#tuE9bIs_9uuP2l1VCxj<-zaL_n=1|`6}*&QkHPJvrcrac*&y7dm7_W zF~t9m-=oXf)zMJEKqq9|ikDon*Rp(UvTm6U6m-gOXAJ|H4Z&2<>lob@!dX%;n;Kb2 z9$VY?=NdZA-)bP|3WY6&t!asq!$*$}%ooO7ezQ@fm9hCsEE_z3O+v4aO_vvx=a<{Z zPn%Ax+QA$K>%bh&soK9Fh4x!Xo6DE(^MnNkmun#9_4Tutj7Nlxe zbIT2dK%b+H6=`IJU6`vlsqz$3{3nPzKA68(?y5fic=fKzbL|Hhhk>0}V@_}H=;<+M z^%#tJoc&&v+&254eiyn^>z7uU%{zA7zH8TA+qaW>?Dt5jduN_PU{vTtOdmh{>@!=o z+<4=+Y?cFxrQvM*6$lTxirqtge8!b1Drf@Lxq5atbMHEO`>>cLr7RunM0=~<1gs&0 ztEW@ow*lMT4?p7|hc;YGub)Pf)aY~Ruut@V;T;U#gW%6ItP%C|q4nUv^J#uav6hNG z*Z11Z)wR+-ZM?DKjZYENK{@&5*RD$d=zzSP<_sL*fR*H;)Sv9$t<=kxWwnezZ`4)H zDupkc+Ll?n`|`^xSN6-4Myo0o*-XMOdDe`wOREXE`~|H<%6wg9&_t~{nbYbu(~g~F zvk=@G|BP>e)7*oyG-mdITui&W7Tp@HKt!9}J^3VMWfIbG2ZEz6Z~K?!@}6>;Ik19d zvofdd5)bA)1_CzrZ={xXnwz=6!LtdoqHzBCh4vD$M5U2AedGalIx>;z5nTQ=4WXUJZ{ z+A}l1Y#&f-bKqu^x37G!{nT3*;}q=JL7H2({H01>d*qS!PYQiQt3CP)Eh`NCMrqeWM(&-0J`TrPu z4>&2SD{gr2GrjlT%QL1=2Vv1=m^YESf%D3Kd!L8#TKoSNEHy zm(QyNTqMgFXEfAj_rM-)doSr3o%qTYEJ%eP zo8@AtQ)018B3=DVv)ExJT`s%buk-6n&S0$##qOm)&@D_Ni(qFU4+{D5VaKN== zvUTQ+ccTQEj6#PkU=NT^-v=qE@iMPnz<`~AK@SuI;XQ@7LXRT$<`+Qhd@JDWEiHGo zJPdXf$YL`%OG_m1QA0ZMlvB_?a}N!U`(ZY*!C&-B4|-MzmR)Ra0Q0~f*sw@8!YC&$ znl>(gBd~GdN6;sL>s7F~hNs`qK!A|}8x7PD~d>l|9L13u0^8v?dwhDE30|Ga3C3oqabXxDHa1hp<*_p? zj-Wr>6K)H&k-}wqn2so1*Iw)IyJL6!qBg%byIOZ(N6c+e%V1w|z|rM?f>xe|N+=Vq zgnHm!xN4VU*UPZ_54N>@B9!5&CnB>vowlsLf@?7e#A>Mf;Rz#7g~?=HcLk^2<5H^s zxp%*)!)~f{y~|>E2mM-Dq3ZL{IMaytI$LaDZJ(%T>4J(>jO2~!3N0)s%b@QFLqS>t ze>m`bn7P|+M0;oTFx(mz@n$5L4oqSfepZWO9gDxm0A~^I-hr_jKhWJ-ci6XjaPY5T z(YN2~{_>~y-uvhyBt7=G!t+cvPr#OoQ#KnRd5-@0{L)M7F95;AV73svOE0C~x)46% za`{mKhkFj%-FPPFLBp}IZ|V7H3w5Kyg3hEat47KM!?@tsF7$y!X?1B^E268< zroJ9PG58+3p~eXZhJ~Y^AQDurtRZ=Y`m0rCRLb7ZW?{c^Q|~Dl zJkJdvQw_@vJIl1H1NzOjl0Xwbd=r@6oyubY!{7=L3m|LLHNosZo4CWq8xHo)k`t4v zF)=X>W5ehx0AXUR&HT;}ZOb*I!{AYkbrSb?jNF=?IP;f6y(7Ma7}#`xT}Sy>VCfP# zDhiQQL0nG#1xaQf{kRhGf(xj(ckljy!BR>j;mRE#LajC(Yi1?_)53aoi-c@8iyaf7 z0F;3KDuSL1`k+WRP5dbq`;?Qczk}HneZXXHt#pYNBulMmM=Tf$#)QGePz#Cpi>AB3*QYg&%9+}+ZG%>v9jfGluI znp7GNJV(P6Mhp&Z^Mch2l$gyeV?&vNsR|E(9C#5XK7?y&9;Ve29k+$w(}-ZeFN(u! zP9G5i|6@ak0hwtI#mKP7>P>VE4jLZDO!BMHy3U;Z5pGZ+#9 zN1^eD6sk9bk`7l~tAS0vOr5!?b(t&d*!aT_%eE3MuHNG`N0o7t%ZE}X4ikc!-{vBb zT%@}@Lj8DV=Eg&bF1tr1b=myy13IrdYIHe!jXE8TCf$ zPl^SVkKTc)7j&D2xn$iTLZb_wPsW#OR!*Z8(J1u~7Tc&0Tb;%Z3yJLvZ_dwcOkCDA+l#w7k{wF4#J7eIPNk*9!NDbBfqX zNyTYHhc1=@-2qHMGX-tb0Mj%sy^(-~pQ58yv`0%{EO5`PV$6{jD!9d zz#85!up6)z_I(>Y4WRVs?q%R1ES?(pU>BLq z8cQ1NCx^~tj_b|wB?eg}?6HTT_f^E@2h=Jzb=JAb-_Gy*hb@|wi~sC{IR*@7QJ{L( z>ze1C@ADqJtPQ5bY0S=lATG>9=E9~9N5qyN4l^Ed1rj=+ubu0#S=wMA7-%-w$)UHCoo|`V zZ@*1_CFR$ieAoot-fWY->^(=SNCYOxg~5<)dZnrAjwXreP-rR?qAH}-;P5-NX2<12 zE2T@~frp0MM<)a%n`MwkeXuKV!0VGO>Q#rlgY1EY#F7ryJdaPpVpUTfKqky;7|Bjd0cq&h1_seamu(KF8emBqVz__=*dIeD zSXIFJBpVX*sWh@@o_XdvwB*X?&`-}>+_@dCzwL91_xbfUbvTl>y45O2Tzht#&-#g8 zYp^b!JT|?HE$F`A8WRW=*Y(QT+dn#Wsj-Ne6PT`~K8vFZsn52n*2JPIUY`1jq6i*o z(?z@I0!*FcjbfIeei2+MU!|Z?VT7W24Ds&a&)qF{ZT z-idtS=EhEBS;a7-M*Q1ud-Kg6Ndm&PLptZQ7cmo5D&#UHA#W#B2l|IBQ(eSZkh%`? zL_wtrBD+u;!#nJcxz)-5ajn_vRhfB4rN$1tLA0bH9`_K;Imts63TP<65SN<;ZDZ~@ zzyOx~X7vMY5;yGOhI0#uK%3cEdth|H7@*dqFP=GbD(j|x0I5|yzm;!5ZwmglVU<08 z{+2{rYq8bgu{(1v85>eK-C*>wc=D*u=5hyp5%1ECYc3}auAB7CtVEf3y14FYTbpI) z^_QF*tr>*8bCxcei4+o%aMI(@y8TMmxHoN31a(P)NN4m#N~KIHPdqSEidTB*bD3bD z`wMWcGH7CaF8nreWECr0yxb-oXX8?3jCM`<#9z{%fRtDppL9ClM54ic-+Xo{jLGIv zKf5B=2a~|hU_-Ej3sViB;7LS^(VkokEDD`PXH=_TO&CKjDXF0^U#T-gr37iZ_)|hl zFo`y2z!h-}Nfw`18y=ha=LZkEX1e<~pzx~Inz3&ch zCcQkNM(+)kb}lBKfpO(UizDQ0^qCMaH}QLn`xqw39Or4>(gb9}&1Sisv-e3~t2?g3 z*9vp<=s%h^0i2pXvxE2@Pb6Wx0};#jQy1JrePu_Ihf8*9AAvSP`xBG;kcCJ=7fhhK zh(YaQ%h7cVYNs<4b%tGuMN31;l-8~(IYx-xVB#_iW>fHtI_vxEyK9AYGwbCQk=vrN z3QPQFCc|M>S(a;6RK#n$K`Dm=Q$4vr;IR7<;71a6_u$#d*kZ@sW3zZe-``1n9gQ2r zMmKOBz(PY`;`?hJs>!eomIb!Px9q^-MMgehF*8Wh zCgHJZ6#|GKixpOP&i@M9Rw`k+bU_WkOHijV1pe*GbZBz&e^a8V)K8`ZZBx=LeF_zT zwXv`QsTldB6-aU6&-~j*Cu(}BoJ-whFxS%MIbytSarcvctt{X%EudcIip7fIj$FhW zil_}h2$>ihb{1>^WwF|1Ql*PJWATiCbs{tz)Vl(iy^BlHWTw4ma{4Ul|88HldX;PG zlKERuZE#{_q281W!_0Pzm26!xGEkko#5Pv!NGH7xof4MTvBh#sj7{zkvaeVXS{lPS ze4-_O{BByN+aQK?GEHdSfI%TxrtxM@nkC?P*n(^^WXj6MD zn-|Y(+i;gX>Iyi9^I?ZsDdDlTw)MevJ|Eh2(Y%GN=|We>_R2#KRaziIX+Qn|%wzUI zmh}jYBsqh?Ai+23L|J=DASx3yl9`0o`+E^D3jWDl0B(S(_4t>t)bH_|B zH;jNRsg0B_n24qeW8sNHDl)M=GB%p}P0Q zH6yv6ty}Javdt2UGam06STQ{+<_Yt1$>l2o(Nxyb0rLf0W3lU_OWb$&x2^2xYfaVq zI@X-E+&MPWnX60}Hmq7d5%&Y|Bft|Jmybfd^~oGFs}G>?u8H(v+=mCphSV+{9h_4X zNt&%@f#xKhIzdHXwG*j+2P2!ZnQX@GKlW`HSWq%6)jaCi{}2qGd_t?WXgM8zlFcR1 z;|6mzThi;$UWi6l2-f@Uq|+U;dwiB@>LH#?riJZR{!%iWOaXK06bdoy8)q_kqKdgV z?AAEEsf~+^!EmzFKfPxERU-rQd_AB|yJZGvPGQgFw=ZA2!aX&$a1-iu`W)f0o}9%L zvgs{OS8eme{I*nUVaT?)y{`}>Eov!`!(vNq8{^Mm8Owo;y#(}HiThl@L%^~HxwK{k zRz|N@#;1j@UWkJ>cm{07IF1X@HKv#>5SNYrmxetVh)&Y4*m9s92pYuE_WM`p=3efqDH+*xBNTkA)m)1vU(IoU%)FLTwdv)zx_&!NO@%OeBx?oFcq0#Mk?ucV|n#M9qLPv^Grj=b8?QVAYdnG)X+~@ z-Y_#Y+36H2f$@yvFIXmq}{yMzt!ar>YScLC6)`K+o(Sx zMQEce0Mcpj)EK;GqbM z`YBH;R(6#$ol`0;&`1i(4S-L?E84U5U#x?@hWc7Or-nj_rAY8j6P6nzeA@I@SNl_Fn4l@PyOSa!#_=Tl-e@D5BlTxfUKmU z>hVmd3&4?*hH`?5niz2A*8W0yv_2g_N4i+7}77iPRE$*hs%yJXBrEG}7 zHJZNgE3wpJRR~|P9S{ZqIkFJwPq8o=u{i^AG|hw|0SxgXbyyIy*m+#$7Xn$&7O3~H z@Yp<6GBC2tZFL5`i@Mt#uyPa9?@=R!qs&Ot3)#Au@67p8;X2Mn7${I65c6}+mjYW3 z)&S_u9+k-?l!;QXgj#OXM2dbNVz|siOF7{Q)>5_PvYJ|{thfYVbb=9&NB{oHs0i7alw%54mkUKhb312XlnhnW7#%z+B2Zv(GTu0#gt3VIYeO%i2k*;Mb?RH#uVex_dX^D~?yJQx zp=b3;+70hbYI=ok49FH7t;S+QV=`umQ$U#(oY@Q$W7ce>gV)$7Z!%~Sej{)eSXOD= zXpY?!_>Jg;%ln`ylTGz+ixsqL^e0G&=W$k?Mb%jXh`H)i>YQ0Bv3q1nnL;8F;&3N} z!^pwHVVlnvclVrq@Xm{Jx8MHy1CTVd2H7zibQTBV$v(6)nhX>}|MD1YR$GO%C(hV8 zvTE~1)YpG_$+lu@!8SD7>2WwborNsP%e*!JEq* zfY(M7t{Fvu>&KQP6gD-wfIuLjbZZvHX^U%4%VA36PfUOOBlyvXNkXh=@t##%SNQGJ zMmE}NkUjTDGqsDcnA&1S(MOF&J;7A|m`zPvz2~kn%`D$CD&dQGS*b$l&U6lS=S#U# zrj#rbJ79CopJpGcV7raldY?)w5la=YeN~rIhdgLUEuF4W7fGRSn-GbTdY7H_hyA7w zops1FVS`~uF!w+t-~)`KpXF|k-x#Ov<8XKaSI7ypzDe$X>1u~Jniy%pdG zx|GSz!B8T*qm8(99`)VP?BkDTN6~_LnGG8q>BKH43l`ob$-FPA~e=g0DtzVsScL7N?yAMaF80(f(X~Zd%Na zhIRu|q#LvCAW?Kuy%5k2l1s`wQaSMCJIpE|W$+Bl!o#vU3-1jQo(Atg)P{|Yl^{qf zM9Ap}jw-_U7z?~nXV(Qb8TEW;ZOgJ{xsS4o7jJoxzh!e;|{aKFI7P4oS4sn)GRYC$Wg_Q=nr&jvBTYJbB?;gi{?*7 z__fbdXfnM5KGM`3snz^U+o~=y?DZ#Ceh&NINndp5lj5|fvv&wnwB3<%UB7Pp2IWMt zs}Qss6|m3GW*c+-5dC`e9fdE!Yi(97S*+BI`_ z*rdlKY7(e{XALepT^@nFn5cLNI7ZZ3nDJO#ZYk{WG|2Ei$1G+vmpek%Hj_JgNyQM4nnp`9-xS*PmNfJ0 zubJP#2fB?Tj9BfS&MJDd{_Yh5E|-&IqjOUM(O}=A#m`;rOL?zd9L&bs)#KT=xX)%# znC;|<6WGk@AMfalmKSV#NF3bq`s>v0bqo&F6cA@DziTux54G_mdAGyaIn)A^U+qo) zt3a-1FZ2Z9OdV}sHCd|xM{e9gj;H5A+;f)6u=WG`osd*oNU;gQcDLY)lv#Juk*3ER z(YgOdF#t_d9IU}o-bmSDLmq|^;{T2X+0$9F;-*L#egw}jdL3SK&>u;J(ysHbt#!iA z>k;%4i%IZUGEO?86^sccIYrK!{GahzoRBwDs0t-Qm?qC-tBIndNIj-f#B?6E(*<@i zYrXkXCWG}una!w_+9YyQcFZ^&5Gj}}liuN0^Ukv`h}Z09-d6Ne%QRxK1=Ne3oy0aK zD`__R4c1Eii-7?sE}j=otOsW-6b`t|IvC5$D+|6k3Yv^y1~RQXj!LqU?f2ZV$(|<9 zs<`H@GF-AW<0tjdT6VMdDJD835i{HP`r&ogR-Brdgbb=pAR=E4!;ESklqpF3GPT`z z?YZ_vYnScL9O@}|1aJ91kFc{i-5#6W>KUe{jtH;0P=qnW3-k9d&(H2ofW|*;A!7`I zO{VF_P0p5n(0~Du+6eVDxHt_0&~S54xEc`0o-+>T;5SJtD%yZ<0x)0)t=R!S=KB(^ z?x~Q9A?0p5cgr=xj;?cVzx_f(BHZswaU~l|uzc=;HR&j`l$HS`Pl{Jz04YFFh4q#2 z4`NjT*2N6Tlv23_dYTkPEF#D1=(+;+E67*F1MOA_rDBWOTP=-xVk;*`{c-|9r@K@v zsj#@BC8?22mwS4T*hqGEW=2vA#9W>`-<7L%&sSts?LC8|3p;vC;audh(ebnOS8PeR zVFv?`dy9Xa118F#`PBW_F8cLoMk`f;b3k_X0hWZ0UqjQie-53`f-2n;(g9?{Rvxe{ zFm%HcJz)z$=PyPWfNM-W{Qdu-$FL>?-F0&91tfMbK~KhKEDRW+S@_!AX~uj{+?A{r z21tukNPV5tN+i*Q-K{?G?`NJVJ^eJ2LN#KJVE_Fawoo8(8LgUh`$B+aIlwX-kFYI3 zdYB`!)+v!l{nU%M!^fq=Wu-{MrG9xG{eiwUn+u+@%~YRnlglpl88N*JsaQNF<#KcA zf`CEg=___tbBi+b=4BRbDised&n+J4D5g?{s_E8^F_%>h%gaM#k3VN1m0n^w68L~^MK|@phw9I$drOvp4}~YZy=tf zfbEEenv%{2_nJTi!~*C41iQj@%sDgoO9YgMzaT}2X9{4eoUS~?w}R+@+hlm}!rai7 zm9bKwWYHN~`#LwkUWXV-J1#)eA|igt`2zeFSK6$QxOFm?+aI7{UPW9dhDKFB^_wiZ?5_6qb<}T>7C(sIzG2IN+flC^Suhw4xP7t9o)Kq7 z)2{T=??Aa*#(K~CwVMqvWn#W9>+@giZ`bLhglT%UVr?ujl|znHiuyFSbY`)p;EIIn z4+jFcHkT2jK7Dj50aT*K&~C_eW~fd1Hkp^KUEZOp2?8 zYH7m1hA~J6Z3_vB$@81ROd=7({$E^k zUKFr7QYZCBq1f6QujYmv5r@~>9SOgUV3u9m0N-HmKn#)Fz^+uwWZdr3$5Cg z>#UBWJHGb^%KYBdz6IkxJj(lqm7!uGF+Svw!i;L6&t)zV$Bc%wuV6CNb0SGpBC-|3 zAFE6{smQv1)E{3tvoJDt_L>Kw3%S7JRQtOo?m1T|5DBwJ)t*(}Sg>dsh?gTVH<*P2 z$crz8g}llj>T@|<_PCF9LfTlVij=4f^`1Ix3#Jq@61vAA-ww5l)OXb?wIQ*TT%I%O zG+J0->rPX*Q}>k5_4I~SDw*o8>@s`SW%HsjDOag7r434%`I3OU-_q-LTZBTNQ0NVL zf=aDQBy^8L{h))c_l-ey6Yf6PPiy}}cshBLM;hR!YF-*vByjK8bcY+IuM*=Cy|eHX z00SebVTNM-n}a>z2)e2e|2CRUXiEVz)okT3{hRTK8C)7gV;(l^;cL1m2Y3RQkG4}s zY%XJYg@L*_FTx=SK{#n4HQto*g1%?}mD7m1T6sz-znBdn%yjsLXOAAe=blFv<*l+2 z!6G&z%V2L>&R)Bo&Ep9TI=)byRfuE~yHjHj@VF2V%<;uiJ9XIUvbpr@Dv$m78$8{O zP-qRsfW?%pKYjDf*J=!L`^@`RiN;_}sEqUN?ty`JYYc5wLqjrCE3tQGYF%1QJefE% zL%p(4%wtNSk<25KvbCm#sm^#?Y(iY?-~PEEC)jh_Ejz)m*2+e^tGQ@RPqwQQHotu^ z+vf!n_ZH6@rw1L|X4I`#7z{1A8p z5AE|`GWk4-MXyz*BI9Dv@1T?k`eIb!tSg$MI6HTqb?DHKue$2ThYnF|(NDG?IB>_y zS(D8ktC;SxEsrKm*`O=?&b^mimftyg)m8HY(Pf!%-0Pd(F}PyG`H6g@tu2vvhg`|2 z;l#$tg>AX++Nk2C%Oa#rP0~~+ex1sa!7`U6ri`O zN7C$1j`yy#O!z~{TR%c9LlPTQ1O;`f=+;0jTgsiY-TLN#&{lv8HaE`!BZSQsRNt0o;JANj1TtRtKaM>Ixs1*8X<5UaX_@c{H9z-B9$iq&`WX>bg5mbbvZouzBD z6&!yH7?0LKbOw)ZRK#UGbU+L2RQQSk8w(6(huNB~-vjn1Td3*D^k|{Z04_jiEE@Ev zWV(28u+=x=%?7#+MXNSTju{}9uP0iebz%|qcyzHZ6)o?q-%3oQj8&%5dR5xsh9Ws{ zD6{!I_l|wJUYo}j`l~K#jwh+(^`H0(#Vy+_CfF}sQ3!Zyeg3SH&fU@KGL<$~%xa~S z`2In-M+Ve%A7#hNF1+w7)5Eo9Do~fPd1!*c7710R-q0dmS1)XZ;Zh&ho^hU^&Ivz6q{v#3Ni?WuHGR(a z*__dlq4cKxz8!nBJ@|Zov3V@1*zuN!(~iv27`QB)R*{vQ{_b!-+tqVH(VQ|_tKdK`T+%@|B#b@-!vK&Tv!)x$QJUE`=qiB%SsPw@*m%CduMo$^$l)qq=k<>y zZ(6%_>7Z-r@Wec{7JLL7^(h0UJQn9Kncp{J@11|muV6>Z!}Z@2>V>{t@~-dAY9 z8b0zmxmx-}=@ROb<&!g`_WOT@u9Yj~>XJfce()Z!IGwoM&0_OAHDwiXXr!fsu;{-fNn&DY{Zr4eh91eV7VTR3x2Stw?Hk42Buv^>2tLx zR8pGaG%8^5*_NGUO4w`Zn~JMeVplaSQR4D*7_F& zz21drnOn@0)bA?!T8&lA$GV7VEJfE8B7Vrk?#)sAV8skn*p^`#o(;Xnm32g{1F21y z`L-S`4ECdlIcPDP!W6YKn9CdWTJ1Hlv=0^}8w~W1Dm5_FiT+WT+@~{VA#AQE?zkB4 zoCA93X3+m3KobLWanoGVOGVzeliebFyUVera>Gw!CYQqMd{Ma)x*QXSZ`Qh7sU6V z?^*&Kn0T@~uGtB(uZi4sE;40^3`c-^zMh2mLu_BWK@j!d@QVXbKi1~|O{OVUFO zZcFm`4;T+ajSHlvNc^VoC!i4x@leJ>9||9=fjn3cKxSc-$RczX43}8gH}Safy&pVr zvt#hD^vaE^sWTMnYIm<7?C{&kzn1SP-_7O9YI5*6NK(Upv&_H-N+aO31;&u3jDlJDMfOZ!z<*|oML;~My{LViIeKq94D z(dzvLBuPsz@wftIHA?*s!oKmH)>JP3l8$K7uY`Ni@LE^2dKx*pu3U$rn@Hz!hZR{Y?@oV=SSdp7LuxD@U>#w)g|5;Q)2M=qo zc_v(2_Wx$HQBEZ@mS9=7(yl?;a7wrf%ud1%P*JT&{>O8nUVTuR-*%P@4-j`Qtbgu$2(!U zFlPYtDD6FC=EWT+@Wz2Z{^y&f({ccxpq}=YQrjYxOsYcdtWjrnO{!Oo-hY4X)?3Gi z*}l+1g{qv72rL?iC;zOcu-jiNWV@&%c(f8)uTsO7(@#sUzg~J%XNL82Ch8cFm^BC! z#zFZ2+tBat9_UL8K?hO>A~?W1n4H)mJLzpf+2B!r(WC=gu(Sy zn7gA;dHfwn$Yk?HoEm7^&kC=+Qg}_q7nW2i>Te~Lj$jOOtsA#oQ0{AAN%YvGE~MdSE<4IX+N=&zuyY+zP^m{VjB_uu{3HO#ud_@z+n z(@P+Em?VUYQ3KZ?Vh~nY!OalBH-{Ow!u!tPj0lr9 zR~i*xG6zr^g7mbjkKcP!4(SWP7B=WiaP{Gh!)HS}PGz#jZ8q)`vsr`7cIwU5k!}2| zIucyNt3HYNE{vQE=!w(>qr=Jmh+ho>Nf^Iz7y2xu`2nmT;PUHLr2wX6s+Sh7x~g{B zW$mPk<#1d5K%cNh;L+$wk4mfTH`@0j{o#BmZPgURnP@7qZqbUd!0^=Qs!%+X3T8CM zMCrxhzJbox{n!Euu#&9fRcHdgU{(q;zT+T2FGOFMeWOMs zdXCH#V}l3!dxZDy@oLDTp9RekP*u!e)?wRXCzMgG>ACB!+65O>It6_laQu)@5RZ`E zxS4e=;`nMol!RR%rA#&l0dN)drb#BRd10fC8|)N>>;_lAfx`>f4abp1F0PWcpO;>F zrSvEazz*1`&k8EA9+<%Lv;2G??0dCC_ZFZD`0~v-Db|&zaWPGS1_Yi!6SN*NKSKZa zGlc4yj&-V;sYf2E9y-+Fb#m>2ez~fg0xXdmon)p$TwE^Kw^vX_XRhjPpO&6{vh*XJ zR92M9P1IlWDh;cT1*Ic2#*xskvAEXZSVh;>mv$^y#UQl;D0lt!Vzo19=i0+uK^`bu^a?aOBS{iVaIMyYa+wi4_H(pK zZ>ou;LfLzz7hWhm2c7*gNkOK7!3xyx;f*r;@J0pVqB=M>1U)DFF-8C;S!O_s(H(+u zV4eoIz=RDN|1c?=IhckDO{*IElLi6+W#Wa}7hk+&{O5a~Ggf_Dmoiuyxy0>M+WjLp z+)&-JWz#0PveGS3$TTqHewxamS4&9kD0yQklZyJJG#?Q>E=wwG<%x|SW?yL4m^??%*LceBjj8K8jo+%C;d7VM}Xwn?Wu zi{-HByZbY5BHIk~SJ`Z>A%~F*F5)BS`Zm9v?sUfk^Y#^9e)*j9cI3Sa9qKDxWrrq5 zOy)sR^>VHMQ44rgC?(|!f1P{r#oU`*zEBEdA!TAd9mBZ+YGjh2yUMeg>;LuUfYWF` z0|Tr9jkINkH5P49##OJKa*5@}C6=6UEU;7@;7^G3lc~jO0YTLDu3W(Ub0j|b8uKr_ zkf*QlPXPA#ofAEMC2!oluzKL;(A8(atVq6Zkjg6i)1SRwe z%!H>13pQ}(bQLWlv#u1)hVTqMt^vP)3%X;oSfjHM)d%&|YP*IFc{Zuf3N3nVg@oQhx!uGkV!p;+{g?O5XuY zs<3V(o~pMK4{q$WMBGyi9CCqeb~(cdHQfnX;^D1ry*0KLM@Nu<>ASxNdK^(8V=cTtDY3@{%8+X(kn zBrqysd-e3Ut-*3cTLBPRXt;|w%H0rqIBQcILYN$0h=+$ZKIzNGFv@YCdgMp7#*$&M zbl!QjbIu9F)Fm>Y&lyQ;5IvOg28{)MCOnt{B~38cd>GtWNLAQAgF_kSKz{4{dQd`;Oez)}2TcwBeUhP2l!b#1-q4wVH`HNRe z)=ZC11vJjyS}`%1j49QEZYc1z2Io(l(U*eC-f+yD?OPhigj?h7CZ~@2xJj;teBwF- zJnLElaU^`-;BL^Lw1!^GwlY4L*?Nxz!DUo48SZ;uWS0UUxUh>gKhyu@@eLGv(D z-PUQ7Nzg5~6yEZvf%@gUfPnzwLEcNl)9bGq-kZqVLH0XoKEZ zHJd-qz4cb^c`*#1m*>?AiJbbnpw?oWj{pS`QHWuzF;k;VqL-+m4cn7?nN^NZNMDAW z1nryM$iO>oVo+(m6zGIWGk0^)<}l0AP0oez2)2H4umQgcTLE~J=Kmny9PzLyXw)DD zU^BoVgChg!N+{J^ow6-#>rQP7!jvSr(oUu#F^5GVR z_Efz2)>2ocuR4Ob8dXjpP~3iC)v|#|$QwW@LMsu}bjFWMZ@yW2-XxV56>^ar8+2M$ zCmRe;3;N`H=kIw8+jMw7IqsPW;XbQVsRSqo7>55$rl1H4bU#EyfI9RZ0-8c|1U;3* zW}^p9%PGmYdUI<*r|TKs>)qAUs`>OcEC%b1w-Bl#^ySwCw5&3b{0l{)y5ZO{E0hL9 z#i_-8`Q^1edrEE#-{k8A9BkMU(_?2W-@SA7mVs>h@|o_6P`dm8&#@Z zxtT@N{b)uk6pPAYiBR@z;n7D6w;5&fqEu<49-fnO2H^Tph#iu!qai?dm^CWOVMlJ$ z2bwE5$7e*dPva$Z*wBUNPDM_TE6f#F3}#8w6NIT^aEj)=VXR2frS}cg04ETCn+^;$2!LMz z1>pZBCO`~$!70lAyK5%a%%DjjPo>R7GMp}-n+Y!Qr|wmgelVHaaMYPW$)WK=TgsQ! z0vG}9tMkiU%K4p_Uk+YDyu>M3Oeb)Y!-iEAg#F`QNC*geBzkwXP@`0C21F;#wjmvx zt7EfuO5z^s<(^DAy?wWN=Q>y~--&h=sZO(|YA}CL{K-#>PpfT3n^a7FIg7BEF4#xR zKvm8XjIYppPKS!!^o6_JO%DKCrkf@4SoY!OXWa%&5jgz+a+RAbatc`>b(w}t-Cd$m zsRZT!iUWrN_gv^weatCawK<;%RfNdHrat5eg_8NXNar$F2sfmv!LCH8gH;D>B2PM$ zL%o0?2M!cT8_#QYS>~uj931cVEgY0D?bX}d;ay{?sk67;E-ZtaL@>_1_J*?*1!Zd? z9iKn$S+RU|R3+d>6izlTX(~RA0R)KjQR$~YEj^3HM)sQ=R>6H#5->h2$;an=U5 z_$v(P*M#^xZo;Eekxf&Oj-cWwDt-X#VD*AAlU{-c#l8S7_<>vuAOcXXK}a$M_LU9x z&;4?8K9wX=nVR0H(w}+e!Gljd1?d-)Rb%E>EC%xR*AE@qy&FC8YiEuvay$J=_xAq$ zt;ZI>Yk3>V-JcWF!BjEhcf-m)(NJbIGoHN&$4KGq%w8VDdQo;9o}@INphhs~GnoW{$f1Df(#q)&Ac zEMQ-R))%+UFSRB1J!f9jHNUbZTKVN~e_Q$2zg`dRUvlHIW3l1?!a$|E*z~6s_$Su} zrd&=>Xmz2gw~*nDM+)C5+aUQWlXB_w6fDRX0cX#ydxWq4m%VrjJ2NhInjK`WuSy+mqn@Y) z^8WI~V_VPcDUaUIj*4cW?V^iXw{H(Rg_87!CnGY$v># z%BD@RE>Txmp(_DmhaiU|RH9;``JD^^KP)ld-K@$AGD1)BIk_ z6ALRQ<0qvb|G4zLiY$``>UTMaeszO~e>3$H6s3LwXJgPc9oI8lz=I@?qthD)n<T)y2?h@@Tq7;OSgyt9G`T5_`9d_P0I~d#+TPXP~hPtG5HNsXH6+pc^h1am8^Cs$J#Hyyu5GUawNVc zGA|r)!4MtvyESK}zldneS*=o{cnYr1uD9s9#R`e8@k*pd>hoDxU|6)RCeu6E?RYd2 zxX3l+@Y&sY&(1|j|8yYcAi*-F_T;ez4$7kkrv1@55S$}8LifA6XoQr}OW8oMbTB^= zwm3F392@|C{7J{qAh!A6hIQkD=lByJu+zD~#Bufixn|dat&Vq*1lcu4ovxt0b7kj9 z+Z9*3zjJ-(m^>0fpyYiPF0B0~0&?-4gwed*vqUQd z>e#Kpk+8yKrrz`|o${`ni-c_kk?2dD-g+Iz4r{3Y z03R@WIaYLsTAF(DoOG#>Ph`vya^PE|umW8kXKoqk+lg@~K=;-qTKq77qyy&=X6r`& z8%{CgoXYR42E&69-S}2x9vp20VauQqZ?K?`xqLl@EqMV_9foWtpzZb7r#iS+dkx(G zrfACVe@Xb`2WuCxon*)J42D}H^+Y^l6B?nY;`hSfNQEYXlnj=DfmjTMunN`oil1V4 z=n=KEB$t|L6@o*JJCNV+Y4!NQ-24q$74YRSuHX=ZL8_!qufJp74+NsT2k&l|ssc1t zxu9m_c}A|q1GA2C4>Oj@WV5^nZn2?L0Ei$uItR*FjsE{L4Pa&x&;iFJp#p*N;6k-( z?E5fIfUgX0acy)+VU!Md&ncN&tA+JG?M!B!bwcr+amMM2=NZ%@l{hjAh0~4fai}F= zFF;$`9LCmQ+PAo~P@PA{)W(!4xR~mfVX>A=w;KjY#G5rBa8L<5Pp7COqQ69%E8wPe zDoV8f*w_n(>$n;@0ZfGk9g^TSk@)Z`7&`Vp`GbDx$pXSa)u)*@nZ+1MFELw+(1VmQ zzx@V!qOR^8P)4AL{N|f$7hjy~WIN;%Cs?{o#}GYK|0AJza%~p&+WIeCy6j#Q=JG>= zlE?IN@m900O)M2je~Q)6V;VO>?u{DZX{m!&J4W{wm? z(OreY4b>vGQx2Vpyl^e3+iFF!aG4`CeNIPswGhgdQh#frJm^{z*b$FG&KggB05i~e zlCL*AVS1K{8-`OV>BjpH=#C|Pv1)iYXfy}5r|)m}^>nNt9mmUqz{sKvFIppGdqhH< zhKv~S>5Fe@c*c@;(#?wMv@j=BNi1Y=coLbx?2EgMIw=e&C05Q5FgfE8q*Y-S4~Lgn zp)%}p$jWL}J8Zy`S?!r*!e*9=!INx9CA8NkCU+ZD0qBHeR6u5qeZgSJ`lSW4e$ARn zcegXd7zI@r<=V7l8@nAEnj%+y+>z-lbmpoFZzz{2XKLwM-WOgv6$p)Wj#hf2Y3(B% zaJmS#T9qY~4^DM0=pN|`75Z97Iu;$xKlN1oVK+{(!Vq17GHPj8{0YUh&3yq_ zj!xAvI7D|6%+kd%gh{4BdzY;&MnY$Q$8GXd7qn65kk%J|E*Ef~`!VcQtf)|sdPN4K zqAxo3mr%R3)oI+etvWiI4iicftWq!891wZHL8zCCt>FvS1--F#+e1oIY;x)1<;9xI zSjakb+zo|Rr>XdWOkR-j_+R8-d@=vFQd#8lRn(s_$8|Jg4N>5^A41*`ZyyF9k;L2s zoC9V}1MxLY-;eH`V?jUx3|cy*S}6Jg$5eg<%S<%20EhYp9LJ^~LPFf&XN)Z5 z2|_J+r!qbxeMH<`*4z~X1vm|G07PuYmtgse8_?pEJR{uZX;<9H07($z&b8fb@zsy` zzZl8!7k#!W;NNzh+tYevudjE0&oZTE;dvSob#s*UUEo|b@*z61RodsY9X!~&c5TV0 zG76+dbWQ6(*V1QqCU1COp?>vjiV#BRD2pC_r8=DUgR;Bc!sAJ_=ZUxi=MJ~F36uZg z2~{PHK=@JNrI!jXs?`w8f__0ziGV4Mv*&4EhL{QH#0G*ks=om==0K(qPHJQiX0_t! zmyC^}llU1s%qN9zu^&MDaLZl(EB85^=m0ZyfBw-_tk$>8zk1PpR{?m{J8y7#3M%p?Y4lR3&*IZN z)Gl_79}O;|E;B+qD?_ANXFkJcEt(< ztlwL*U^qw9DGITY!%#PG1YHRp9BNd~)7CxkHl$OrjZBx$$7Lii&#YFV<0qK=ai;lX z_J^81Kn}(N>WIMRYRx2VMv0J#-WPD05@E@y)O}J9R?Dh^&O?V<&pF3nR=adIs065l z+}sS*0bB{ia|D_|tmww6^<&;@Pi3e!YW2TkRf=4C&AbASnqlcxWxf7WTt@T?U(A!& zgkl}_f0`^o%)2>6;`{d7r zCt1w1$2`?;ZiGzkTroa!rgvxWI!9{_y+`l_q^_KX=9WS>2kYIahtN&N$Dw+Bc9w7J*P%ftpN)i2niR`{zYGfvA-aeVZ)mw;83H)e3u#wr^iEbspR=lb&z$ zH28XasJe6EXvB#j@^tGL+$|_Zz;{d*Ncdul0z)0#HU2zdM$BFKX@&&xzEY^u=<+(P z)@77&rh{FT<;f;*`sNr>W*hq)1gA5 ztqxJ-oZG4KhWid5F7MgnfS@UuxdZH6JJ?iir5L+UXp?nezw5rkho_tlw?F3eSPBNG zEx7pz^_yqO$(b$ZBhw8xtXp7-Chq%Si_dAbq9j4+WWthIZh61>=9|T*Wbj+vd7GX3 zH5AMsqK9ootIu7&@Z#Uz*WiiofS!*+g}0UNQUc8ohE8it6S!P92gXFxG7qEIuzg9F z{$Rm4StRHr+^GUV0_QOHiX}$s>DG$l?7oUO6bQfguvTp}Bx8xiH&OT2&?og_p>#~P zf9KBH%#1=E55Jr7(AHe~8Jj1|@p&9R^@o%~&8l&&|NaxmKh7!t5PYII#LTDXe4@q~ z;fOXEKZO+nZRr63fY%F8VxLK)#{a~Gphe&`0cd;&h;$n00sAm+Tzk=>87-N-sI3U2 zcF}63c$r0OwX*u7UKT8}qnn?!?K4y){t=pVt(yjJ|fEB7+x8`j&uGu95 z2T==!Tah3)86nXNci&xQ!miuZ=w&kMy7MDN6?T-ocI5=ruXG6P$!+U4z#5GIyzPS5 zFkG%yy+869R8NSjVwvUlSVDdbLluiM1&0Ug=%|0>q2wO|UFZi_(b(XIIxieUaMPK# zB|s%QvEXQAmfSe9g$V&Emf2Qn2$R!KG*F{a7=mAPN`Qf8jgwVE3-qU9j$&Nm?q~}s zUn)g;<#9(}ddIe-ce; z9b$h9nrmj6pAOpsr(-;%yY2AF06eka;7<@DIt>56oURv3?dpjVgf5qVQ^9jiK74%m z$>?Ca$)MF#Alwh*@qB^6bCdVI_T|P%JG%_^wat1NB-UvvCjHx`pZ`35+ij&gO7|U2 z^-OvKus&%Sy3tJr+(CE1k-X}u(jD=KRgS#dKz%+7iEKJPvLEhQLf0nco3;61_uw8N z2)AMoq?P-r_t?1mlP?nY9^%vq?nj~jZa-2wQu?&?X}wx%Q;&E}S6x*)`|Ny)ZMIb) z!r2Uq>^%Y>pW;8Z)u^wKp@(qaa!dVJhjq%TUL^P+_vV|q=QU8zEHhERZi+B5bZzb? zv`s&ey5X>#QsBvesSZCzo7qI(N-m%{_w+k z743pdF_ME)5=#a0w=nuXq_7p_fP(Kq6$z^ocIXlE4?g&y^t<2vj*eB~{?Gs*ZyQug zKxYW91DumMKp;@XG-rWQriIM`hda&mKFzzr)+jI}3xq6;7`k@9;)%s;h08B>qOYHU z9+xjZRzX5i4RgM7szQA<)1w;jTCcsfb@ggTR-~|`!S-wv`w@Gno6!pD=7*VxHGK2b z+0;8avh4Im87}J2T}EGF*OX1KKihN{n${Vr2C?XUY=S)}H5K__fPInzAp+Bbc=nd; z$Frv)4

    Z*VlMkLePx(`_>#=4lcO5o^?ete<@H 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

    q$sv;g=e&p|(@ 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

    1. n*fd!F1-%JT)OgwDKPJ}QN25ZRLk$bPa^EgRkW^e zyn)^OiB-Ad~Ow5^eX`FeP03 zdLLwppudefgx^DzAv9>;v-~+rfM#kx11F6NAxsEf-8geHT6`wY#$Y zAsMk&9)0xd+4+r?gNs`g%pHZo?AbL0LDj&IT?#Y1bL(#jc>Q69(J#tmirub(N}lpw zh#)RKpW=9MR^b>(2J$E!2E8vQQ;o(QJGvy)3g#%rk~Thor$7^w7|2hp#=cq&&hzy3 zTpl34X#qV+=r7{+D<7=K%q$St~>OIY(ABnbJT`S#v>Q4o$Ubz@(nJh z¨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

      ;!$GZ4M*oQoYj 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

      0S%&WZ1N1bZ@zwYJ@o*$cu3u$` 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�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%0APJ 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 z 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
      NC0^b7EP}fP&KQ-I}Vh#uiqTkJcm4 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 + + + + ./tests + + + \ No newline at end of file diff --git a/source/vendor/kosinix/grafika/sami.phar b/source/vendor/kosinix/grafika/sami.phar new file mode 100644 index 0000000000000000000000000000000000000000..3cee921663cf58de33871d0a02a70d488f017005 GIT binary patch literal 2213889 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≠qmUimI7c+7vcpo}n%dhIiwo;~9J#dv6c|wHexYQVIT4D0#1lukstf zsfO|>JY4XpzJ_TH3LTJ=_R^c@*8q~Rzl@?a*sN9_u$ErTIlUqC)h9`ICemL)1kujb ze*}s^bEshUj=Y{rkT1|QPt`K$8Q_aQIPdH`fV%oYvVL1mt>y!n3q)Tt6Mot9{(wwF zZ>wNyXt1Vl9ZN0wB0`2Llh#9A)ph=|IZ5s%P?yjTLNzve$A1x2(aASWF|BELHqF7N zKVS5jK?yBjkpIj~D%1Q6eIMZYzm+P@w*~=$(H?$uWkq$gtA*iI|MVFK(l^-srB2VR zficqbf%NjJ(GuF+1EDMe!Ktgc@n>sKPTI%`LkC5$;9qoul4|PiDv5(?bgcc;w

      X5nYP1}vOVH&9W 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

      h&f?hG>nJu3_B|wA-zhnN-C_u2LCG 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

      b7-(^s5F>$q*g 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�sgMg_ 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

        jVY4zrn)>j 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 |pEbTnAg?=Bkzpb15C5C5i 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
      '),s.pagingCount>1)for(var a=0;a":''+o+"","thumbnails"===s.vars.controlNav&&!0===s.vars.thumbCaptions){var c=e.attr("data-thumbcaption");""!==c&&void 0!==c&&(t+=''+c+"")}s.controlNavScaffold.append("
    2. "+t+"
    3. "),o++}s.controlsContainer?n(s.controlsContainer).append(s.controlNavScaffold):s.append(s.controlNavScaffold),g.controlNav.set(),g.controlNav.active(),s.controlNavScaffold.delegate("a, img",u,function(t){if(t.preventDefault(),""===h||h===t.type){var e=n(this),i=s.controlNav.index(e);e.hasClass(r+"active")||(s.direction=i>s.currentSlide?"next":"prev",s.flexAnimate(i,s.vars.pauseOnAction))}""===h&&(h=t.type),g.setToClearWatchedEvent()})},setupManual:function(){s.controlNav=s.manualControls,g.controlNav.active(),s.controlNav.bind(u,function(t){if(t.preventDefault(),""===h||h===t.type){var e=n(this),i=s.controlNav.index(e);e.hasClass(r+"active")||(i>s.currentSlide?s.direction="next":s.direction="prev",s.flexAnimate(i,s.vars.pauseOnAction))}""===h&&(h=t.type),g.setToClearWatchedEvent()})},set:function(){var t="thumbnails"===s.vars.controlNav?"img":"a";s.controlNav=n("."+r+"control-nav li "+t,s.controlsContainer?s.controlsContainer:s)},active:function(){s.controlNav.removeClass(r+"active").eq(s.animatingTo).addClass(r+"active")},update:function(t,e){s.pagingCount>1&&"add"===t?s.controlNavScaffold.append(n('
    4. '+s.count+"
    5. ")):1===s.pagingCount?s.controlNavScaffold.find("li").remove():s.controlNav.eq(e).closest("li").remove(),g.controlNav.set(),s.pagingCount>1&&s.pagingCount!==s.controlNav.length?s.update(e,t):g.controlNav.active()}},directionNav:{setup:function(){var t=n('");s.customDirectionNav?s.directionNav=s.customDirectionNav:s.controlsContainer?(n(s.controlsContainer).append(t),s.directionNav=n("."+r+"direction-nav li a",s.controlsContainer)):(s.append(t),s.directionNav=n("."+r+"direction-nav li a",s)),g.directionNav.update(),s.directionNav.bind(u,function(t){t.preventDefault();var e;""!==h&&h!==t.type||(e=n(this).hasClass(r+"next")?s.getTarget("next"):s.getTarget("prev"),s.flexAnimate(e,s.vars.pauseOnAction)),""===h&&(h=t.type),g.setToClearWatchedEvent()})},update:function(){var t=r+"disabled";1===s.pagingCount?s.directionNav.addClass(t).attr("tabindex","-1"):s.vars.animationLoop?s.directionNav.removeClass(t).removeAttr("tabindex"):0===s.animatingTo?s.directionNav.removeClass(t).filter("."+r+"prev").addClass(t).attr("tabindex","-1"):s.animatingTo===s.last?s.directionNav.removeClass(t).filter("."+r+"next").addClass(t).attr("tabindex","-1"):s.directionNav.removeClass(t).removeAttr("tabindex")}},pausePlay:{setup:function(){var t=n('
      ');s.controlsContainer?(s.controlsContainer.append(t),s.pausePlay=n("."+r+"pauseplay a",s.controlsContainer)):(s.append(t),s.pausePlay=n("."+r+"pauseplay a",s)),g.pausePlay.update(s.vars.slideshow?r+"pause":r+"play"),s.pausePlay.bind(u,function(t){t.preventDefault(),""!==h&&h!==t.type||(n(this).hasClass(r+"pause")?(s.manualPause=!0,s.manualPlay=!1,s.pause()):(s.manualPause=!1,s.manualPlay=!0,s.play())),""===h&&(h=t.type),g.setToClearWatchedEvent()})},update:function(t){"play"===t?s.pausePlay.removeClass(r+"pause").addClass(r+"play").html(s.vars.playText):s.pausePlay.removeClass(r+"play").addClass(r+"pause").html(s.vars.pauseText)}},touch:function(){function i(e){e.stopPropagation(),s.animating?e.preventDefault():(s.pause(),t._gesture.addPointer(e.pointerId),C=0,u=d?s.h:s.w,v=Number(new Date),c=m&&p&&s.animatingTo===s.last?0:m&&p?s.limit-(s.itemW+s.vars.itemMargin)*s.move*s.animatingTo:m&&s.currentSlide===s.last?s.limit:m?(s.itemW+s.vars.itemMargin)*s.move*s.currentSlide:p?(s.last-s.currentSlide+s.cloneOffset)*u:(s.currentSlide+s.cloneOffset)*u)}function n(i){i.stopPropagation();var n=i.target._slider;if(n){var s=-i.translationX,o=-i.translationY;return C+=d?o:s,h=C,b=d?Math.abs(C)500)&&(i.preventDefault(),!f&&n.transitions&&(n.vars.animationLoop||(h=C/(0===n.currentSlide&&C<0||n.currentSlide===n.last&&C>0?Math.abs(C)/u+2:1)),n.setProps(c+h,"setTouch"))))}}function o(t){t.stopPropagation();var e=t.target._slider;if(e){if(e.animatingTo===e.currentSlide&&!b&&null!==h){var i=p?-h:h,n=i>0?e.getTarget("next"):e.getTarget("prev");e.canAdvance(n)&&(Number(new Date)-v<550&&Math.abs(i)>50||Math.abs(i)>u/2)?e.flexAnimate(n,e.vars.pauseOnAction):f||e.flexAnimate(e.currentSlide,e.vars.pauseOnAction,!0)}a=null,r=null,h=null,c=null,C=0}}var a,r,c,u,h,v,g,y,w,b=!1,T=0,x=0,C=0;l?(t.style.msTouchAction="none",t._gesture=new MSGesture,t._gesture.target=t,t.addEventListener("MSPointerDown",i,!1),t._slider=s,t.addEventListener("MSGestureChange",n,!1),t.addEventListener("MSGestureEnd",o,!1)):(g=function(e){s.animating?e.preventDefault():(window.navigator.msPointerEnabled||1===e.touches.length)&&(s.pause(),u=d?s.h:s.w,v=Number(new Date),T=e.touches[0].pageX,x=e.touches[0].pageY,c=m&&p&&s.animatingTo===s.last?0:m&&p?s.limit-(s.itemW+s.vars.itemMargin)*s.move*s.animatingTo:m&&s.currentSlide===s.last?s.limit:m?(s.itemW+s.vars.itemMargin)*s.move*s.currentSlide:p?(s.last-s.currentSlide+s.cloneOffset)*u:(s.currentSlide+s.cloneOffset)*u,a=d?x:T,r=d?T:x,t.addEventListener("touchmove",y,!1),t.addEventListener("touchend",w,!1))},y=function(t){T=t.touches[0].pageX,x=t.touches[0].pageY,h=d?a-x:a-T,b=d?Math.abs(h)e)&&(t.preventDefault(),!f&&s.transitions&&(s.vars.animationLoop||(h/=0===s.currentSlide&&h<0||s.currentSlide===s.last&&h>0?Math.abs(h)/u+2:1),s.setProps(c+h,"setTouch")))},w=function(e){if(t.removeEventListener("touchmove",y,!1),s.animatingTo===s.currentSlide&&!b&&null!==h){var i=p?-h:h,n=i>0?s.getTarget("next"):s.getTarget("prev");s.canAdvance(n)&&(Number(new Date)-v<550&&Math.abs(i)>50||Math.abs(i)>u/2)?s.flexAnimate(n,s.vars.pauseOnAction):f||s.flexAnimate(s.currentSlide,s.vars.pauseOnAction,!0)}t.removeEventListener("touchend",w,!1),a=null,r=null,h=null,c=null},t.addEventListener("touchstart",g,!1))},resize:function(){!s.animating&&s.is(":visible")&&(m||s.doMath(),f?g.smoothHeight():m?(s.slides.width(s.computedW),s.update(s.pagingCount),s.setProps()):d?(s.viewport.height(s.h),s.setProps(s.h,"setTotal")):(s.vars.smoothHeight&&g.smoothHeight(),s.newSlides.width(s.computedW),s.setProps(s.computedW,"setTotal")))},smoothHeight:function(t){if(!d||f){var e=f?s:s.viewport;t?e.animate({height:s.slides.eq(s.animatingTo).innerHeight()},t):e.innerHeight(s.slides.eq(s.animatingTo).innerHeight())}},sync:function(t){var e=n(s.vars.sync).data("flexslider"),i=s.animatingTo;switch(t){case"animate":e.flexAnimate(i,s.vars.pauseOnAction,!1,!0);break;case"play":e.playing||e.asNav||e.play();break;case"pause":e.pause()}},uniqueID:function(t){return t.filter("[id]").add(t.find("[id]")).each(function(){var t=n(this);t.attr("id",t.attr("id")+"_clone")}),t},pauseInvisible:{visProp:null,init:function(){var t=g.pauseInvisible.getHiddenProp();if(t){var e=t.replace(/[H|h]idden/,"")+"visibilitychange";document.addEventListener(e,function(){g.pauseInvisible.isHidden()?s.startTimeout?clearTimeout(s.startTimeout):s.pause():s.started?s.play():s.vars.initDelay>0?setTimeout(s.play,s.vars.initDelay):s.play()})}},isHidden:function(){var t=g.pauseInvisible.getHiddenProp();return!!t&&document[t]},getHiddenProp:function(){var t=["webkit","moz","ms","o"];if("hidden"in document)return"hidden";for(var e=0;es.currentSlide?"next":"prev"),v&&1===s.pagingCount&&(s.direction=s.currentItems.limit&&1!==s.visible?s.limit:y):h=0===s.currentSlide&&t===s.count-1&&s.vars.animationLoop&&"next"!==s.direction?p?(s.count+s.cloneOffset)*w:0:s.currentSlide===s.last&&0===t&&s.vars.animationLoop&&"prev"!==s.direction?p?0:(s.count+1)*w:p?(s.count-1-t+s.cloneOffset)*w:(t+s.cloneOffset)*w,s.setProps(h,"",s.vars.animationSpeed),s.transitions?(s.vars.animationLoop&&s.atEnd||(s.animating=!1,s.currentSlide=s.animatingTo),s.container.unbind("webkitTransitionEnd transitionend"),s.container.bind("webkitTransitionEnd transitionend",function(){clearTimeout(s.ensureAnimationEnd),s.wrapup(w)}),clearTimeout(s.ensureAnimationEnd),s.ensureAnimationEnd=setTimeout(function(){s.wrapup(w)},s.vars.animationSpeed+100)):s.container.animate(s.args,s.vars.animationSpeed,s.vars.easing,function(){s.wrapup(w)})}s.vars.smoothHeight&&g.smoothHeight(s.vars.animationSpeed)}},s.wrapup=function(t){f||m||(0===s.currentSlide&&s.animatingTo===s.last&&s.vars.animationLoop?s.setProps(t,"jumpEnd"):s.currentSlide===s.last&&0===s.animatingTo&&s.vars.animationLoop&&s.setProps(t,"jumpStart")),s.animating=!1,s.currentSlide=s.animatingTo,s.vars.after(s)},s.animateSlides=function(){!s.animating&&o&&s.flexAnimate(s.getTarget("next"))},s.pause=function(){clearInterval(s.animatedSlides),s.animatedSlides=null,s.playing=!1,s.vars.pausePlay&&g.pausePlay.update("play"),s.syncExists&&g.sync("pause")},s.play=function(){s.playing&&clearInterval(s.animatedSlides),s.animatedSlides=s.animatedSlides||setInterval(s.animateSlides,s.vars.slideshowSpeed),s.started=s.playing=!0,s.vars.pausePlay&&g.pausePlay.update("pause"),s.syncExists&&g.sync("play")},s.stop=function(){s.pause(),s.stopped=!0},s.canAdvance=function(t,e){var i=v?s.pagingCount-1:s.last;return!!e||(!(!v||s.currentItem!==s.count-1||0!==t||"prev"!==s.direction)||(!v||0!==s.currentItem||t!==s.pagingCount-1||"next"===s.direction)&&(!(t===s.currentSlide&&!v)&&(!!s.vars.animationLoop||(!s.atEnd||0!==s.currentSlide||t!==i||"next"===s.direction)&&(!s.atEnd||s.currentSlide!==i||0!==t||"next"!==s.direction))))},s.getTarget=function(t){return s.direction=t,"next"===t?s.currentSlide===s.last?0:s.currentSlide+1:0===s.currentSlide?s.last:s.currentSlide-1},s.setProps=function(t,e,i){var n=function(){var i=t?t:(s.itemW+s.vars.itemMargin)*s.move*s.animatingTo,n=function(){if(m)return"setTouch"===e?t:p&&s.animatingTo===s.last?0:p?s.limit-(s.itemW+s.vars.itemMargin)*s.move*s.animatingTo:s.animatingTo===s.last?s.limit:i;switch(e){case"setTotal":return p?(s.count-1-s.currentSlide+s.cloneOffset)*t:(s.currentSlide+s.cloneOffset)*t;case"setTouch":return p?t:t;case"jumpEnd":return p?t:s.count*t;case"jumpStart":return p?s.count*t:t;default:return t}}();return n*-1+"px"}();s.transitions&&(n=d?"translate3d(0,"+n+",0)":"translate3d("+n+",0,0)",i=void 0!==i?i/1e3+"s":"0s",s.container.css("-"+s.pfx+"-transition-duration",i),s.container.css("transition-duration",i)),s.args[s.prop]=n,(s.transitions||void 0===i)&&s.container.css(s.args),s.container.css("transform",n)},s.setup=function(t){if(f)s.slides.css({width:"100%","float":"left",marginRight:"-100%",position:"relative"}),"init"===t&&(c?s.slides.css({opacity:0,display:"block",webkitTransition:"opacity "+s.vars.animationSpeed/1e3+"s ease",zIndex:1}).eq(s.currentSlide).css({opacity:1,zIndex:2}):0==s.vars.fadeFirstSlide?s.slides.css({opacity:0,display:"block",zIndex:1}).eq(s.currentSlide).css({zIndex:2}).css({opacity:1}):s.slides.css({opacity:0,display:"block",zIndex:1}).eq(s.currentSlide).css({zIndex:2}).animate({opacity:1},s.vars.animationSpeed,s.vars.easing)),s.vars.smoothHeight&&g.smoothHeight();else{var e,i;"init"===t&&(s.viewport=n('
      ').css({overflow:"hidden",position:"relative"}).appendTo(s).append(s.container),s.cloneCount=0,s.cloneOffset=0,p&&(i=n.makeArray(s.slides).reverse(),s.slides=n(i),s.container.empty().append(s.slides))),s.vars.animationLoop&&!m&&(s.cloneCount=2,s.cloneOffset=1,"init"!==t&&s.container.find(".clone").remove(),s.container.append(g.uniqueID(s.slides.first().clone().addClass("clone")).attr("aria-hidden","true")).prepend(g.uniqueID(s.slides.last().clone().addClass("clone")).attr("aria-hidden","true"))),s.newSlides=n(s.vars.selector,s),e=p?s.count-1-s.currentSlide+s.cloneOffset:s.currentSlide+s.cloneOffset,d&&!m?(s.container.height(200*(s.count+s.cloneCount)+"%").css("position","absolute").width("100%"),setTimeout(function(){s.newSlides.css({display:"block"}),s.doMath(),s.viewport.height(s.h),s.setProps(e*s.h,"init")},"init"===t?100:0)):(s.container.width(200*(s.count+s.cloneCount)+"%"),s.setProps(e*s.computedW,"init"),setTimeout(function(){s.doMath(),s.newSlides.css({width:s.computedW,marginRight:s.computedM,"float":"left",display:"block"}),s.vars.smoothHeight&&g.smoothHeight()},"init"===t?100:0))}m||s.slides.removeClass(r+"active-slide").eq(s.currentSlide).addClass(r+"active-slide"),s.vars.init(s)},s.doMath=function(){var t=s.slides.first(),e=s.vars.itemMargin,i=s.vars.minItems,n=s.vars.maxItems;s.w=void 0===s.viewport?s.width():s.viewport.width(),s.h=t.height(),s.boxPadding=t.outerWidth()-t.width(),m?(s.itemT=s.vars.itemWidth+e,s.itemM=e,s.minW=i?i*s.itemT:s.w,s.maxW=n?n*s.itemT-e:s.w,s.itemW=s.minW>s.w?(s.w-e*(i-1))/i:s.maxWs.w?s.w:s.vars.itemWidth,s.visible=Math.floor(s.w/s.itemW),s.move=s.vars.move>0&&s.vars.moves.w?s.itemW*(s.count-1)+e*(s.count-1):(s.itemW+e)*s.count-s.w-e):(s.itemW=s.w,s.itemM=e,s.pagingCount=s.count,s.last=s.count-1),s.computedW=s.itemW-s.boxPadding,s.computedM=s.itemM},s.update=function(t,e){s.doMath(),m||(ts.controlNav.length?g.controlNav.update("add"):("remove"===e&&!m||s.pagingCounts.last&&(s.currentSlide-=1,s.animatingTo-=1),g.controlNav.update("remove",s.last))),s.vars.directionNav&&g.directionNav.update()},s.addSlide=function(t,e){var i=n(t);s.count+=1,s.last=s.count-1,d&&p?void 0!==e?s.slides.eq(s.count-e).after(i):s.container.prepend(i):void 0!==e?s.slides.eq(e).before(i):s.container.append(i),s.update(e,"add"),s.slides=n(s.vars.selector+":not(.clone)",s),s.setup(),s.vars.added(s)},s.removeSlide=function(t){var e=isNaN(t)?s.slides.index(n(t)):t;s.count-=1,s.last=s.count-1,isNaN(t)?n(t,s.slides).remove():d&&p?s.slides.eq(s.last).remove():s.slides.eq(t).remove(),s.doMath(),s.update(e,"remove"),s.slides=n(s.vars.selector+":not(.clone)",s),s.setup(),s.vars.removed(s)},g.init()},n(window).blur(function(t){o=!1}).focus(function(t){o=!0}),n.flexslider.defaults={namespace:"am-",selector:".am-slides > li",animation:"slide",easing:"swing",direction:"horizontal",reverse:!1,animationLoop:!0,smoothHeight:!1,startAt:0,slideshow:!0,slideshowSpeed:5e3,animationSpeed:600,initDelay:0,randomize:!1,fadeFirstSlide:!0,thumbCaptions:!1,pauseOnAction:!0,pauseOnHover:!1,pauseInvisible:!0,useCSS:!0,touch:!0,video:!1,controlNav:!0,directionNav:!0,prevText:" ",nextText:" ",keyboard:!0,multipleKeyboard:!1,mousewheel:!1,pausePlay:!1,pauseText:"Pause",playText:"Play",controlsContainer:"",manualControls:"",customDirectionNav:"",sync:"",asNavFor:"",itemWidth:0,itemMargin:0,minItems:1,maxItems:0,move:0,allowOneSlide:!0,start:function(){},before:function(){},after:function(){},end:function(){},added:function(){},removed:function(){},init:function(){}},n.fn.flexslider=function(t){var e=Array.prototype.slice.call(arguments,1);if(void 0===t&&(t={}),"object"==typeof t)return this.each(function(){var e=n(this),i=t.selector?t.selector:".am-slides > li",s=e.find(i);1===s.length&&t.allowOneSlide===!1||0===s.length?(s.fadeIn(400),t.start&&t.start(e)):void 0===e.data("flexslider")&&new n.flexslider(this,t)});var i,s=n(this).data("flexslider");switch(t){case"next":s.flexAnimate(s.getTarget("next"),!0);break;case"prev":case"previous":s.flexAnimate(s.getTarget("prev"),!0);break;default:"number"==typeof t?s.flexAnimate(t,!0):"string"==typeof t&&(i="function"==typeof s[t]?s[t].apply(s,e):s[t])}return void 0===i?this:i},s.ready(function(t){n("[data-am-flexslider]",t).each(function(t,e){var i=n(e),o=s.utils.parseOptions(i.data("amFlexslider"));o.before=function(t){t._pausedTimer&&(window.clearTimeout(t._pausedTimer),t._pausedTimer=null)},o.after=function(t){var e=t.vars.playAfterPaused;!e||isNaN(e)||t.playing||t.manualPause||t.manualPlay||t.stopped||(t._pausedTimer=window.setTimeout(function(){t.play()},e))},i.flexslider(o)})}),t.exports=n.flexslider}).call(e,i(12).setImmediate)},function(t,e,i){(function(t,n){function s(t,e){this._id=t,this._clearFn=e}var o=i(13).nextTick,a=Function.prototype.apply,r=Array.prototype.slice,l={},c=0;e.setTimeout=function(){return new s(a.call(setTimeout,window,arguments),clearTimeout)},e.setInterval=function(){return new s(a.call(setInterval,window,arguments),clearInterval)},e.clearTimeout=e.clearInterval=function(t){t.close()},s.prototype.unref=s.prototype.ref=function(){},s.prototype.close=function(){this._clearFn.call(window,this._id)},e.enroll=function(t,e){clearTimeout(t._idleTimeoutId),t._idleTimeout=e},e.unenroll=function(t){clearTimeout(t._idleTimeoutId),t._idleTimeout=-1},e._unrefActive=e.active=function(t){clearTimeout(t._idleTimeoutId);var e=t._idleTimeout;e>=0&&(t._idleTimeoutId=setTimeout(function(){t._onTimeout&&t._onTimeout()},e))},e.setImmediate="function"==typeof t?t:function(t){var i=c++,n=!(arguments.length<2)&&r.call(arguments,1);return l[i]=!0,o(function(){l[i]&&(n?t.apply(null,n):t.call(null),e.clearImmediate(i))}),i},e.clearImmediate="function"==typeof n?n:function(t){delete l[t]}}).call(e,i(12).setImmediate,i(12).clearImmediate)},function(t,e){function i(t){if(l===setTimeout)return setTimeout(t,0);try{return l(t,0)}catch(e){try{return l.call(null,t,0)}catch(e){return l.call(this,t,0)}}}function n(t){if(c===clearTimeout)return clearTimeout(t);try{return c(t)}catch(e){try{return c.call(null,t)}catch(e){return c.call(this,t)}}}function s(){p&&h&&(p=!1,h.length?d=h.concat(d):m=-1,d.length&&o())}function o(){if(!p){var t=i(s);p=!0;for(var e=d.length;e;){for(h=d,d=[];++m1)for(var n=1;n0&&(a=s?s/2.5*(c/8):0,l=Math.abs(t)+a,r=l/c),{destination:Math.round(a),duration:r}};var s=t("transform");return e.extend(e,{hasTransform:s!==!1,hasPerspective:t("perspective")in i,hasTouch:"ontouchstart"in window,hasPointer:!(!window.PointerEvent&&!window.MSPointerEvent),hasTransition:t("transition")in i}),e.isBadAndroid=function(){var t=window.navigator.appVersion;if(/Android/.test(t)&&!/Chrome\/\d/.test(t)){var e=t.match(/Safari\/(\d+.\d)/);return!(e&&"object"==typeof e&&e.length>=2)||parseFloat(e[1])<535.19}return!1}(),e.extend(e.style={},{transform:s,transitionTimingFunction:t("transitionTimingFunction"),transitionDuration:t("transitionDuration"),transitionDelay:t("transitionDelay"),transformOrigin:t("transformOrigin")}),e.hasClass=function(t,e){var i=new RegExp("(^|\\s)"+e+"(\\s|$)");return i.test(t.className)},e.addClass=function(t,i){if(!e.hasClass(t,i)){var n=t.className.split(" ");n.push(i),t.className=n.join(" ")}},e.removeClass=function(t,i){if(e.hasClass(t,i)){var n=new RegExp("(^|\\s)"+i+"(\\s|$)","g");t.className=t.className.replace(n," ")}},e.offset=function(t){for(var e=-t.offsetLeft,i=-t.offsetTop;t=t.offsetParent;)e-=t.offsetLeft,i-=t.offsetTop;return{left:e,top:i}},e.preventDefaultException=function(t,e){for(var i in e)if(e[i].test(t[i]))return!0;return!1},e.extend(e.eventType={},{touchstart:1,touchmove:1,touchend:1,mousedown:2,mousemove:2,mouseup:2,pointerdown:3,pointermove:3,pointerup:3,MSPointerDown:3,MSPointerMove:3,MSPointerUp:3}),e.extend(e.ease={},{quadratic:{style:"cubic-bezier(0.25, 0.46, 0.45, 0.94)",fn:function(t){return t*(2-t)}},circular:{style:"cubic-bezier(0.1, 0.57, 0.1, 1)",fn:function(t){return Math.sqrt(1- --t*t)}},back:{style:"cubic-bezier(0.175, 0.885, 0.32, 1.275)",fn:function(t){var e=4;return(t-=1)*t*((e+1)*t+e)+1}},bounce:{style:"",fn:function(t){return(t/=1)<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}},elastic:{style:"",fn:function(t){var e=.22,i=.4;return 0===t?0:1==t?1:i*Math.pow(2,-10*t)*Math.sin((t-e/4)*(2*Math.PI)/e)+1}}}),e.tap=function(t,e){var i=document.createEvent("Event");i.initEvent(e,!0,!0),i.pageX=t.pageX,i.pageY=t.pageY,t.target.dispatchEvent(i)},e.click=function(t){var e,i=t.target;/(SELECT|INPUT|TEXTAREA)/i.test(i.tagName)||(e=document.createEvent(window.MouseEvent?"MouseEvents":"Event"),e.initEvent("click",!0,!0),e.view=t.view||window,e.detail=1,e.screenX=i.screenX||0,e.screenY=i.screenY||0,e.clientX=i.clientX||0,e.clientY=i.clientY||0,e.ctrlKey=!!t.ctrlKey,e.altKey=!!t.altKey,e.shiftKey=!!t.shiftKey,e.metaKey=!!t.metaKey,e.button=0,e.relatedTarget=null,e._constructed=!0,i.dispatchEvent(e))},e}();n.prototype={version:"5.2.0",_init:function(){this._initEvents()},destroy:function(){this._initEvents(!0),clearTimeout(this.resizeTimeout),this.resizeTimeout=null,this._execEvent("destroy")},_transitionEnd:function(t){t.target==this.scroller&&this.isInTransition&&(this._transitionTime(),this.resetPosition(this.options.bounceTime)||(this.isInTransition=!1,this._execEvent("scrollEnd")))},_start:function(t){if(1!=a.eventType[t.type]){var e;if(e=t.which?t.button:t.button<2?0:4==t.button?1:2,0!==e)return}if(this.enabled&&(!this.initiated||a.eventType[t.type]===this.initiated)){!this.options.preventDefault||a.isBadAndroid||a.preventDefaultException(t.target,this.options.preventDefaultException)||t.preventDefault();var i,n=t.touches?t.touches[0]:t;this.initiated=a.eventType[t.type],this.moved=!1,this.distX=0,this.distY=0,this.directionX=0,this.directionY=0,this.directionLocked=0,this.startTime=a.getTime(),this.options.useTransition&&this.isInTransition?(this._transitionTime(),this.isInTransition=!1,i=this.getComputedPosition(),this._translate(Math.round(i.x),Math.round(i.y)),this._execEvent("scrollEnd")):!this.options.useTransition&&this.isAnimating&&(this.isAnimating=!1, +this._execEvent("scrollEnd")),this.startX=this.x,this.startY=this.y,this.absStartX=this.x,this.absStartY=this.y,this.pointX=n.pageX,this.pointY=n.pageY,this._execEvent("beforeScrollStart")}},_move:function(t){if(this.enabled&&a.eventType[t.type]===this.initiated){this.options.preventDefault&&t.preventDefault();var e,i,n,s,o=t.touches?t.touches[0]:t,r=o.pageX-this.pointX,l=o.pageY-this.pointY,c=a.getTime();if(this.pointX=o.pageX,this.pointY=o.pageY,this.distX+=r,this.distY+=l,n=Math.abs(this.distX),s=Math.abs(this.distY),!(c-this.endTime>300&&n<10&&s<10)){if(this.directionLocked||this.options.freeScroll||(n>s+this.options.directionLockThreshold?this.directionLocked="h":s>=n+this.options.directionLockThreshold?this.directionLocked="v":this.directionLocked="n"),"h"==this.directionLocked){if("vertical"==this.options.eventPassthrough)t.preventDefault();else if("horizontal"==this.options.eventPassthrough)return void(this.initiated=!1);l=0}else if("v"==this.directionLocked){if("horizontal"==this.options.eventPassthrough)t.preventDefault();else if("vertical"==this.options.eventPassthrough)return void(this.initiated=!1);r=0}r=this.hasHorizontalScroll?r:0,l=this.hasVerticalScroll?l:0,e=this.x+r,i=this.y+l,(e>0||e0?0:this.maxScrollX),(i>0||i0?0:this.maxScrollY),this.directionX=r>0?-1:r<0?1:0,this.directionY=l>0?-1:l<0?1:0,this.moved||this._execEvent("scrollStart"),this.moved=!0,this._translate(e,i),c-this.startTime>300&&(this.startTime=c,this.startX=this.x,this.startY=this.y)}}},_end:function(t){if(this.enabled&&a.eventType[t.type]===this.initiated){this.options.preventDefault&&!a.preventDefaultException(t.target,this.options.preventDefaultException)&&t.preventDefault();var e,i,n=(t.changedTouches?t.changedTouches[0]:t,a.getTime()-this.startTime),s=Math.round(this.x),o=Math.round(this.y),r=Math.abs(s-this.startX),l=Math.abs(o-this.startY),c=0,u="";if(this.isInTransition=0,this.initiated=0,this.endTime=a.getTime(),!this.resetPosition(this.options.bounceTime))return this.scrollTo(s,o),this.moved?this._events.flick&&n<200&&r<100&&l<100?void this._execEvent("flick"):(this.options.momentum&&n<300&&(e=this.hasHorizontalScroll?a.momentum(this.x,this.startX,n,this.maxScrollX,this.options.bounce?this.wrapperWidth:0,this.options.deceleration):{destination:s,duration:0},i=this.hasVerticalScroll?a.momentum(this.y,this.startY,n,this.maxScrollY,this.options.bounce?this.wrapperHeight:0,this.options.deceleration):{destination:o,duration:0},s=e.destination,o=i.destination,c=Math.max(e.duration,i.duration),this.isInTransition=1),s!=this.x||o!=this.y?((s>0||s0||o0?e=0:this.x0?i=0:this.y-1&&this._events[t].splice(i,1)}},_execEvent:function(t){if(this._events[t]){var e=0,i=this._events[t].length;if(i)for(;e0;var s=this.options.useTransition&&n.style;!i||s?(s&&(this._transitionTimingFunction(n.style),this._transitionTime(i)),this._translate(t,e)):this._animate(t,e,i,n.fn)},scrollToElement:function(t,e,i,n,s){if(t=t.nodeType?t:this.scroller.querySelector(t)){var o=a.offset(t);o.left-=this.wrapperOffset.left,o.top-=this.wrapperOffset.top,i===!0&&(i=Math.round(t.offsetWidth/2-this.wrapper.offsetWidth/2)),n===!0&&(n=Math.round(t.offsetHeight/2-this.wrapper.offsetHeight/2)),o.left-=i||0,o.top-=n||0,o.left=o.left>0?0:o.left0?0:o.top=h?(r.isAnimating=!1,r._translate(t,e),void(r.resetPosition(r.options.bounceTime)||r._execEvent("scrollEnd"))):(f=(f-u)/i,m=n(f),d=(t-l)*m+l,p=(e-c)*m+c,r._translate(d,p),void(r.isAnimating&&o(s)))}var r=this,l=this.x,c=this.y,u=a.getTime(),h=u+i;this.isAnimating=!0,s()},handleEvent:function(t){switch(t.type){case"touchstart":case"pointerdown":case"MSPointerDown":case"mousedown":this._start(t);break;case"touchmove":case"pointermove":case"MSPointerMove":case"mousemove":this._move(t);break;case"touchend":case"pointerup":case"MSPointerUp":case"mouseup":case"touchcancel":case"pointercancel":case"MSPointerCancel":case"mousecancel":this._end(t);break;case"orientationchange":case"resize":this._resize();break;case"transitionend":case"webkitTransitionEnd":case"oTransitionEnd":case"MSTransitionEnd":this._transitionEnd(t);break;case"wheel":case"DOMMouseScroll":case"mousewheel":this._wheel(t);break;case"keydown":this._key(t);break;case"click":this.enabled&&!t._constructed&&(t.preventDefault(),t.stopPropagation())}}},n.utils=a,t.exports=s.iScroll=n},function(t,e,i){"use strict";function n(t,e){return this.each(function(){var i=s(this),n=i.data("amui.modal"),o="object"==typeof t&&t;n||i.data("amui.modal",n=new c(this,o)),"string"==typeof t?n[t]&&n[t](e):n.toggle(t&&t.relatedTarget||void 0)})}var s=i(1),o=i(2),a=i(9),r=s(document),l=o.support.transition,c=function(t,e){this.options=s.extend({},c.DEFAULTS,e||{}),this.$element=s(t),this.$dialog=this.$element.find(".am-modal-dialog"),this.$element.attr("id")||this.$element.attr("id",o.utils.generateGUID("am-modal")),this.isPopup=this.$element.hasClass("am-popup"),this.isActions=this.$element.hasClass("am-modal-actions"),this.isPrompt=this.$element.hasClass("am-modal-prompt"),this.isLoading=this.$element.hasClass("am-modal-loading"),this.active=this.transitioning=this.relatedTarget=null,this.dimmer=this.options.dimmer?a:{open:function(){},close:function(){}},this.events()};c.DEFAULTS={className:{active:"am-modal-active",out:"am-modal-out"},selector:{modal:".am-modal",active:".am-modal-active"},closeViaDimmer:!0,cancelable:!0,onConfirm:function(){},onCancel:function(){},closeOnCancel:!0,closeOnConfirm:!0,dimmer:!0,height:void 0,width:void 0,duration:300,transitionEnd:l&&l.end+".modal.amui"},c.prototype.toggle=function(t){return this.active?this.close():this.open(t)},c.prototype.open=function(t){var e=this.$element,i=this.options,n=this.isPopup,o=i.width,a=i.height,r={};if(!this.active&&this.$element.length){t&&(this.relatedTarget=t),this.transitioning&&(clearTimeout(e.transitionEndTimmer),e.transitionEndTimmer=null,e.trigger(i.transitionEnd).off(i.transitionEnd)),n&&this.$element.show(),this.active=!0,e.trigger(s.Event("open.modal.amui",{relatedTarget:t})),this.dimmer.open(e),e.show().redraw(),n||this.isActions||(o&&(r.width=parseInt(o,10)+"px"),a&&(r.height=parseInt(a,10)+"px"),this.$dialog.css(r)),e.removeClass(i.className.out).addClass(i.className.active),this.transitioning=1;var c=function(){e.trigger(s.Event("opened.modal.amui",{relatedTarget:t})),this.transitioning=0,this.isPrompt&&this.$dialog.find("input").eq(0).focus()};return l?void e.one(i.transitionEnd,s.proxy(c,this)).emulateTransitionEnd(i.duration):c.call(this)}},c.prototype.close=function(t){if(this.active){var e=this.$element,i=this.options,n=this.isPopup;this.transitioning&&(clearTimeout(e.transitionEndTimmer),e.transitionEndTimmer=null,e.trigger(i.transitionEnd).off(i.transitionEnd),this.dimmer.close(e,!0)),this.$element.trigger(s.Event("close.modal.amui",{relatedTarget:t})),this.transitioning=1;var o=function(){e.trigger("closed.modal.amui"),n&&e.removeClass(i.className.out),e.hide(),this.transitioning=0,this.dimmer.close(e,!1),this.active=!1};return e.removeClass(i.className.active).addClass(i.className.out),l?void e.one(i.transitionEnd,s.proxy(o,this)).emulateTransitionEnd(i.duration):o.call(this)}},c.prototype.events=function(){var t=this,e=this.options,i=this.$element,n=this.dimmer.$element,o=i.find(".am-modal-prompt-input"),a=i.find("[data-am-modal-confirm]"),r=i.find("[data-am-modal-cancel]"),l=function(){var t=[];return o.each(function(){t.push(s(this).val())}),0===t.length?void 0:1===t.length?t[0]:t};this.options.cancelable&&i.on("keyup.modal.amui",function(e){t.active&&27===e.which&&(i.trigger("cancel.modal.amui"),t.close())}),this.options.dimmer&&this.options.closeViaDimmer&&!this.isLoading&&n.on("click.dimmer.modal.amui",function(){t.close()}),i.on("click.close.modal.amui","[data-am-modal-close], .am-modal-btn",function(i){i.preventDefault();var n=s(this);n.is(a)?e.closeOnConfirm&&t.close():n.is(r)?e.closeOnCancel&&t.close():t.close()}).on("click",function(t){s(t.target).is(i)&&n.trigger("click.dimmer.modal.amui")}),a.on("click.confirm.modal.amui",function(){i.trigger(s.Event("confirm.modal.amui",{trigger:this}))}),r.on("click.cancel.modal.amui",function(){i.trigger(s.Event("cancel.modal.amui",{trigger:this}))}),i.on("confirm.modal.amui",function(e){e.data=l(),t.options.onConfirm.call(t,e)}).on("cancel.modal.amui",function(e){e.data=l(),t.options.onCancel.call(t,e)})},s.fn.modal=n,r.on("click.modal.amui.data-api","[data-am-modal]",function(){var t=s(this),e=o.utils.parseOptions(t.attr("data-am-modal")),i=s(e.target||this.href&&this.href.replace(/.*(?=#[^\s]+$)/,"")),a=i.data("amui.modal")?"toggle":e;n.call(i,a,this)}),t.exports=o.modal=c},function(t,e,i){"use strict";function n(t,e){var i=Array.prototype.slice.call(arguments,1);return this.each(function(){var n=s(this),o=n.data("amui.offcanvas"),a=s.extend({},"object"==typeof t&&t);o||(n.data("amui.offcanvas",o=new c(this,a)),(!t||"object"==typeof t)&&o.open(e)),"string"==typeof t&&o[t]&&o[t].apply(o,i)})}var s=i(1),o=i(2);i(3);var a,r=s(window),l=s(document),c=function(t,e){this.$element=s(t),this.options=s.extend({},c.DEFAULTS,e),this.active=null,this.bindEvents()};c.DEFAULTS={duration:300,effect:"overlay"},c.prototype.open=function(t){var e=this,i=this.$element;if(i.length&&!i.hasClass("am-active")){var n=this.options.effect,o=s("html"),l=s("body"),c=i.find(".am-offcanvas-bar").first(),u=c.hasClass("am-offcanvas-bar-flip")?-1:1;c.addClass("am-offcanvas-bar-"+n),a={x:window.scrollX,y:window.scrollY},i.addClass("am-active"),l.css({width:window.innerWidth,height:r.height()}).addClass("am-offcanvas-page"),"overlay"!==n&&l.css({"margin-left":c.outerWidth()*u}).width(),o.css("margin-top",a.y*-1),setTimeout(function(){c.addClass("am-offcanvas-bar-active").width()},0),i.trigger("open.offcanvas.amui"),this.active=1,i.on("click.offcanvas.amui",function(t){var i=s(t.target);i.hasClass("am-offcanvas-bar")||i.parents(".am-offcanvas-bar").first().length||(t.stopImmediatePropagation(),e.close())}),o.on("keydown.offcanvas.amui",function(t){27===t.keyCode&&e.close()})}},c.prototype.close=function(t){function e(){r.removeClass("am-offcanvas-page").css({width:"",height:"","margin-left":"","margin-right":""}),l.removeClass("am-active"),c.removeClass("am-offcanvas-bar-active"),n.css("margin-top",""),window.scrollTo(a.x,a.y),l.trigger("closed.offcanvas.amui"),i.active=0}var i=this,n=s("html"),r=s("body"),l=this.$element,c=l.find(".am-offcanvas-bar").first();l.length&&this.active&&l.hasClass("am-active")&&(l.trigger("close.offcanvas.amui"),o.support.transition?(setTimeout(function(){c.removeClass("am-offcanvas-bar-active")},0),r.css("margin-left","").one(o.support.transition.end,function(){e()}).emulateTransitionEnd(this.options.duration)):e(),l.off("click.offcanvas.amui"),n.off(".offcanvas.amui"))},c.prototype.bindEvents=function(){var t=this;return l.on("click.offcanvas.amui",'[data-am-dismiss="offcanvas"]',function(e){e.preventDefault(),t.close()}),r.on("resize.offcanvas.amui orientationchange.offcanvas.amui",function(){t.active&&t.close()}),this.$element.hammer().on("swipeleft swipeleft",function(e){e.preventDefault(),t.close()}),this},s.fn.offCanvas=n,l.on("click.offcanvas.amui","[data-am-offcanvas]",function(t){t.preventDefault();var e=s(this),i=o.utils.parseOptions(e.data("amOffcanvas")),a=s(i.target||this.href&&this.href.replace(/.*(?=#[^\s]+$)/,"")),r=a.data("amui.offcanvas")?"open":i;n.call(a,r,this)}),t.exports=o.offcanvas=c},function(t,e,i){"use strict";var n=i(1),s=i(2),o=s.utils.rAF,a=function(t){var e=function(e,i){this.el=t(e),this.zoomFactor=1,this.lastScale=1,this.offset={x:0,y:0},this.options=t.extend({},this.defaults,i),this.setupMarkup(),this.bindEvents(),this.update(),this.enable()},i=function(t,e){return t+e},n=function(t,e){return t>e-.01&&t1){var e=this.getTouches(t)[0];this.drag(e,this.lastDragPosition),this.offset=this.sanitizeOffset(this.offset),this.lastDragPosition=e}},handleDragEnd:function(){this.el.trigger(this.options.dragEndEventName),this.end()},handleZoomStart:function(t){this.el.trigger(this.options.zoomStartEventName),this.stopAnimation(),this.lastScale=1,this.nthZoom=0,this.lastZoomCenter=!1,this.hasInteraction=!0},handleZoom:function(t,e){var i=this.getTouchCenter(this.getTouches(t)),n=e/this.lastScale;this.lastScale=e,this.nthZoom+=1,this.nthZoom>3&&(this.scale(n,i),this.drag(i,this.lastZoomCenter)),this.lastZoomCenter=i},handleZoomEnd:function(){this.el.trigger(this.options.zoomEndEventName),this.end()},handleDoubleTap:function(t){var e=this.getTouches(t)[0],i=this.zoomFactor>1?1:this.options.tapZoomFactor,n=this.zoomFactor,s=function(t){this.scaleTo(n+t*(i-n),e)}.bind(this);this.hasInteraction||(n>i&&(e=this.getCurrentZoomCenter()),this.animate(this.options.animationDuration,s,this.swing),this.el.trigger(this.options.doubleTapEventName))},sanitizeOffset:function(t){var e=(this.zoomFactor-1)*this.getContainerX(),i=(this.zoomFactor-1)*this.getContainerY(),n=Math.max(e,0),s=Math.max(i,0),o=Math.min(e,0),a=Math.min(i,0);return{x:Math.min(Math.max(t.x,o),n),y:Math.min(Math.max(t.y,a),s)}},scaleTo:function(t,e){this.scale(t/this.zoomFactor,e)},scale:function(t,e){t=this.scaleZoomFactor(t),this.addOffset({x:(t-1)*(e.x+this.offset.x),y:(t-1)*(e.y+this.offset.y)})},scaleZoomFactor:function(t){var e=this.zoomFactor;return this.zoomFactor*=t,this.zoomFactor=Math.min(this.options.maxZoom,Math.max(this.zoomFactor,this.options.minZoom)),this.zoomFactor/e},drag:function(t,e){e&&(this.options.lockDragAxis?Math.abs(t.x-e.x)>Math.abs(t.y-e.y)?this.addOffset({x:-(t.x-e.x),y:0}):this.addOffset({y:-(t.y-e.y),x:0}):this.addOffset({y:-(t.y-e.y),x:-(t.x-e.x)}))},getTouchCenter:function(t){return this.getVectorAvg(t)},getVectorAvg:function(t){return{x:t.map(function(t){return t.x}).reduce(i)/t.length,y:t.map(function(t){return t.y}).reduce(i)/t.length}},addOffset:function(t){this.offset={x:this.offset.x+t.x,y:this.offset.y+t.y}},sanitize:function(){this.zoomFactor=t?(e(1),n&&n(),this.update(),this.stopAnimation(),this.update()):(i&&(l=i(l)),e(l),this.update(),o(a))}}.bind(this);this.inAnimation=!0,o(a)},stopAnimation:function(){this.inAnimation=!1},swing:function(t){return-Math.cos(t*Math.PI)/2+.5},getContainerX:function(){return this.container[0].offsetWidth},getContainerY:function(){return this.container[0].offsetHeight},setContainerY:function(t){return this.container.height(t)},setupMarkup:function(){this.container=t('
      '),this.el.before(this.container),this.container.append(this.el),this.container.css({overflow:"hidden",position:"relative"}),this.el.css({"-webkit-transform-origin":"0% 0%","-moz-transform-origin":"0% 0%","-ms-transform-origin":"0% 0%","-o-transform-origin":"0% 0%","transform-origin":"0% 0%",position:"absolute"})},end:function(){this.hasInteraction=!1,this.sanitize(),this.update()},bindEvents:function(){s(this.container.get(0),this),t(window).on("resize",this.update.bind(this)),t(this.el).find("img").on("load",this.update.bind(this))},update:function(){this.updatePlaned||(this.updatePlaned=!0,setTimeout(function(){this.updatePlaned=!1,this.updateAspectRatio();var t=this.getInitialZoomFactor()*this.zoomFactor,e=-this.offset.x/t,i=-this.offset.y/t,n="scale3d("+t+", "+t+",1) translate3d("+e+"px,"+i+"px,0px)",s="scale("+t+", "+t+") translate("+e+"px,"+i+"px)",o=function(){this.clone&&(this.clone.remove(),delete this.clone)}.bind(this);!this.options.use2d||this.hasInteraction||this.inAnimation?(this.is3d=!0,o(),this.el.css({"-webkit-transform":n,"-o-transform":s,"-ms-transform":s,"-moz-transform":s,transform:n})):(this.is3d&&(this.clone=this.el.clone(),this.clone.css("pointer-events","none"),this.clone.appendTo(this.container),setTimeout(o,200)),this.el.css({"-webkit-transform":s,"-o-transform":s,"-ms-transform":s,"-moz-transform":s,transform:s}),this.is3d=!1)}.bind(this),0))},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1}};var s=function(t,e){var i=null,n=0,s=null,o=null,a=function(t,n){if(i!==t){if(i&&!t)switch(i){case"zoom":e.handleZoomEnd(n);break;case"drag":e.handleDragEnd(n)}switch(t){case"zoom":e.handleZoomStart(n);break;case"drag":e.handleDragStart(n)}}i=t},r=function(t){2===n?a("zoom"):1===n&&e.canDrag()?a("drag",t):a(null,t)},l=function(t){return Array.prototype.slice.call(t).map(function(t){return{x:t.pageX,y:t.pageY}})},c=function(t,e){var i,n;return i=t.x-e.x,n=t.y-e.y,Math.sqrt(i*i+n*n)},u=function(t,e){var i=c(t[0],t[1]),n=c(e[0],e[1]);return n/i},h=function(t){t.stopPropagation(),t.preventDefault()},d=function(t){var o=(new Date).getTime();if(n>1&&(s=null),o-s<300)switch(h(t),e.handleDoubleTap(t),i){case"zoom":e.handleZoomEnd(t);break;case"drag":e.handleDragEnd(t)}1===n&&(s=o)},p=!0;t.addEventListener("touchstart",function(t){e.enabled&&(p=!0,n=t.touches.length,d(t))}),t.addEventListener("touchmove",function(t){if(e.enabled){if(p)r(t),i&&h(t),o=l(t.touches);else{switch(i){case"zoom":e.handleZoom(t,u(o,l(t.touches)));break;case"drag":e.handleDrag(t)}i&&(h(t),e.update())}p=!1}}),t.addEventListener("touchend",function(t){e.enabled&&(n=t.touches.length,r(t))})};return e};t.exports=s.pichzoom=a(n)},function(t,e,i){"use strict";var n=i(1),s=i(2),o=n(window),a=function(t,e){this.options=n.extend({},a.DEFAULTS,e),this.$element=n(t),this.active=null,this.$popover=this.options.target&&n(this.options.target)||null,this.init(),this._bindEvents()};a.DEFAULTS={theme:null,trigger:"click",content:"",open:!1,target:null,tpl:'
      '},a.prototype.init=function(){function t(){i.sizePopover()}var e,i=this,o=this.$element;this.options.target||(this.$popover=this.getPopover(),this.setContent()),e=this.$popover,e.appendTo(n("body")),this.sizePopover(),o.on("open.popover.amui",function(){n(window).on("resize.popover.amui",s.utils.debounce(t,50))}),o.on("close.popover.amui",function(){n(window).off("resize.popover.amui",t)}),this.options.open&&this.open()},a.prototype.sizePopover=function(){var t=this.$element,e=this.$popover;if(e&&e.length){var i=e.outerWidth(),n=e.outerHeight(),s=e.find(".am-popover-caret"),a=s.outerWidth()/2||8,r=n+8,l=t.outerWidth(),c=t.outerHeight(),u=t.offset(),h=t[0].getBoundingClientRect(),d=o.height(),p=o.width(),m=0,f=0,v=0,g=2,y="top";e.css({left:"",top:""}).removeClass("am-popover-left am-popover-right am-popover-top am-popover-bottom"),r-gp&&(f=p-i-20),"top"===y&&e.addClass("am-popover-top"),"bottom"===y&&e.addClass("am-popover-bottom"),v-=f):"middle"===y&&(f=u.left-i-a,e.addClass("am-popover-left"),f<5&&(f=u.left+l+a,e.removeClass("am-popover-left").addClass("am-popover-right")),f+i>p&&(f=p-i-5,e.removeClass("am-popover-left").addClass("am-popover-right"))),e.css({top:m+"px",left:f+"px"})}},a.prototype.toggle=function(){return this[this.active?"close":"open"]()},a.prototype.open=function(){var t=this.$popover;this.$element.trigger("open.popover.amui"),this.sizePopover(),t.show().addClass("am-active"),this.active=!0},a.prototype.close=function(){var t=this.$popover;this.$element.trigger("close.popover.amui"),t.removeClass("am-active").trigger("closed.popover.amui").hide(),this.active=!1},a.prototype.getPopover=function(){var t=s.utils.generateGUID("am-popover"),e=[];return this.options.theme&&n.each(this.options.theme.split(" "),function(t,i){e.push("am-popover-"+n.trim(i))}),n(this.options.tpl).attr("id",t).addClass(e.join(" "))},a.prototype.setContent=function(t){t=t||this.options.content,this.$popover&&this.$popover.find(".am-popover-inner").empty().html(t)},a.prototype._bindEvents=function(){for(var t="popover.amui",e=this.options.trigger.split(" "),i=e.length;i--;){var s=e[i];if("click"===s)this.$element.on("click."+t,n.proxy(this.toggle,this));else{var o="hover"==s?"mouseenter":"focusin",a="hover"==s?"mouseleave":"focusout";this.$element.on(o+"."+t,n.proxy(this.open,this)),this.$element.on(a+"."+t,n.proxy(this.close,this))}}},a.prototype.destroy=function(){this.$element.off(".popover.amui").removeData("amui.popover"),this.$popover.remove()},s.plugin("popover",a),s.ready(function(t){n("[data-am-popover]",t).popover()}),t.exports=a},function(t,e,i){"use strict";var n=i(2),s=function(){function t(t,e,i){return ti?i:t}function e(t){return 100*(-1+t)}function i(t,i,n){var s;return s="translate3d"===c.positionUsing?{transform:"translate3d("+e(t)+"%,0,0)"}:"translate"===c.positionUsing?{transform:"translate("+e(t)+"%,0)"}:{"margin-left":e(t)+"%"},s.transition="all "+i+"ms "+n,s}function n(t,e){var i="string"==typeof t?t:a(t);return i.indexOf(" "+e+" ")>=0}function s(t,e){var i=a(t),s=i+e;n(i,e)||(t.className=s.substring(1))}function o(t,e){var i,s=a(t);n(t,e)&&(i=s.replace(" "+e+" "," "),t.className=i.substring(1,i.length-1))}function a(t){return(" "+(t.className||"")+" ").replace(/\s+/gi," ")}function r(t){t&&t.parentNode&&t.parentNode.removeChild(t)}var l={};l.version="0.2.0";var c=l.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,parent:"body",barSelector:'[role="nprogress-bar"]',spinnerSelector:'[role="nprogress-spinner"]',template:'
      '};l.configure=function(t){var e,i;for(e in t)i=t[e],void 0!==i&&t.hasOwnProperty(e)&&(c[e]=i);return this},l.status=null,l.set=function(e){var n=l.isStarted();e=t(e,c.minimum,1),l.status=1===e?null:e;var s=l.render(!n),o=s.querySelector(c.barSelector),a=c.speed,r=c.easing;return s.offsetWidth,u(function(t){""===c.positionUsing&&(c.positionUsing=l.getPositioningCSS()),h(o,i(e,a,r)),1===e?(h(s,{transition:"none",opacity:1}),s.offsetWidth,setTimeout(function(){h(s,{transition:"all "+a+"ms linear",opacity:0}),setTimeout(function(){l.remove(),t()},a)},a)):setTimeout(t,a)}),this},l.isStarted=function(){return"number"==typeof l.status},l.start=function(){l.status||l.set(0);var t=function(){setTimeout(function(){l.status&&(l.trickle(),t())},c.trickleSpeed)};return c.trickle&&t(),this},l.done=function(t){return t||l.status?l.inc(.3+.5*Math.random()).set(1):this},l.inc=function(e){var i=l.status;return i?("number"!=typeof e&&(e=(1-i)*t(Math.random()*i,.1,.95)),i=t(i+e,0,.994),l.set(i)):l.start()},l.trickle=function(){return l.inc(Math.random()*c.trickleRate)},function(){var t=0,e=0;l.promise=function(i){return i&&"resolved"!==i.state()?(0===e&&l.start(),t++,e++,i.always(function(){e--,0===e?(t=0,l.done()):l.set((t-e)/t)}),this):this}}(),l.render=function(t){if(l.isRendered())return document.getElementById("nprogress");s(document.documentElement,"nprogress-busy");var i=document.createElement("div");i.id="nprogress",i.innerHTML=c.template;var n,o=i.querySelector(c.barSelector),a=t?"-100":e(l.status||0),u=document.querySelector(c.parent);return h(o,{transition:"all 0 linear",transform:"translate3d("+a+"%,0,0)"}),c.showSpinner||(n=i.querySelector(c.spinnerSelector),n&&r(n)),u!=document.body&&s(u,"nprogress-custom-parent"),u.appendChild(i),i},l.remove=function(){o(document.documentElement,"nprogress-busy"),o(document.querySelector(c.parent),"nprogress-custom-parent");var t=document.getElementById("nprogress");t&&r(t)},l.isRendered=function(){return!!document.getElementById("nprogress")},l.getPositioningCSS=function(){var t=document.body.style,e="WebkitTransform"in t?"Webkit":"MozTransform"in t?"Moz":"msTransform"in t?"ms":"OTransform"in t?"O":"";return e+"Perspective"in t?"translate3d":e+"Transform"in t?"translate":"margin"};var u=function(){function t(){var i=e.shift();i&&i(t)}var e=[];return function(i){e.push(i),1==e.length&&t()}}(),h=function(){function t(t){return t.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(t,e){return e.toUpperCase()})}function e(t){var e=document.body.style;if(t in e)return t;for(var i,n=s.length,o=t.charAt(0).toUpperCase()+t.slice(1);n--;)if(i=s[n]+o,i in e)return i;return t}function i(i){return i=t(i),o[i]||(o[i]=e(i))}function n(t,e,n){e=i(e),t.style[e]=n}var s=["Webkit","O","Moz","ms"],o={};return function(t,e){var i,s,o=arguments;if(2==o.length)for(i in e)s=e[i],void 0!==s&&e.hasOwnProperty(i)&&n(t,i,s);else n(t,o[1],o[2])}}();return l}();t.exports=n.progress=s},function(t,e,i){"use strict";var n=i(1),s=i(2),o=i(17),a=i(3),r=s.support.animation,l=s.support.transition,c=function(t,e){this.$element=n(t),this.$body=n(document.body),this.options=n.extend({},c.DEFAULTS,e),this.$pureview=n(this.options.tpl).attr("id",s.utils.generateGUID("am-pureview")),this.$slides=null,this.transitioning=null,this.scrollbarWidth=0,this.init()};c.DEFAULTS={tpl:'
          /
          ',className:{prevSlide:"am-pureview-slide-prev",nextSlide:"am-pureview-slide-next",onlyOne:"am-pureview-only",active:"am-active",barActive:"am-pureview-bar-active",activeBody:"am-pureview-active"},selector:{slider:".am-pureview-slider",close:'[data-am-close="pureview"]',total:".am-pureview-total",current:".am-pureview-current",title:".am-pureview-title",actions:".am-pureview-actions", +bar:".am-pureview-bar",pinchZoom:".am-pinch-zoom",nav:".am-pureview-nav"},shareBtn:!1,toggleToolbar:!0,target:"img",weChatImagePreview:!0},c.prototype.init=function(){var t=this,e=this.options,i=this.$element,s=this.$pureview;this.refreshSlides(),n("body").append(s),this.$title=s.find(e.selector.title),this.$current=s.find(e.selector.current),this.$bar=s.find(e.selector.bar),this.$actions=s.find(e.selector.actions),e.shareBtn&&this.$actions.append(''),this.$element.on("click.pureview.amui",e.target,function(i){i.preventDefault();var n=t.$images.index(this);e.weChatImagePreview&&window.WeixinJSBridge?window.WeixinJSBridge.invoke("imagePreview",{current:t.imgUrls[n],urls:t.imgUrls}):t.open(n)}),s.find(".am-pureview-direction").on("click.direction.pureview.amui","li",function(e){e.preventDefault(),n(this).is(".am-pureview-prev")?t.prevSlide():t.nextSlide()}),s.find(e.selector.nav).on("click.nav.pureview.amui","li",function(){var e=t.$navItems.index(n(this));t.activate(t.$slides.eq(e))}),s.find(e.selector.close).on("click.close.pureview.amui",function(e){e.preventDefault(),t.close()}),this.$slider.hammer().on("swipeleft.pureview.amui",function(e){e.preventDefault(),t.nextSlide()}).on("swiperight.pureview.amui",function(e){e.preventDefault(),t.prevSlide()}).on("press.pureview.amui",function(i){i.preventDefault(),e.toggleToolbar&&t.toggleToolBar()}),this.$slider.data("hammer").get("swipe").set({direction:a.DIRECTION_HORIZONTAL,velocity:.35}),i.DOMObserve({childList:!0,subtree:!0},function(t,e){}),i.on("changed.dom.amui",function(e){e.stopPropagation(),t.refreshSlides()}),n(document).on("keydown.pureview.amui",n.proxy(function(t){var e=t.keyCode;37==e?this.prevSlide():39==e?this.nextSlide():27==e&&this.close()},this))},c.prototype.refreshSlides=function(){this.$images=this.$element.find(this.options.target);var t=this,e=this.options,i=this.$pureview,o=n([]),a=n([]),r=this.$images,l=r.length;this.$slider=i.find(e.selector.slider),this.$nav=i.find(e.selector.nav);var c="data-am-pureviewed";this.imgUrls=this.imgUrls||[],l&&(1===l&&i.addClass(e.className.onlyOne),r.not("["+c+"]").each(function(e,i){var r,l;"A"===i.nodeName?(r=i.href,l=i.title||""):(r=n(i).data("rel")||i.src,r=s.utils.getAbsoluteUrl(r),l=n(i).attr("alt")||""),i.setAttribute(c,"1"),t.imgUrls.push(r),o=o.add(n('
        1. ')),a=a.add(n("
        2. "+(e+1)+"
        3. "))}),i.find(e.selector.total).text(l),this.$slider.append(o),this.$nav.append(a),this.$navItems=this.$nav.find("li"),this.$slides=this.$slider.find("li"))},c.prototype.loadImage=function(t,e){var i="image-appended";if(!t.data(i)){var s=n("",{src:t.data("src"),alt:t.data("title")});t.html(s).wrapInner('
          ').redraw();var a=t.find(this.options.selector.pinchZoom);a.data("amui.pinchzoom",new o(a[0],{})),t.data("image-appended",!0)}e&&e.call(this)},c.prototype.activate=function(t){var e=this.options,i=this.$slides,o=i.index(t),a=t.data("title")||"",r=e.className.active;i.find("."+r).is(t)||this.transitioning||(this.loadImage(t,function(){s.utils.imageLoader(t.find("img"),function(e){t.find(".am-pinch-zoom").addClass("am-pureview-loaded"),n(e).addClass("am-img-loaded")})}),this.transitioning=1,this.$title.text(a),this.$current.text(o+1),i.removeClass(),t.addClass(r),i.eq(o-1).addClass(e.className.prevSlide),i.eq(o+1).addClass(e.className.nextSlide),this.$navItems.removeClass().eq(o).addClass(e.className.active),l?t.one(l.end,n.proxy(function(){this.transitioning=0},this)).emulateTransitionEnd(300):this.transitioning=0)},c.prototype.nextSlide=function(){if(1!==this.$slides.length){var t=this.$slides,e=t.filter(".am-active"),i=t.index(e),n="am-animation-right-spring";i+1>=t.length?r&&e.addClass(n).on(r.end,function(){e.removeClass(n)}):this.activate(t.eq(i+1))}},c.prototype.prevSlide=function(){if(1!==this.$slides.length){var t=this.$slides,e=t.filter(".am-active"),i=this.$slides.index(e),n="am-animation-left-spring";0===i?r&&e.addClass(n).on(r.end,function(){e.removeClass(n)}):this.activate(t.eq(i-1))}},c.prototype.toggleToolBar=function(){this.$pureview.toggleClass(this.options.className.barActive)},c.prototype.open=function(t){var e=t||0;this.checkScrollbar(),this.setScrollbar(),this.activate(this.$slides.eq(e)),this.$pureview.show().redraw().addClass(this.options.className.active),this.$body.addClass(this.options.className.activeBody)},c.prototype.close=function(){function t(){this.$pureview.hide(),this.$body.removeClass(e.className.activeBody),this.resetScrollbar()}var e=this.options;this.$pureview.removeClass(e.className.active),this.$slides.removeClass(),l?this.$pureview.one(l.end,n.proxy(t,this)).emulateTransitionEnd(300):t.call(this)},c.prototype.checkScrollbar=function(){this.scrollbarWidth=s.utils.measureScrollbar()},c.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.scrollbarWidth&&this.$body.css("padding-right",t+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},s.plugin("pureview",c),s.ready(function(t){n("[data-am-pureview]",t).pureview()}),t.exports=c},function(t,e,i){"use strict";var n=i(1),s=i(2),o=function(t,e){if(s.support.animation){this.options=n.extend({},o.DEFAULTS,e),this.$element=n(t);var i=function(){s.utils.rAF.call(window,n.proxy(this.checkView,this))}.bind(this);this.$window=n(window).on("scroll.scrollspy.amui",i).on("resize.scrollspy.amui orientationchange.scrollspy.amui",s.utils.debounce(i,50)),this.timer=this.inViewState=this.initInView=null,i()}};o.DEFAULTS={animation:"fade",className:{inView:"am-scrollspy-inview",init:"am-scrollspy-init"},repeat:!0,delay:0,topOffset:0,leftOffset:0},o.prototype.checkView=function(){var t=this.$element,e=this.options,i=s.utils.isInView(t,e),n=e.animation?" am-animation-"+e.animation:"";i&&!this.inViewState&&(this.timer&&clearTimeout(this.timer),this.initInView||(t.addClass(e.className.init),this.offset=t.offset(),this.initInView=!0,t.trigger("init.scrollspy.amui")),this.timer=setTimeout(function(){i&&t.addClass(e.className.inView+n).width()},e.delay),this.inViewState=!0,t.trigger("inview.scrollspy.amui")),!i&&this.inViewState&&e.repeat&&(t.removeClass(e.className.inView+n),this.inViewState=!1,t.trigger("outview.scrollspy.amui"))},o.prototype.check=function(){s.utils.rAF.call(window,n.proxy(this.checkView,this))},s.plugin("scrollspy",o),s.ready(function(t){n("[data-am-scrollspy]",t).scrollspy()}),t.exports=o},function(t,e,i){"use strict";var n=i(1),s=i(2);i(23);var o=function(t,e){this.options=n.extend({},o.DEFAULTS,e),this.$element=n(t),this.anchors=[],this.$links=this.$element.find('a[href^="#"]').each(function(t,e){this.anchors.push(n(e).attr("href"))}.bind(this)),this.$targets=n(this.anchors.join(", "));var i=function(){s.utils.rAF.call(window,n.proxy(this.process,this))}.bind(this);this.$window=n(window).on("scroll.scrollspynav.amui",i).on("resize.scrollspynav.amui orientationchange.scrollspynav.amui",s.utils.debounce(i,50)),i(),this.scrollProcess()};o.DEFAULTS={className:{active:"am-active"},closest:!1,smooth:!0,offsetTop:0},o.prototype.process=function(){var t=this.$window.scrollTop(),e=this.options,i=[],o=this.$links,a=this.$targets;if(a.each(function(t,n){s.utils.isInView(n,e)&&i.push(n)}),i.length){var r;if(n.each(i,function(e,i){if(n(i).offset().top>=t)return r=n(i),!1}),!r)return;e.closest?(o.closest(e.closest).removeClass(e.className.active),o.filter('a[href="#'+r.attr("id")+'"]').closest(e.closest).addClass(e.className.active)):o.removeClass(e.className.active).filter('a[href="#'+r.attr("id")+'"]').addClass(e.className.active)}},o.prototype.scrollProcess=function(){var t=this.$links,e=this.options;e.smooth&&n.fn.smoothScroll&&t.on("click",function(t){t.preventDefault();var i=n(this),s=n(i.attr("href"));if(s){var o=e.offsetTop&&!isNaN(parseInt(e.offsetTop))&&parseInt(e.offsetTop)||0;n(window).smoothScroll({position:s.offset().top-o})}})},s.plugin("scrollspynav",o),s.ready(function(t){n("[data-am-scrollspynav]",t).scrollspynav()}),t.exports=o},function(t,e,i){"use strict";var n=i(1),s=i(2),o=s.utils.rAF,a=s.utils.cancelAF,r=!1,l=function(t,e){function i(t){return(t/=.5)<1?.5*Math.pow(t,5):.5*(Math.pow(t-2,5)+2)}function s(){p.off("touchstart.smoothscroll.amui",w),r=!1}function c(t){r&&(u||(u=t),h=Math.min(1,Math.max((t-u)/y,0)),d=Math.round(f+g*i(h)),g>0&&d>m&&(d=m),g<0&&d=0};var o=function(t,e){this.$element=n(t),this.options=n.extend({},o.DEFAULTS,{placeholder:t.getAttribute("placeholder")||o.DEFAULTS.placeholder},e),this.$originalOptions=this.$element.find("option"),this.multiple=t.multiple,this.$selector=null,this.initialized=!1,this.init()};o.DEFAULTS={btnWidth:null,btnSize:null,btnStyle:"default",dropUp:0,maxHeight:null,maxChecked:null,placeholder:"\u70b9\u51fb\u9009\u62e9...",selectedClass:"am-checked",disabledClass:"am-disabled",searchBox:!1,tpl:'

          \u8fd4\u56de

          <% if (searchBox) { %> <% } %>
            <% for (var i = 0; i < options.length; i++) { %> <% var option = options[i] %> <% if (option.header) { %>
          • <%= option.text %>
          • <% } else { %>
          • <%= option.text %>
          • <% } %> <% } %>
          ',listTpl:'<% for (var i = 0; i < options.length; i++) { %> <% var option = options[i] %> <% if (option.header) { %>
        4. <%= option.text %>
        5. <% } else { %>
        6. <%= option.text %>
        7. <% } %> <% } %>'},o.prototype.init=function(){var t=this,e=this.$element,i=this.options;e.hide();var o={id:s.utils.generateGUID("am-selected"),multiple:this.multiple,options:[],searchBox:i.searchBox,dropUp:i.dropUp,placeholder:i.placeholder};this.$selector=n(s.template(this.options.tpl,o)),this.$selector.css({width:this.options.btnWidth}),this.$list=this.$selector.find(".am-selected-list"),this.$searchField=this.$selector.find(".am-selected-search input"),this.$hint=this.$selector.find(".am-selected-hint");var a=this.$selector.find(".am-selected-btn"),r=[];i.btnSize&&r.push("am-btn-"+i.btnSize),i.btnStyle&&r.push("am-btn-"+i.btnStyle),a.addClass(r.join(" ")),this.$selector.dropdown({justify:a}),e[0].disabled&&this.disable(),i.maxHeight&&this.$selector.find(".am-selected-list").css({"max-height":i.maxHeight,"overflow-y":"scroll"});var l=[],c=e.attr("minchecked"),u=e.attr("maxchecked")||i.maxChecked;this.maxChecked=u||1/0,e[0].required&&l.push("\u5fc5\u9009"),(c||u)&&(c&&l.push("\u81f3\u5c11\u9009\u62e9 "+c+" \u9879"),u&&l.push("\u81f3\u591a\u9009\u62e9 "+u+" \u9879")),this.$hint.text(l.join("\uff0c")),this.renderOptions(),this.$element.after(this.$selector),this.dropdown=this.$selector.data("amui.dropdown"),this.$status=this.$selector.find(".am-selected-status"),setTimeout(function(){t.syncData(),t.initialized=!0},0),this.bindEvents()},o.prototype.renderOptions=function(){function t(t,e,s){if(""===e.value)return!0;var o="";e.disabled&&(o+=i.disabledClass),!e.disabled&&e.selected&&(o+=i.selectedClass),n.push({group:s,index:t,classNames:o,text:e.text,value:e.value})}var e=this.$element,i=this.options,n=[],o=e.find("optgroup");this.$originalOptions=this.$element.find("option"),this.multiple||null!==e.val()||this.$originalOptions.length&&(this.$originalOptions.get(0).selected=!0),o.length?o.each(function(e){n.push({header:!0,group:e+1,text:this.label}),o.eq(e).find("option").each(function(i,n){t(i,n,e)})}):this.$originalOptions.each(function(e,i){t(e,i,null)}),this.$list.html(s.template(i.listTpl,{options:n})),this.$shadowOptions=this.$list.find("> li").not(".am-selected-list-header")},o.prototype.setChecked=function(t){var e=this.options,i=n(t),s=i.hasClass(e.selectedClass);if(this.multiple){var o=this.$list.find("."+e.selectedClass).length;if(!s&&this.maxChecked<=o)return this.$element.trigger("checkedOverflow.selected.amui",{selected:this}),!1}else{if(this.dropdown.close(),s)return!1;this.$shadowOptions.not(i).removeClass(e.selectedClass)}i.toggleClass(e.selectedClass),this.syncData(t)},o.prototype.syncData=function(t){var e=this,i=this.options,s=[],o=n([]);if(this.$shadowOptions.filter("."+i.selectedClass).each(function(){var i=n(this);s.push(i.find(".am-selected-text").text()),t||(o=o.add(e.$originalOptions.filter('[value="'+i.data("value")+'"]').prop("selected",!0)))}),t){var a=n(t);this.$originalOptions.filter('[value="'+a.data("value")+'"]').prop("selected",a.hasClass(i.selectedClass))}else this.$originalOptions.not(o).prop("selected",!1);this.$element.val()||(s=[i.placeholder]),this.$status.text(s.join(", ")),this.initialized&&this.$element.trigger("change")},o.prototype.bindEvents=function(){var t=this,e="am-selected-list-header",i=s.utils.debounce(function(i){t.$shadowOptions.not("."+e).hide().filter(':containsNC("'+i.target.value+'")').show()},100);this.$list.on("click","> li",function(i){var s=n(this);!s.hasClass(t.options.disabledClass)&&!s.hasClass(e)&&t.setChecked(this)}),this.$searchField.on("keyup.selected.amui",i),this.$selector.on("closed.dropdown.amui",function(){t.$searchField.val(""),t.$shadowOptions.css({display:""})}),this.$element.on("validated.field.validator.amui",function(e){if(e.validity){var i=e.validity.valid,n="am-invalid";t.$selector[(i?"remove":"add")+"Class"](n)}}),s.support.mutationobserver&&(this.observer=new s.support.mutationobserver(function(){t.$element.trigger("changed.selected.amui")}),this.observer.observe(this.$element[0],{childList:!0,subtree:!0,characterData:!0})),this.$element.on("changed.selected.amui",function(){t.renderOptions(),t.syncData()})},o.prototype.select=function(t){var e;e="number"==typeof t?this.$list.find("> li").not(".am-selected-list-header").eq(t):"string"==typeof t?this.$list.find(t):n(t),e.trigger("click")},o.prototype.enable=function(){this.$element.prop("disable",!1),this.$selector.dropdown("enable")},o.prototype.disable=function(){this.$element.prop("disable",!0),this.$selector.dropdown("disable")},o.prototype.destroy=function(){this.$element.removeData("amui.selected").show(),this.$selector.remove()},s.plugin("selected",o),s.ready(function(t){n("[data-am-selected]",t).selected()}),t.exports=o},function(t,e,i){"use strict";i(15);var n=i(1),s=i(2),o=i(26),a=document,r=n(a),l=function(t){this.options=n.extend({},l.DEFAULTS,t||{}),this.$element=null,this.$wechatQr=null,this.pics=null,this.inited=!1,this.active=!1};l.DEFAULTS={sns:["weibo","qq","qzone","tqq","wechat","renren"],title:"\u5206\u4eab\u5230",cancel:"\u53d6\u6d88",closeOnShare:!0,id:s.utils.generateGUID("am-share"),desc:"Hi\uff0c\u5b64\u591c\u89c2\u5929\u8c61\uff0c\u53d1\u73b0\u4e00\u4e2a\u4e0d\u9519\u7684\u897f\u897f\uff0c\u5206\u4eab\u4e00\u4e0b\u4e0b ;-)",via:"Amaze UI",tpl:''},l.SNS={weibo:{title:"\u65b0\u6d6a\u5fae\u535a",url:"http://service.weibo.com/share/share.php",width:620,height:450,icon:"weibo"},qq:{title:"QQ \u597d\u53cb",url:"http://connect.qq.com/widget/shareqq/index.html",icon:"qq"},qzone:{title:"QQ \u7a7a\u95f4",url:"http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey",icon:"star"},tqq:{title:"\u817e\u8baf\u5fae\u535a",url:"http://v.t.qq.com/share/share.php",icon:"tencent-weibo"},wechat:{title:"\u5fae\u4fe1",url:"[qrcode]",icon:"wechat"},renren:{title:"\u4eba\u4eba\u7f51",url:"http://widget.renren.com/dialog/share",icon:"renren"},douban:{title:"\u8c46\u74e3",url:"http://www.douban.com/recommend/",icon:"share-alt"},mail:{title:"\u90ae\u4ef6\u5206\u4eab",url:"mailto:",icon:"envelope-o"},sms:{title:"\u77ed\u4fe1\u5206\u4eab",url:"sms:",icon:"comment"}},l.prototype.render=function(){var t=this.options,e=[],i=encodeURIComponent(a.title),o=encodeURIComponent(a.location),r="?body="+i+o;return t.sns.forEach(function(n,s){if(l.SNS[n]){var a,c=l.SNS[n];c.id=n,a="mail"===n?r+"&subject="+t.desc:"sms"===n?r:"?url="+o+"&title="+i,c.shareUrl=c.url+a,e.push(c)}}),s.template(t.tpl,n.extend({},t,{sns:e}))},l.prototype.init=function(){if(!this.inited){var t=this,e="[data-am-share-to]";r.ready(n.proxy(function(){n("body").append(this.render()),this.$element=n("#"+this.options.id),this.$element.find("[data-am-share-close]").on("click.share.amui",function(){t.close()})},this)),r.on("click.share.amui",e,n.proxy(function(t){var i=n(t.target),s=i.is(e)&&i||i.parent(e),o=s.attr("data-am-share-to");"mail"!==o&&"sms"!==o&&(t.preventDefault(),this.shareTo(o,this.setData(o))),this.close()},this)),this.inited=!0}},l.prototype.open=function(){!this.inited&&this.init(),this.$element&&this.$element.modal("open"),this.$element.trigger("open.share.amui"),this.active=!0},l.prototype.close=function(){this.$element&&this.$element.modal("close"),this.$element.trigger("close.share.amui"),this.active=!1},l.prototype.toggle=function(){this.active?this.close():this.open()},l.prototype.setData=function(t){if(t){var e={url:a.location,title:a.title},i=this.options.desc,n=this.pics||[],s=/^(qzone|qq|tqq)$/;if(s.test(t)&&!n.length){for(var o=a.images,r=0;r
          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 Wzv`DSX#r0%VDNPHb6Mw<&;$Txfg`&B literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/dialogs/image/image.css b/web/assets/common/plugins/umeditor/dialogs/image/image.css new file mode 100644 index 0000000..5548391 --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/image/image.css @@ -0,0 +1,42 @@ +.edui-dialog-image .edui-image-wrapper{font-size: 12px;margin: 15px;} + +/*upload*/ +.edui-dialog-image .edui-image-upload1{position: absolute;top:50%;left:50%;width:44px;height:38px;margin-top:-19px; margin-left: -22px;} +.edui-dialog-image .edui-image-upload2{position:relative;float:left;width:120px;height:120px;margin:5px 0 0 5px;} + +.edui-dialog-image .edui-image-form{position: absolute;left: 0px;top: 0px;width: 100%;height: 100%;opacity: 0;cursor: pointer;} +.edui-dialog-image .edui-image-form .edui-image-file{width: 100%;height:100%;filter: alpha(opacity=0)} + +.edui-dialog-image .edui-image-upload1 .edui-image-icon{display: inline-block;width:44px;height:38px;background-image: url('images/upload1.png')} +.edui-dialog-image .edui-image-upload1 .edui-image-icon.hover{background-position: -50px 0;} +.edui-dialog-image .edui-image-upload2 .edui-image-icon{display: inline-block;width:120px;height:120px;background-image: url('images/upload2.png')} + +.edui-dialog-image .edui-image-dragTip{position: absolute;display:none;top:50%;left:50%;margin-top:30px;margin-left: -60px; + color: #222;font-size:14px;text-shadow: 0px 2px 3px #555;} + +.edui-dialog-image .edui-image-content{height:330px;width:100%;position: relative;} + +.edui-dialog-image .edui-image-mask{display: none;position: absolute;top:0;left:0;width: 100%; height: 100%;background-color:#fff; + text-align: center;line-height:300px;color:#000;font-size:14px;font-weight:bold;opacity: 0.6;filter: alpha(opacity=60);} +.edui-dialog-image .edui-image-mask.edui-active{display: block;} + +/*network*/ +.edui-dialog-image .edui-image-searchBar{margin: 10px;} +.edui-dialog-image .edui-image-searchBar .edui-image-searchTxt{display: inline-block !important;*display: inline !important;*zoom:1;width:400px; border: 1px solid #c5d2ff; height: 20px; line-height: 18px; font-size: 14px; padding: 3px; margin: 0;outline:0;} +.edui-dialog-image .edui-image-searchBar .edui-image-searchAdd{display: inline-block !important;*display: inline !important;*zoom:1; + width:60px; text-align:center;height: 25px;text-align: center;line-height: 25px; + background-color: #ffffff;padding: 0; border: 1px solid #ababab;margin-left: 20px;cursor: pointer; +} +.edui-dialog-image .edui-image-searchBar .edui-image-searchAdd.hover{ + background-color: #d5e1f2; + padding: 0; + border: 1px solid #a3bde3; +} +.edui-dialog-image .edui-image-searchRes{height:280px;overflow:auto;} + + +/*common*/ +.edui-dialog-image .edui-image-item{position:relative;float:left;width:120px;height:120px;border: 1px solid #CCC;cursor: default;margin: 5px 0 0 5px;} +.edui-dialog-image .edui-image-item .edui-image-pic{position: absolute;left:-9999px;} +.edui-dialog-image .edui-image-item .edui-image-close{position:absolute;right:0;background: url('images/close.png');width:17px;height:17px;cursor:pointer;z-index:1} +.edui-dialog-image .edui-image-item.hover .edui-image-close{display: block;} diff --git a/web/assets/common/plugins/umeditor/dialogs/image/image.js b/web/assets/common/plugins/umeditor/dialogs/image/image.js new file mode 100644 index 0000000..f8f9803 --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/image/image.js @@ -0,0 +1,454 @@ +(function() { + + var utils = UM.utils, + browser = UM.browser, + Base = { + checkURL: function(url) { + if (!url) return false; + url = utils.trim(url); + if (url.length <= 0) { + return false; + } + if (url.search(/http:\/\/|https:\/\//) !== 0) { + url += 'http://'; + } + + url = url.replace(/\?[\s\S]*$/, ""); + + if (!/(.gif|.jpg|.jpeg|.png)$/i.test(url)) { + return false; + } + return url; + }, + getAllPic: function(sel, $w, editor) { + var me = this, + arr = [], + $imgs = $(sel, $w); + + $.each($imgs, function(index, node) { + $(node).removeAttr("width").removeAttr("height"); + + // if (node.width > editor.options.initialFrameWidth) { + // me.scale(node, editor.options.initialFrameWidth - + // parseInt($(editor.body).css("padding-left")) - + // parseInt($(editor.body).css("padding-right"))); + // } + + return arr.push({ + _src: node.src, + src: node.src + }); + }); + + return arr; + }, + scale: function(img, max, oWidth, oHeight) { + var width = 0, + height = 0, + percent, ow = img.width || oWidth, + oh = img.height || oHeight; + if (ow > max || oh > max) { + if (ow >= oh) { + if (width = ow - max) { + percent = (width / ow).toFixed(2); + img.height = oh - oh * percent; + img.width = max; + } + } else { + if (height = oh - max) { + percent = (height / oh).toFixed(2); + img.width = ow - ow * percent; + img.height = max; + } + } + } + + return this; + }, + close: function($img) { + + $img.css({ + top: ($img.parent().height() - $img.height()) / 2, + left: ($img.parent().width() - $img.width()) / 2 + }).prev().on("click", function() { + + if ($(this).parent().remove().hasClass("edui-image-upload-item")) { + //显示图片计数-1 + Upload.showCount--; + Upload.updateView(); + } + + }); + + return this; + }, + createImgBase64: function(img, file, $w) { + if (browser.webkit) { + //Chrome8+ + img.src = window.webkitURL.createObjectURL(file); + } else if (browser.gecko) { + //FF4+ + img.src = window.URL.createObjectURL(file); + } else { + //实例化file reader对象 + var reader = new FileReader(); + reader.onload = function(e) { + img.src = this.result; + $w.append(img); + }; + reader.readAsDataURL(file); + } + }, + callback: function(editor, $w, url, state) { + if (state == "SUCCESS") { + //显示图片计数+1 + Upload.showCount++; + var $img = $(""), + $item = $("
          ").append($img); + + if ($(".edui-image-upload2", $w).length < 1) { + $(".edui-image-content", $w).append($item); + + Upload.render(".edui-image-content", 2) + .config(".edui-image-upload2"); + } else { + $(".edui-image-upload2", $w).before($item).show(); + } + + $img.on("load", function() { + Base.scale(this, 120); + Base.close($(this)); + $(".edui-image-content", $w).focus(); + }); + + } else { + currentDialog.showTip(state); + window.setTimeout(function() { + + currentDialog.hideTip(); + + }, 3000); + } + + Upload.toggleMask(); + + } + }; + + /* + * 本地上传 + * */ + var Upload = { + showCount: 0, + uploadTpl: '
          ' + + '' + + '
          ' + + '' + + '
          ' + + + '
          ', + init: function(editor, $w) { + var me = this; + + me.editor = editor; + me.dialog = $w; + me.render(".edui-image-local", 1); + me.config(".edui-image-upload1"); + me.submit(); + me.drag(); + + $(".edui-image-upload1").hover(function() { + $(".edui-image-icon", this).toggleClass("hover"); + }); + + if (!(UM.browser.ie && UM.browser.version <= 9)) { + $(".edui-image-dragTip", me.dialog).css("display", "block"); + } + + + return me; + }, + render: function(sel, t) { + var me = this; + + $(sel, me.dialog).append($(me.uploadTpl.replace(/%%/g, t))); + + return me; + }, + config: function(sel) { + var me = this, + url = me.editor.options.imageUrl; + + url = url + (url.indexOf("?") == -1 ? "?" : "&") + "editorid=" + me.editor.id; //初始form提交地址; + + $("form", $(sel, me.dialog)).attr("action", url); + + return me; + }, + uploadComplete: function(r) { + console.log('uploadComplete'); + + var me = this; + try { + var json = JSON.parse(r); + var state = json.code === 1 ? 'SUCCESS' : 'Error!'; + Base.callback(me.editor, me.dialog, json.data.file_path, state); + } catch (e) { + var lang = me.editor.getLang('image'); + Base.callback(me.editor, me.dialog, '', (lang && lang.uploadError) || 'Error!'); + } + }, + submit: function(callback) { + console.log('submit'); + + var me = this, + input = $(''), + input = input[0]; + + $(me.dialog).delegate(".edui-image-file", "change", function(e) { + + if (!this.parentNode) { + return; + } + + $('') + .insertBefore(me.dialog).on('load', function() { + var r = this.contentWindow.document.body.innerText; + if (r == '') return; + me.uploadComplete(r); + $(this).unbind('load'); + $(this).remove(); + + }); + + $(this).parent()[0].submit(); + Upload.updateInput(input); + me.toggleMask("Loading...."); + callback && callback(); + + }); + + return me; + }, + //更新input + updateInput: function(inputField) { + + $(".edui-image-file", this.dialog).each(function(index, ele) { + + ele.parentNode.replaceChild(inputField.cloneNode(true), ele); + + }); + + }, + //更新上传框 + updateView: function() { + + if (Upload.showCount !== 0) { + return; + } + + $(".edui-image-upload2", this.dialog).hide(); + $(".edui-image-dragTip", this.dialog).show(); + $(".edui-image-upload1", this.dialog).show(); + + }, + drag: function() { + var me = this; + //做拽上传的支持 + if (!UM.browser.ie9below) { + me.dialog.find('.edui-image-content').on('drop', function(e) { + + //获取文件列表 + var fileList = e.originalEvent.dataTransfer.files; + var img = document.createElement('img'); + var hasImg = false; + $.each(fileList, function(i, f) { + if (/^image/.test(f.type)) { + //创建图片的base64 + Base.createImgBase64(img, f, me.dialog); + + var xhr = new XMLHttpRequest(); + xhr.open("post", me.editor.getOpt('imageUrl') + "?type=ajax", true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + //模拟数据 + var fd = new FormData(); + fd.append(me.editor.getOpt('imageFieldName'), f); + + xhr.send(fd); + xhr.addEventListener('load', function(e) { + var r = e.target.response, + json; + me.uploadComplete(r); + if (i == fileList.length - 1) { + $(img).remove() + } + }); + hasImg = true; + } + }); + if (hasImg) { + e.preventDefault(); + me.toggleMask("Loading...."); + } + + }).on('dragover', function(e) { + e.preventDefault(); + }); + } + }, + toggleMask: function(html) { + var me = this; + + var $mask = $(".edui-image-mask", me.dialog); + if (html) { + if (!(UM.browser.ie && UM.browser.version <= 9)) { + $(".edui-image-dragTip", me.dialog).css("display", "none"); + } + $(".edui-image-upload1", me.dialog).css("display", "none"); + $mask.addClass("edui-active").html(html); + } else { + + $mask.removeClass("edui-active").html(); + + if (Upload.showCount > 0) { + return me; + } + + if (!(UM.browser.ie && UM.browser.version <= 9)) { + $(".edui-image-dragTip", me.dialog).css("display", "block"); + } + $(".edui-image-upload1", me.dialog).css("display", "block"); + } + + return me; + } + }; + + /* + * 网络图片 + * */ + var NetWork = { + init: function(editor, $w) { + var me = this; + + me.editor = editor; + me.dialog = $w; + + me.initEvt(); + }, + initEvt: function() { + var me = this, + url, + $ele = $(".edui-image-searchTxt", me.dialog); + + $(".edui-image-searchAdd", me.dialog).on("click", function() { + url = Base.checkURL($ele.val()); + + if (url) { + + $("").on("load", function() { + + + var $item = $("
          ").append(this); + + $(".edui-image-searchRes", me.dialog).append($item); + + Base.scale(this, 120); + + $item.width($(this).width()); + + Base.close($(this)); + + $ele.val(""); + }); + } + }) + .hover(function() { + $(this).toggleClass("hover"); + }); + } + }; + + var $tab = null, + currentDialog = null; + + UM.registerWidget('image', { + tpl: "image.css\">" + + "
          " + + "" + + "
          " + + "
          " + + "
          " + + "
          " + + "
          <%=lang_input_dragTip%>
          " + + "
          " + + "
          " + + "
          " + + "" + + "
          <%=lang_btn_add%>
          " + + "
          " + + "
          " + + "
          " + + "
          " + + "
          ", + initContent: function(editor, $dialog) { + var lang = editor.getLang('image')["static"], + opt = $.extend({}, lang, { + image_url: UMEDITOR_CONFIG.UMEDITOR_HOME_URL + 'dialogs/image/' + }); + + Upload.showCount = 0; + + if (lang) { + var html = $.parseTmpl(this.tpl, opt); + } + + currentDialog = $dialog.edui(); + + this.root().html(html); + + }, + initEvent: function(editor, $w) { + $tab = $.eduitab({ + selector: ".edui-image-wrapper" + }) + .edui().on("beforeshow", function(e) { + e.stopPropagation(); + }); + + Upload.init(editor, $w); + + NetWork.init(editor, $w); + }, + buttons: { + 'ok': { + exec: function(editor, $w) { + var sel = "", + index = $tab.activate(); + + if (index == 0) { + sel = ".edui-image-content .edui-image-pic"; + } else if (index == 1) { + sel = ".edui-image-searchRes .edui-image-pic"; + } + + var list = Base.getAllPic(sel, $w, editor); + + if (index != -1) { + editor.execCommand('insertimage', list); + } + } + }, + 'cancel': {} + }, + width: 700, + height: 408 + }, function(editor, $w, url, state) { + Base.callback(editor, $w, url, state) + }) +})(); diff --git a/web/assets/common/plugins/umeditor/dialogs/image/images/close.png b/web/assets/common/plugins/umeditor/dialogs/image/images/close.png new file mode 100644 index 0000000000000000000000000000000000000000..d368388061dbc5d5abde9c990edb3a875966a846 GIT binary patch literal 1042 zcmbVLJ8#oa7&R(_s%i%w3$R>VfrLbCUnh2KtETlUG*T0yB&Zqd*td<<+Sk~(#O*@M z075LFZfyMnNQj{W0}Dd1u!0zvnCU;DT<1|3st#DT@9UiNJ?{5CYt-+~OkJH~7-pur zXgBG2A^#>P=zsd{!6!OgAx?+1@G9xM31SQ%cM+(D?iy+$*WY^Z8O<@wgdem!q*Ghc zJREXvj^VN}rfi0po6lm`TSo+R(OMAc?BTojEC_s^U6yJ>Ew)fESlmufYrEd|w%0w? zXXo#LxlE%1AtElw!i^}^GMydhYIL4&^DG!Z$hyuBo$AyYz`_Xv5?3sE0)#-8Iarcp z8Qun>07YJ)uUvp7O_DXS3`P%2ttI}d*0d`lTXdzfJwjrQ=llIW*DrE7S>vIqs=0&S?X4K`O_buz#H07IIy)xQ+?{SH2U2gedr3eq#oe&6w;7^{RBfH5 zC!8Pnnk5#YXiAleq{xm4p=k*f+bAod;+SPsP{tZ3aVbbG+lFN-vI3zas^yZTTA~OQ zNj0HhjJee)C2r)QvE6{$9dXT5xtf(Am*Aw0@y56U8a+&K+QTuhS`t_eA|LnD{Bnk| z+9(MgBEOR05Dc=b1t(aSiY3vO)sk(Q7UPqg%8&+U%Ynn%jBi*%7XNCcX#*Y&g2trdhgA*`i)2X%nmcbygEE{_V>YyR^Fvm Lr*6MCS04WXsz5?a literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/dialogs/image/images/upload1.png b/web/assets/common/plugins/umeditor/dialogs/image/images/upload1.png new file mode 100644 index 0000000000000000000000000000000000000000..c9056ca560fbfe63739f3137a107823ce78c2be9 GIT binary patch literal 848 zcmV-W1F!svP)3 zgo@OSiHg%XHw8g@u!kvL#H%;Klio-e8=MZ*)m7FQ36n00S1MZuE^Q>U4 zj%!ygw%ytNM^9Uv;l_o&wzIl3xw*TMb@Cx7irj&Vk;o~Rb?G8_x_G*Hx_G*Hx_G*D zT;0gzYip|Ib#wE(!D^c>#y_J-psD#?Vdce<&HAX5;KGXOiu~e2AazB4Dac8aRh9Xn z7eWe0en$LaG+ z6DxLP=-{y<1B*+m@ej+?l9FNfv)Y0E#QT+UedwgrK1Hr5|5_}S?3J*HF8#P{jD z4<1*J(hA7J0=Qz!^{bbiKjqw-Sg&NIYpRx2eUO73Dz@Cbw2y9mq$m=P%PBeUI&ZF@ z{)m*SOvw=X^X6>bNc;HbRC=z1ZE^Hvb?y1=E0R$gZaDU~rsra+rlWTJ&rnA=-C$n4Ua+pqR6Lp08H58C zSQ(9k6Ki#83^V(2tGX_-L<*uXfOa{OPTTm!CQUSiMFs%cp_@|lmiD9n0p5E|PZvA* aE5HChK;jKxL@4S20000BKni0bqDq=4Q?gYYQomm|kRxge ziP&#~ZQzjJk`ksaC2~;(Or(?JV#QPlA~G0cD4xz>Ky!#RDnz4D!Oe(;;@NZto0dQv zxJV$HLb8sX!4nL`0y_>V7sKRi3Z<~HkX#r?Min^}h{a+>e9&mIz#>+;QHF_Bu`=b1 zK@U7wDOMnI3_)eYh(}R2T7Yp#fa%*5r1I3%caCMsfkJ_jQB)#11tL=^QfVZwerqL` z0l%m5L2G5^MmbE$fR$*0LJaz`ZpI)O)b8VfB8GqsJ6(Z*rik)*sJK81%diw4hXlTm zC5VL0rNu$CWV%2=XEOLS2ukKs1-zsLCXLBYPGC`)gFZflWztxJc!3};naN;65TC|M zh^Mo-G#bRDvyvfd(jYcPro==tF+3O-0dWVgw2xxhTm>w`P(>z+<_%83(p(fnmAR;# z$X!k+u0&)Kv``so&f8phumbrJmIxH6l-OTgHu8b!^tgB$kHL!Pag!4wSu+Jm^du_8 zWpRPb=Yp)?VkQ5lViZ7z5*dyEjLM)2%wI%&H~3)l?&!cWa9$MPV2q1a83}|To0l%h z{d5!;o zt*7d`v#iFiHaay%r`CvTOfHSlQD(w5X3xv+!0D?FjRDtLJU5%YTC=;{jF*}`tsOmT zqpP&quCoHAOJnlb{`Ba~u2O@e{*kA{9{B6Izp~z0eaCg?n&YU=9tei+J@xGeSfjhK z%~xvl=x;cmcLg*?S1=U5{@CmChFsp@p&I+;JMNkXZneSLjt8Fq-J>l?%G-92HfEZmYeXaO@NI1)D?%?fO~u) zoe{5l=&{^)+ugyl^^OA;{BEny;}2I@K(bDq#oN{yxYr6;xo)?3kJZ@^*Vs+>-5!7F ze1kI>3>~SpYc1Yac%b2lS8Z^pP3|&_M`yy@JGw%kpny|v?{2bGOCSuLmcmQQR5f=u z<|vyNOxW@4VM$E#_LwohX74I;&|5mKJ*`EK+slfJE{?0u7uxQ0kkZ>NYqvj|+cO2Z zs^#vA?He=Gwspv`s4=2fwQq41=W$!N zSC%a-zTkOuyZg;dHnUuPZu1I#Oli~n#Px~Oe`zVGZ$!T47?!0JO`Vc|dUC}o$K^dX zmtg{<%Fuz_Y+2Md;_&Rvt2mDmzG-XN%dqh)!w=WsN4j>tj&8qOT6gNZ9eXmWJJ+n< z-1K0rg0*2^!ZZG;yK_$=x>>yy)}OolEmIB)e&&&Dq=!A=Ud`1Y6gDNCY?!&%H#P3P$;Bd=$-HZLwOJ8@1IUO&F>%5Ni->B4cy zB-y0O@jRz-+s^Hob_Z*5)U3~LSruDEM{h|cMu*48pTD1HJ5n+cZd1ke+$uZvX&ohM zR8_uscZ{>>lNIV8P9j#$@bm*M`ipt_T7unlW0mIM!I-Vv#twAM@@?C+qr>0#uV0Ol dnJ0=zQVB(xX|" + + ".edui-dialog-link .edui-link-table{font-size: 12px;margin: 10px;line-height: 30px}" + + ".edui-dialog-link .edui-link-txt{width:300px;height:21px;line-height:21px;border:1px solid #d7d7d7;}" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + +// "" + +// "" + +// "" + + "", + initContent: function (editor) { + var lang = editor.getLang('link'); + if (lang) { + var html = $.parseTmpl(this.tpl, lang.static); + } + this.root().html(html); + }, + initEvent: function (editor, $w) { + var link = editor.queryCommandValue('link'); + if(link){ + $('#edui-link-Jhref',$w).val(utils.html($(link).attr('href'))); + $('#edui-link-Jtitle',$w).val($(link).attr('title')); + $(link).attr('target') == '_blank' && $('#edui-link-Jtarget').attr('checked',true) + } + $('#edui-link-Jhref',$w).focus(); + }, + buttons: { + 'ok': { + exec: function (editor, $w) { + var href = $('#edui-link-Jhref').val().replace(/^\s+|\s+$/g, ''); + + if (href) { + editor.execCommand('link', { + 'href': href, + 'target': $("#edui-link-Jtarget:checked").length ? "_blank" : '_self', + 'title': $("#edui-link-Jtitle").val().replace(/^\s+|\s+$/g, ''), + '_href': href + }); + } + } + }, + 'cancel':{} + }, + width: 400 + }) +})(); + diff --git a/web/assets/common/plugins/umeditor/dialogs/map/map.html b/web/assets/common/plugins/umeditor/dialogs/map/map.html new file mode 100644 index 0000000..be97a8c --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/map/map.html @@ -0,0 +1,148 @@ + + + + + + + 百度地图API自定义地图 + + + + + + + +
          + + + \ No newline at end of file diff --git a/web/assets/common/plugins/umeditor/dialogs/map/map.js b/web/assets/common/plugins/umeditor/dialogs/map/map.js new file mode 100644 index 0000000..6f898c4 --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/map/map.js @@ -0,0 +1,263 @@ +(function () { + + var widgetName = 'map'; + + UM.registerWidget(widgetName, { + + tpl: "" + + "
          " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""+ + "" + + "
          <%=lang_city%>:\"/><%=lang_address%>:<%=lang_search%>
          " + + "
          " + + "
          " + + "", + initContent: function (editor, $widget) { + + var me = this, + lang = editor.getLang(widgetName), + theme_url = editor.options.themePath + editor.options.theme; + + if( me.inited ) { + me.preventDefault(); + return false; + } + + me.inited = true; + + me.lang = lang; + me.editor = editor; + + me.root().html($.parseTmpl(me.tpl, $.extend({}, lang['static'], { + 'theme_url': theme_url + }))); + + me.initRequestApi(); + + }, + /** + * 初始化请求API + */ + initRequestApi: function () { + + var $ifr = null; + + //已经初始化过, 不用再次初始化 + if (window.BMap && window.BMap.Map) { + this.initBaiduMap(); + } else { + + $ifr = $(''); + $ifr.appendTo( this.root() ); + + $ifr = $ifr[ 0 ].contentWindow.document; + + $ifr.open(); + $ifr.write( this.root().find(".edui-tpl-container").html().replace( /scr_ipt/g, 'script').replace('<>',"'" + this.editor.id + "'") ); + + } + + }, + requestMapApi: function (src) { + + var me = this; + + if (src.length) { + + var _src = src[0]; + + src = src.slice(1); + + if (_src) { + $.getScript(_src, function () { + me.requestMapApi(src); + }); + } else { + me.requestMapApi(src); + } + + } else { + + me.initBaiduMap(); + + } + + + }, + initBaiduMap: function () { + + var $root = this.root(), + map = new BMap.Map($root.find(".edui-map-container")[0]), + me = this, + marker, + point, + imgcss, + img = $(me.editor.selection.getRange().getClosedNode()); + + map.enableInertialDragging(); + map.enableScrollWheelZoom(); + map.enableContinuousZoom(); + + if (img.length && /api[.]map[.]baidu[.]com/ig.test(img.attr("src"))) { + var url = img.attr("src"), + centerPos = me.getPars(url, "center").split(","), + markerPos = me.getPars(url, "markers").split(","); + point = new BMap.Point(Number(centerPos[0]), Number(centerPos[1])); + marker = new BMap.Marker(new BMap.Point(Number(markerPos[0]), Number(markerPos[1]))); + map.addControl(new BMap.NavigationControl()); + map.centerAndZoom(point, Number(me.getPars(url, "zoom"))); + imgcss = img.attr('style'); + } else { + point = new BMap.Point(116.404, 39.915); // 创建点坐标 + marker = new BMap.Marker(point); + map.addControl(new BMap.NavigationControl()); + map.centerAndZoom(point, 10); // 初始化地图,设置中心点坐标和地图级别。 + } + marker.enableDragging(); + map.addOverlay(marker); + + me.map = map; + me.marker = marker; + me.imgcss = imgcss; + }, + doSearch: function () { + var me = this, + city = me.root().find('.edui-map-city').val(), + address = me.root().find('.edui-map-address').val(); + + if (!city) { + alert(me.lang.cityMsg); + return; + } + var search = new BMap.LocalSearch(city, { + onSearchComplete: function (results) { + if (results && results.getNumPois()) { + var points = []; + for (var i = 0; i < results.getCurrentNumPois(); i++) { + points.push(results.getPoi(i).point); + } + if (points.length > 1) { + me.map.setViewport(points); + } else { + me.map.centerAndZoom(points[0], 13); + } + point = me.map.getCenter(); + me.marker.setPoint(point); + } else { + alert(me.lang.errorMsg); + } + } + }); + search.search(address || city); + }, + getPars: function (str, par) { + var reg = new RegExp(par + "=((\\d+|[.,])*)", "g"); + return reg.exec(str)[1]; + }, + reset: function(){ + this.map && this.map.reset(); + }, + initEvent: function () { + var me = this, + $root = me.root(); + + $root.find('.edui-map-address').on('keydown', function (evt) { + evt = evt || event; + if (evt.keyCode == 13) { + me.doSearch(); + return false; + } + }); + + $root.find(".edui-map-button").on('click', function (evt) { + me.doSearch(); + }); + + $root.find(".edui-map-address").focus(); + + $root.on( "mousewheel DOMMouseScroll", function ( e ) { + return false; + } ); + + }, + width: 580, + height: 408, + buttons: { + ok: { + exec: function (editor) { + var widget = editor.getWidgetData(widgetName), + center = widget.map.getCenter(), + zoom = widget.map.getZoom(), + size = widget.map.getSize(), + point = widget.marker.P; + + if (widget.root().find(".edui-map-dynamic")[0].checked) { + var URL = editor.getOpt('UMEDITOR_HOME_URL'), + url = [URL + (/\/$/.test(URL) ? '':'/') + "dialogs/map/map.html" + + '#center=' + center.lng + ',' + center.lat, + '&zoom=' + zoom, + '&width=' + size.width, + '&height=' + size.height, + '&markers=' + point.lng + ',' + point.lat].join(''); + editor.execCommand('inserthtml', ''); + } else { + url = "http://api.map.baidu.com/staticimage?center=" + center.lng + ',' + center.lat + + "&zoom=" + zoom + "&width=" + size.width + '&height=' + size.height + "&markers=" + point.lng + ',' + point.lat; + editor.execCommand('inserthtml', '', true); + } + + widget.reset(); + } + }, + cancel: { + exec: function(editor){ + editor.getWidgetData(widgetName).reset(); + } + } + } + }); + +})(); + diff --git a/web/assets/common/plugins/umeditor/dialogs/video/images/center_focus.jpg b/web/assets/common/plugins/umeditor/dialogs/video/images/center_focus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..262b02916c51c01ab71f942b7ea9d02382b0df07 GIT binary patch literal 11795 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 Bm*4;Z literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/dialogs/video/images/left_focus.jpg b/web/assets/common/plugins/umeditor/dialogs/video/images/left_focus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7886d276dda2e0aecdae22393eac534ed507babf GIT binary patch literal 11423 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 T+mF>-Zi@@LHB9%bmHhD^k7NPf literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/dialogs/video/images/none_focus.jpg b/web/assets/common/plugins/umeditor/dialogs/video/images/none_focus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7c768dcb47aaa289135afd2bd0e8eb0b6ecb7536 GIT binary patch literal 11546 zcmeG?2Ut@{w>Jbr5orRVYY7nqq@;%=3M^GYPz(VT+lJ%x1%+Bq5|cBAW-Ee?c0beuN-1ozbVGKoS7sy@Mq( zi8NRehIjU%;g<@<0!g$KqL9dBHkrgG)9_>}o6ca$Lz&HRaRWN7*1|taz0!3K`r>dr|F#&|bmqZMtgdHX7Kp15VR!Lb&MHQ!}g4Lt~ zqAph1SdXmYxP&)}5S~cE>8Bn%>om*6fO92)N{iTd*ICsx&F59yYrd3jc<9_uvt81; z0@tgDWsJ!+zK`KI=QsUP8`<9HW|on+`9fwz-IKTZMc3+|_WK1!ZP|UK_tOD?_MMvXr1$*N9iPnYucgFXJt>D85KU$u zC(azim46KE?e*Jhba1fFLi>2$#lmAn6_`us6HX45l^-kiG0jarw7}kk@<-ODQ|&tQ z`vX?5wN#y2es=2XoW|8%--YsaCgF;1uU*CcQ0`tByKt$)Ft_8UVZGi@Z{Ma4!dId; zthNKqy*fEy+~Yg{`J?yq-|rxPSSa-4^?GJF9-LM5GNCt{oc>)~Lsz&Qiam8O zb$<7Z%;;mS3+u|>?%mS*?3dR3w;R$tj>PXF-tO2_U8QBv6n3_)+eT;=104LPq+@!= zkbZipYDwdh?hp2FN_SqYef}FT`d)(k?!yW>l-{Iy_#}p-*-(N!*^O|iCaB_itqW! zp_hZhU2^E|8hev$hap=zq`uyvQw}}4+?F&Txi{oo+w_s&aJs%l*Z#LSJBN2OeLC_k z*_jeM`iGoWtFYPp!jwa;WBx{Jy+fBl+_hOSK_EOec3QWn`s;Ak}GL6O}K+zwh3V~%Df)L;1ATcBX8HTJOKByyaseer4afl2e`$ZbhFiIFP4ylSG zMI!J(Vg(lwQ+XvN)SoNm>i2uWnv7bu)0LKq|z!{W$LAfxH( zgJ@rj;y`^OFH$C%4~yXlo(vWM1jJnz29GdnD3}opLU9ib!h^^EcK@-F5hAYZGA?Lp zWKa<2e~Iyu2Ekuoa6FNW%M1Dfr^SbX92_HamoDIXc?L)%B1ECuDEbR#^i{559GyeJ7dqYmUjP|_J>Iun;kRAkvw?5B!f(66 z|KqztbVV~pRy4>LWU%HzkT&E93BX1o0Bj`ikT(hA0Z5r2_<4YqLByoRWdsQuvO8C(6BMJU-i}~ zqkuog7Y4QtXyXDt5f;IruvjJqo+Tp=K$V7#p0iH41d&UyZXA*#;0fuv8Km!G~R=fSI#K8gx?5lrh#Y zwsQo`lYz|#*Z_?Nz~`StRc6fGiWRv7E>(3e}B*%DOQ~PGl;JMWWdyNok{|k49Wn{q*_r}3@a*$Y(*tAL1tOe zDIilR00;Egl0XK$wn&{0=pa*RRLWc`!_|%A?8tCrGMp&3Oj}1+29-jlQmIVfRGJg$ zG7_$AZ($59TIM1ILm@?dL1m7oEFVu{j8M=}@BvFEhy@0nPNk3Fzw9QJ!DP^^Xe1Vc zghJ2(TQWrfDJVt(sQ^zWD|l#Dh?Fe~L1#fn00uySp)u(s6oQU|GUz6Nz%jN|;5GyT zegIhrWCBhAk+!J7i3mhmp#tY35Xb_E$^zHY$TUDjAg~rd1;!RFGZDxFb^?gE=dgGD2=fKCJ=vMeSd%VHutEGEJO;unQv6e8MKs5TZ0X_1BMWFeM-dC#x{ z18N0=z=~mIYsIu;SpnCO$yQ{F6`5*9rdfdj225mID-aD7zzbpogb0WpItU~X2OwlX zB+R8V7|t}%8L|t_1x!B|7TuXbbE7-ix-s3T3=$oA9iS^Ldx202R;hI(FBl&R;RZ_Y2n7Z2ro6`(L(s#F6}9o;U~wyA2`%=>-vaD=UV9 z_>Uum(#E~PN7=!)04a{`NbE=V4d0&MJn+o}-#qZm1K&LG%>)0BJn;EU0~Uj=MKn0E z0Cxk7NA3oE56(xxMFkzK5>|0of*hEDgkBd=e2o9Mpwj1r9ZTaRk&oj_V}yUVAip|x zT>yMvS6mmUfNFrr%HUh~=cI7c}#k0O3@XRg}~~7s0nA6=fyl4#5Z& z{ZJ1s7)&-))73XHGoNB)0S@3*fFH2RIB?wnNf^Zy16@4>$as>zp&DVbkz=?ClT2}% zMXl$h(p*gGv$OUbo8qnqT2NBPs$g*wk^3qNNnJf4rDA|nB|8!*`b&6*;fb?qGi|{= zgu71E!zOgse7DIQn#<-Q#kYG5xUztJ0(c9TK2ZJb*2-7i>*HA$YgyOt{PI)do!~4# zzm$95x8-Hw_E<7st&d-^p=X~(v6Hj)%E^1SEHp?jj(?S7aYOY@VrO4e!MXR^+gT3o-(e}-UVB^A7nX~a3(i;b8=?ft}l6ye9U0Q&2-sE+D z=Z15iwyNL#b7F^mZtc-sqE@s;aC<`q3Uo_}G%s6Dwluo3J+pts?0v7dS(`kG#Y|d) zckd=|f25($FsR`;EXE(ykGInKW0`%qJ@pIfypUguSTkG@OF!F#G* zvu0eqR=sT4`NeEQXXSYl2&V+aNkI#D%fcMjQrG)_n0Pp$#@}_wVQ|}qzI93T{cmO} ze5{0XfS&LB`EqEMYH*_Qk@j=dd%{;ZX=o=}?D~m&ozPERMjMy!nbI{;i$*Ry2!{!rqbGUh>#XskZ z{P*@9Yd_wUyS#6&Y7}o{vrv6|I5)aQ%lc|X@+KTN;{e0*m~TvRGG$s^f^K<3v`f>Q z#x0zr(#NVq@eawH&2tupZoXJ*`m8BD@{)yb3mG56S7$HvN#1w`=q8oBPMzp&fnMCezADb;okgm(De)*qC`}Zk0#kOAC&- zqiX3-o{{^5+w@9HH^K(!9=>{J3T6_Z}QmafS!S+T;7TQ@};B0bb$tS#eT5 zzBxN-@+_%OXd&)Np^&Se@9y;D5)Hf#6(2BLrrcBJ|*+u_?@awOI`gt zx&C2b*3Da?@4XrQq?*H*cdt_0eRFk=$HvCBYAZU6u5$G9bMU%50^7@VKZPG_)N9*N zQX9LSyn=SVrcb!$aeiD+<)9quw{m|xb9z`eJtQQ2`doAI<=%}Yz|-2jO%aq$auDvm2&m$pzDR9tg?8q`Hs|3*VR{wdY%kJJ@(Iw%g;scJXyKm zUC?6QnSn0L@DPE(H8;auxaanzTJg2I{)JA{g~!$1XDuq5TiM=|;gSxfUa-j5JZ=$NH~Ust3snvsOYh_7w{0ajmutHl z>PT_xr{T@$_Oo|!W7Xz@1L{86@S4cz>BAD6;)kDl zT1`qH|88@5(DsVQN(0sVF89*XeSTRMWpX*VDJSDWGI#S$*wP~@n8Q8eIQ>y_ip(MI z@#`4ozKcWlCt@A`5R`mqI@$Mb-opg#y;ibct+mWJr1MMjP1-&2(}o0=bnc#GnV%cfntIy~c@t=H|D6+<&FZ#mc(^7QyZy69EQqOS0vq#;|~ znCL4r%9pTX*E?`NY_lr47GGD~+~pCp^GW%+O7Ec=``@ogcI}^h-{bYN5B_!!_xt5! z3^_3T_=?351A?i0__4!z{mpSJR{DoTiAxLS_|JH;v~LnsIm^70qiuds;JK~wNYueh z--rB*q<|$A1zN3EyDL9xH?Mi#B$!|HG34wk-5c8zd+0t-wTn_&e|%@ozZBL|QdL+k zhh~&zergvtw)hMv=xg1u>9hroW>71c=~vXdd}7;kuV#<^su|cVn!9~rk9+k``d7A{8;htxA1$0qWY$!169F{WNj8IHRxbpFI;Nv;hY?insm68{x& z;zq#^cvIn-^d0M_>u1h+GsDd^bKRo)1J@($51u||&4R&_UL*YAld=WXaVocR0wBua9w2KLe)W#UTf_4IcsyZH-NHuYRTeXMqvXx!aKu_<{3yW88i1wF5q{za_>GknTS8Uk( ZqP+Rj#m?aw2h%HWZ?OF-vq^scKLCxw6Lplf*89HS`>nNREz(EQKOhY+ zcTaZ+BZq-Jz&}XZrtr`$o)-o|K0c5Y1VPG>5@s@l1uYEt2VwLe1+)!8A(-i7?Ffv; z2#*}#AwmE``k+Q*NN8Idw0CaiLGmNA$HDU+r1~`lg3L8WpH88|5ZKT!TqqHW!-XP4 zmjH_4VxAyW7$=5E1R{||B(MlfL(m|QSR^`70bvhgWaCPu5Oi2>OuyM->{#0bVFX1H zgB`I^@q0foGrswOG5g*R5R(ymF}8AJy5*0^)FIJAA|iWAzmFw?Lt@zlsi8gvyZAtI z(r!o{?Lna_h@`X`+5o93D=VugtEs4{X--g`ps6!SO>L5nu9lXLmX@xj8d^pkvd&*2 zX5xei6V)ebsH{yz*>3KCM87$Dr$leak4zHAPT3YKw7t38?oTB|NVDllaRLPpVM3dbU0#4 zuc;zpfRY*YN**wj$Bg)|A=K4!vyZh`qX6QoZdH7q)&CgcEh_P#s2yder7OSUb2 znX&XEvB%_Yz5V|AALa-LH!dD3jvMyW(!kz7zoxM+*}W%sNfC9}d;dAjUruvczAin^ zpE#tIW!e)N(7n1WvbA65xp8z;FlCbzY7#%+IfvL^`(kTj@`Az-)|?t0?l7Xv;Ze90 z`kg9;n(4`j_IBqQhX(W_*0^q4Svy=kD>>q3y^zD*n!h#m%NO>|M6ChR3c~Z*H4Yw^ z>uz4IDSYded3#l1%#PCE>WnTIh_eg=zf@&cnsR5`_LN9!2a|$JmiuUDyJ^3Yx ztF0dw#Rc>y;rky*p~D}|UhmA8*yg-RJm18r7YUQ^w7fL_k~yewRCg|GBTSzsZ|%FVpT`(iHoGeNA2Gv|evExU{}+ z(b?YK*DFtKvMWe)ahYY3wx-8cyt~Hkgi>zYP|4jPt7CGG6O6m82=Y&78WFBxs_0HiRmfXax9mekSzl0RPIhu9&(gt#Q#Pemn4oW8 zS@U)Fz=vSz{b$uu=zs(J*xOckP;F*WQeH!AQS$8MSHr`N^9N0((CxTk`3pv-`Ed@n zy5m}t>xYZ8XfyBd9d-?$?opFMIn$e858rJy7PNYCOCK$gLT^6~cS@oA!S*Km9fs&q zNM#L63ONi<=+;dR8`|AyXIE8f7cmrhU#s(Baf1|EH(;OTeYM_y_)zexkPakLhQAH5zLYOSz{OyieC!gC=V6I*nGpFUVQ?62oizm`ELuh10nw{N%}vbp|Ma#bVG=* zVBwH+l+}^A%>Y;kU2tS^)I+*hCjeyrAYBN|)DVQkUNN9A=<^{z$R;?z-v{)Oh3K#4 zdmJJ&x)6@>ea|q895D{*3S#(t@Icmb#SmePAXGd*B!UBaBXa&62@&*=R}BS%^_njf zaFI4r=D}f-@B7XYVdO|3HN&49K1xU35%YN*SiFcoPYi@HH3{+ zC2q7ZMl=qkD2(QDc>-7v6A5J0+yW5o^HCh=Ph`hPgmYm59L<)%p#T8|CWzn>W>pz8 zf3YoK4LCB4x`BCO9I*4aX57j4CLT=iKo~j z&}UwVP{>CVDvzSaF{^|NqZ6I@Jnm?uCdr)l7)1j~rBFDG9mAJ^rb;XvE%_VvMWg8P z>=Qz`E<(OA`n%w2%Je(W9U}k{$X6&pVyz+(isr{i#PDchAT(7zNTh$js)Yz8Aff&N zKLNbr@V~O7U!Db`3{nGC&fy)}mVITKXd5G&h2)WKsw|-~s1(8v$yWk^1ab5m0mX<#C{v*IH~k+6hq7$3{OWWyoQ$c9w1EtyK9kto)NBm#kfC(!X^f(?2+c>hVFq(@eGMP*~frKZKYygLiI6)v`$J+?R#-kcsVKFC~Cz9}l z0z*V28|+6^$OG zjERg>Jw`@f$EXkpu8+^(3yi+G$jGrF#1gky;Ko0D&thcx$NR%#VN5g!c8djO8jlVp z;e~w*=Q!gK$~e1Va3=Nl10j?#RuRi38fHttrUPtzMib!M&z|p2ln@(6xM5eY546J} zM@%*ZrVWV}NTjhyR2G#wi@;zJ2r^b5VJI&w;X76$0dNP7o<@yh1$h|CmazXNb0~)u zCX9|`gBbE6*<2VOA%eL$!?9PxLib5vWMCc{HN(e;1u}qKG^bjp)3ZI z>cn&=GD&22k{j^D*_}pkCX$&<0)@_CGMz_dU4@(&BvVFZL;nl1vi&og|KHPryipcV z0S0FypMf2Yh%-cyKOxD%kC7qi;BzDp@I(ZT(3kuR`Zw}I0S`oyG$Q8<#)*Ypd0beG zV5u}~5|s`r15~Cp`1q|!bPA{d$DmkK$plb|pbhX0vNefGvnCUW)?^|BRHikR1S**X za6k{804m_6BYi5MgG#25NwXQw6p|BzK_t?d6gMK3?(FWyaB^mnC^V|8JK33mGepvr z<;RPM`9Hbxz^f3Wu^^A5Ecu>78<9a%AplI7AQxy!CIgXWG7ugn1K|O=k3u2}5p7IV8KjV?ZtWXo2Ma3F99zrnM_1e*&mR%^5rH2O_z{605%>{-|3?w{b{YT+z!5+k z_^l5v%#B7a%pJkW0=T``z{+7|#|+3(11RVny6lGf--2@A3U;h~CyCq`%Z(BK-G=na z*d02!be7$rS3@_zQL`Ml0RC1KmE@HnIR#a4UJ5AR@Q{L{yb?fQlt-@3701LB!F9V5 zpiG^jtf{SIZZU1LmaZi@vsM5X+u&+@23+4GXXX9ew?fSAjuS($@x%kSuI6k`u$ggrTl!nQ^xMfros_z)@{aWF zH*5XxeMz@`)cclZCJOPqug{N;UUy^d=4E^LhuRG>>VrE{_iSoDoeWefW8}c!5kQ$% zMpqIVixU^jxk-Cd?xQ}{_xhQHRo5aeCRy&d^-J+l#jW91l~-$Vu|My5{5~>mre7a8^v&GP%a6BcZkreBNMIf)t&6>2h9Gk|T`t^cMcw7+lN*TSm>+5<{v zvH?Lj6`+X)PA?+5N!a>T(88W;>_q&p&h0Zcu5|A&RwOQ&PKr?@^t!rkxfJhWRg_O5 zxY^rXi0d%glxkX6qPpBIE9C1zMS^Ntt`geYOjs0O&WXh#*>9i)_{jGGlu)IsY)FG z;McFJeSz?kv`^mF#kw_Ywbil9JNH=P+G?Teq%YfSx>l6$HmZnyoiV{b&@aj&i}FCl z^X&B{FAMoU|GZ5n-K$>P*wW#1_9e5KD~_)^*V9yWgejkyB&x}Dsa;XH)^hs)pRI2;5wnRnPXOKWn0_=yN(h&0^s+ zi@CW^yI%#Kcs{5vYQ1U@Y^>0-GvBnnx#Yk+xn}G-zh9>8FYQk8dpCP(%&Kh1it}Ep zrZ~B(t~B)Y$lQI-Yo6J%`zDTu7Pn-+ecKY1`b)y1_={CD3&Sb{bcZvFK9`r*7_4p~ z8Jn-rjQ7B-6yrD-gnp^p;-xpuI~a<$9~@CBaPbbl-Nfl}*%lr*Wy$eVsw6=M7Y-pZA+oFj6&fe9I266P|pHp9L+Yw`%ap!%@HR3eO z$lRB?4%)`gX;pWt%HO=oUo+6~`c$s(F<*~qdD)vhIh1B2-M0@1TA#0KuuUxdvikVb z_l>W9$-%uTNS+b)3-6QXpY10L(Tu_(8P)%)yvSfZC#_pQ=h&I2Ee?^ZV>{0@K6$>q zOcZq{Brp%RD0at@rC)Qaho|iuT<$bucl*{4A@5%dr*v1DM;+Sm(!OEEZpq#Gzdnka z6}Kzm{o^TRH{9yxJ-N{55!;kUDWP@xEHfph#FW^*@OXG!Wrb?fhajcoq_D1*LmCOZ zIgRP@HN0Gn4JVl9!r8`Cv=1(yn4X%jVPng3awKbehPo1#{rI&>>0;}18fup-I)iSR z*$?n6yuQRxPnv1T*IG^skzYd>)So%nJ!?vrD`W3UM{>eS-+5U2rl%M6`te5ZSVgnf zTf%d;^yV9Sh!Jl5dm($%4B21`anx(&BgR4+i(E7V}8Bb%y`J#9u{)Np?mikrP@KmR5 zUDs!3X@WH{Zx3dQiXkoT_WiCoDe}Qv_jqo)MM?| zw7RnNV@z=d?eXf5&k|h*}UvDmPGy<>G}87sK1)jU9)H(>8u zsk-_0V%WoTk?*u4ZMIdHq)_Rbhk0Lj-);0^CD|YEo$QI%?sHxeTcgoj*im)mVg8!E z4j*a*&%HEH(~1d73|zxZ{O#nVeA;uKE-XI*)|@p2)M6F*j5O?+2(VM=k=s5fMOYXFl%XP zhV}D!$tlfPV#ofpoo@OEZW^+g-gJ)wu0`U8AZF#t{}C({j{#*@du(M%^Gk(Ud=Uw8})cm={>K-n27zQ znc8ZHp``2E`K{31&6-*PrO%$(zEqj3a#*5!AzM7%zEFHU^~K3)(x%!<;=T7H)Tbt*!j@a^RF5ceZG#hP=RY zY$#W)DqjVk_%bbZkK?%={>#-`w-}|sw>R8AeV_#T&i;eu#Usik$jwmWQ`8@ArMs05AYd!b8eB7Z%je=H-nki=PT@GCR^;4C6 zboPj{CtFzQ%SY044>na+M5Yh74qZ+3emJC1#rm`C&f&qeNAKNhDaRiUgZ!JnVcWgRClBMV5d!$k0kir+5-b?54NS>y|b2{zW2HG@qYmKL(QcC literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/dialogs/video/video.css b/web/assets/common/plugins/umeditor/dialogs/video/video.css new file mode 100644 index 0000000..1908b0f --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/video/video.css @@ -0,0 +1,170 @@ +@charset "utf-8"; +.edui-dialog-video .edui-video-wrapper { + width: 570px; + _width: 575px; + margin: 10px auto; + zoom: 1; + position: relative +} + +.edui-dialog-video .edui-video-tabbody { + height: 335px; +} + +.edui-dialog-video .edui-video-panel { + position: absolute; + width: 100%; + height: 100%; + background: #fff; +} + +.edui-dialog-video .edui-video-panel table { + margin: 5px 0; +} + +.edui-dialog-video .edui-video-panel table td { + vertical-align: middle; + padding-left: 10px; +} + +.edui-dialog-video #eduiVideoSearchTxt { + margin-left: 15px; + background: #FFF; + width: 200px; + height: 21px; + line-height: 21px; + border: 1px solid #d7d7d7; +} + +.edui-dialog-video #searchList { + width: 570px; + overflow: auto; + zoom: 1; + height: 270px; +} + +.edui-dialog-video #searchList div { + float: left; + width: 120px; + height: 135px; + margin: 5px 15px; +} + +.edui-dialog-video #searchList img { + margin: 2px 8px; + cursor: pointer; + border: 2px solid #fff +} + +/*不用缩略图*/ +.edui-dialog-video #searchList p { + margin-left: 10px; +} + +.edui-dialog-video #eduiVideoType { + width: 65px; + height: 23px; + line-height: 22px; + border: 1px solid #d7d7d7; +} + +.edui-dialog-video #eduiVideoSearchBtn, .edui-dialog-video #eduiVideoSearchReset { + height: 25px; + line-height: 25px; + background: #eee; + border: 1px solid #d7d7d7; + cursor: pointer; + padding: 0 5px; +} + +.edui-dialog-video #eduiVideoPreview { + width: 380px; + min-height: 240px; + margin-left: 10px; + background-color: #f9f9f9; + float: left; +} + +.edui-dialog-video #eduiVideoPreview video { + max-width: 100%; +} + +.edui-dialog-video #eduiVideoInfo { + float: left; + width: 155px; + margin-left: 10px; +} + +.edui-dialog-video .edui-video-wrapper fieldset { + margin-bottom: 20px; + padding-left: 5px; + padding-bottom: 5px; + border: 1px solid #ddd; +} + +.edui-dialog-video .edui-video-wrapper label { + margin-bottom: 0; +} + +.edui-dialog-video .edui-video-wrapper fieldset legend { + width: auto; + font-size: 12px; + font-weight: bold; + margin-bottom: 0; + border-bottom: none; + padding: 0 5px; +} + +.edui-dialog-video .edui-video-wrapper fieldset p { + line-height: 30px; +} + +.edui-dialog-video .edui-video-wrapper fieldset input.edui-video-txt { + width: 65px; + height: 21px; + padding: 0 !important; + line-height: 21px; + margin: 8px 5px; + background: #FFF; + border: 1px solid #d7d7d7; +} + +.edui-dialog-video .edui-video-panel input[type=text] { + width: 470px; + height: 25px; + padding: 0; + line-height: 21px; + margin: 8px 5px; + background: #FFF; + /*border: 1px solid #d7d7d7;*/ +} + +.edui-dialog-video .edui-video-wrapper label { + font-weight: bold; + margin-left: 5px; + margin-bottom: 0; +} + +.edui-dialog-video #eduiVideoFloat div { + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); + margin: 9px; + _margin: 5px; + width: 38px; + height: 36px; + float: left; +} + +.edui-dialog-video #eduiVideoFloat .edui-video-focus { + opacity: 1; + filter: alpha(opacity=100) +} + +.edui-dialog-video .edui-video-wrapper span.edui-video-view { + display: inline-block; + width: 30px; + float: right; + cursor: pointer; + color: blue +} \ No newline at end of file diff --git a/web/assets/common/plugins/umeditor/dialogs/video/video.js b/web/assets/common/plugins/umeditor/dialogs/video/video.js new file mode 100644 index 0000000..e5c4b69 --- /dev/null +++ b/web/assets/common/plugins/umeditor/dialogs/video/video.js @@ -0,0 +1,314 @@ + +(function(){ + var domUtils = UM.dom.domUtils; + var widgetName = 'video'; + + UM.registerWidget( widgetName,{ + + tpl: "video.css\" />" + + "
          " + + "
          " + + "
          " + + "<%=lang_tab_insertV%>" + + "
          " + + "
          " + + "
          " + + "" + + "" + + " " + + " " + + "" + + "" + + " " + + " " + + "" + + // "" + + // " " + + // " " + + // "" + + "
          " + + " " + + " " + + " " + + "
          " + + " " + + "
          " + + // " " + + // " " + + // "
          " + + "
          " + + "
          " + + "
          " + + "<%=lang_video_size%>" + + "" + + // "" + + "" + + " " + + " " + + "" + + "
          " + + "
          " + + "
          " + + "<%=lang_alignment%>" + + "
          " + + "
          " + + "
          " + + "
          " + + "
          " + + "
          " + + "
          ", + initContent:function( editor, $widget ){ + + var me = this, + lang = editor.getLang( widgetName), + video_url = UMEDITOR_CONFIG.UMEDITOR_HOME_URL + 'dialogs/video/'; + + me.lang = lang; + me.editor = editor; + me.$widget = $widget; + me.root().html( $.parseTmpl( me.tpl, $.extend( { video_url: video_url }, lang['static'] ) ) ); + + me.initController( lang ); + + }, + initEvent:function(){ + + var me = this, + url = $("#eduiVideoUrl", me.$widget)[0]; + + if( 'oninput' in url ) { + url.oninput = function(){ + me.createPreviewVideo( this.value ); + }; + } else { + url.onpropertychange = function () { + me.createPreviewVideo( this.value ); + } + } + + }, + initController: function( lang ){ + + var me = this, + img = me.editor.selection.getRange().getClosedNode(), + url; + + me.createAlignButton( ["eduiVideoFloat"] ); + + //编辑视频时初始化相关信息 + if(img && img.className == "edui-faked-video"){ + $("#eduiVideoUrl", me.$widget)[0].value = url = img.getAttribute("_url"); + $("#eduiVideoWidth", me.$widget)[0].value = img.width; + $("#eduiVideoHeight", me.$widget)[0].value = img.height; + var align = domUtils.getComputedStyle(img,"float"), + parentAlign = domUtils.getComputedStyle(img.parentNode,"text-align"); + me.updateAlignButton(parentAlign==="center"?"center":align); + } + me.createPreviewVideo(url); + + }, + /** + * 根据url生成视频预览 + */ + createPreviewVideo: function(url){ + + if ( !url )return; + + var me = this; + // lang = me.lang, + // conUrl = me.convert_url(url); + + // if(!me.endWith(conUrl,[".swf",".flv",".wmv"])){ + // $("#eduiVideoPreview", me.$widget).html( lang.urlError ); + // return; + // } + + $("#eduiVideoPreview", me.$widget)[0].innerHTML = ''; + + }, + /** + * 将单个视频信息插入编辑器中 + */ + insertSingle: function(){ + + var me = this, + // width = $("#eduiVideoWidth", me.$widget)[0], + height = $("#eduiVideoHeight", me.$widget)[0], + url = $('#eduiVideoUrl', me.$widget)[0].value, + poster = $('#eduiVideoPoster', me.$widget)[0].value, + align = this.findFocus("eduiVideoFloat","name"); + + if(!url) return false; + if ( !me.checkNum( [ height] ) ) return false; + this.editor.execCommand('insertvideo', { + url: me.convert_url(url), + // width: width.value, + poster: poster, + height: height.value, + align: align + }); + + }, + /** + * URL转换 + */ + convert_url: function(url){ + if ( !url ) return ''; + var matches = url.match(/youtu.be\/(\w+)$/) || + url.match(/youtube\.com\/watch\?v=(\w+)/) || + url.match(/youtube.com\/v\/(\w+)/), + youku = url.match(/youku\.com\/v_show\/id_(\w+)/), + youkuPlay = /player\.youku\.com/ig.test(url); + + if(youkuPlay){ + url = url.replace(/\?f=.*/, ""); + } else if (matches){ + url = "https://www.youtube.com/v/" + matches[1] + "?version=3&feature=player_embedded"; + }else if(youku){ + url = "http://player.youku.com/player.php/sid/"+youku[1]+"/v.swf" + } else { + url = url.replace(/http:\/\/www\.tudou\.com\/programs\/view\/([\w\-]+)\/?/i, "http://www.tudou.com/v/$1") + .replace(/http:\/\/www\.youtube\.com\/watch\?v=([\w\-]+)/i, "http://www.youtube.com/v/$1") + .replace(/http:\/\/v\.youku\.com\/v_show\/id_([\w\-=]+)\.html/i, "http://player.youku.com/player.php/sid/$1") + .replace(/http:\/\/www\.56\.com\/u\d+\/v_([\w\-]+)\.html/i, "http://player.56.com/v_$1.swf") + .replace(/http:\/\/www.56.com\/w\d+\/play_album\-aid\-\d+_vid\-([^.]+)\.html/i, "http://player.56.com/v_$1.swf") + .replace(/http:\/\/v\.ku6\.com\/.+\/([^.]+)\.html/i, "http://player.ku6.com/refer/$1/v.swf") + .replace(/\?f=.*/, ""); + } + return url; + }, + /** + * 检测传入的所有input框中输入的长宽是否是正数 + */ + checkNum: function checkNum( nodes ) { + + var me = this; + + for ( var i = 0, ci; ci = nodes[i++]; ) { + var value = ci.value; + if ( !me.isNumber( value ) && value) { + alert( me.lang.numError ); + ci.value = ""; + ci.focus(); + return false; + } + } + return true; + }, + /** + * 数字判断 + * @param value + */ + isNumber: function( value ) { + return /(0|^[1-9]\d*$)/.test( value ); + }, + updateAlignButton: function( align ) { + var aligns = $( "#eduiVideoFloat", this.$widget )[0].children; + + for ( var i = 0, ci; ci = aligns[i++]; ) { + if ( ci.getAttribute( "name" ) == align ) { + if ( ci.className !="edui-video-focus" ) { + ci.className = "edui-video-focus"; + } + } else { + if ( ci.className =="edui-video-focus" ) { + ci.className = ""; + } + } + } + + }, + /** + * 创建图片浮动选择按钮 + * @param ids + */ + createAlignButton: function( ids ) { + var lang = this.lang, + vidoe_home = UMEDITOR_CONFIG.UMEDITOR_HOME_URL + 'dialogs/video/'; + + for ( var i = 0, ci; ci = ids[i++]; ) { + var floatContainer = $( "#" + ci, this.$widget ) [0], + nameMaps = {"none":lang['default'], "left":lang.floatLeft, "right":lang.floatRight}; + for ( var j in nameMaps ) { + var div = document.createElement( "div" ); + div.setAttribute( "name", j ); + if ( j == "none" ) div.className="edui-video-focus"; + div.style.cssText = "background:url("+ vidoe_home +"images/" + j + "_focus.jpg);"; + div.setAttribute( "title", nameMaps[j] ); + floatContainer.appendChild( div ); + } + this.switchSelect( ci ); + } + }, + /** + * 选择切换 + */ + switchSelect: function( selectParentId ) { + var selects = $( "#" + selectParentId, this.$widget )[0].children; + for ( var i = 0, ci; ci = selects[i++]; ) { + $(ci).on("click", function () { + for ( var j = 0, cj; cj = selects[j++]; ) { + cj.className = ""; + cj.removeAttribute && cj.removeAttribute( "class" ); + } + this.className = "edui-video-focus"; + } ) + } + }, + /** + * 找到id下具有focus类的节点并返回该节点下的某个属性 + * @param id + * @param returnProperty + */ + findFocus: function( id, returnProperty ) { + var tabs = $( "#" + id , this.$widget)[0].children, + property; + for ( var i = 0, ci; ci = tabs[i++]; ) { + if ( ci.className=="edui-video-focus" ) { + property = ci.getAttribute( returnProperty ); + break; + } + } + return property; + }, + /** + * 末尾字符检测 + */ + endWith: function(str,endStrArr){ + for(var i=0,len = endStrArr.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 ZzyK$sgmq7P@T33$002ovPDHLkV1jUEUt0hG literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/alldeletebtnupskin.png b/web/assets/common/plugins/umeditor/lang/en/images/alldeletebtnupskin.png new file mode 100644 index 0000000000000000000000000000000000000000..61658ce6f10164478ce293c05f1f0485a8fa1fc4 GIT binary patch literal 743 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 P9go0R-$Jhf?tK5Bgo^WU literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/button.png b/web/assets/common/plugins/umeditor/lang/en/images/button.png new file mode 100644 index 0000000000000000000000000000000000000000..098874cb1fa85852d77ba9acbb5850c91c341fb7 GIT binary patch literal 4929 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 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/deletedisable.png b/web/assets/common/plugins/umeditor/lang/en/images/deletedisable.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ee75094f59f0c1262806fd294d361f30f64f58 GIT binary patch literal 649 zcmV;40(Sk0P)!u-Pc3GOd!YPO+bG=gGoA_gW7h+<&i6W@>CzIUsorkT}tXKy|I-GoR_nf%RY$+yzqrWn_y0c| zycG_I116IRG@DK0sZ-E55u|Ou1fok>7zp9LsHJgReXhe;7C4$VS;)mE^wOWxM5!>w+j7B4b z!(p<+cswRyO2rY#*XuP527_xko|w>=_a0qmu_(~#bP__L5ZG)saJ${)TYOfjR7kAi zYvg9L`EQ38LQ-=rdwc{0EpJ`Z=~U9|_2kw4eh&tN0eJ55 jcWg?)n1^9d$t00000NkvXXu0mjfULPWN literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/deleteenable.png b/web/assets/common/plugins/umeditor/lang/en/images/deleteenable.png new file mode 100644 index 0000000000000000000000000000000000000000..26acc883567c5d7fde8de3ba052d7754a5b1c539 GIT binary patch literal 664 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 F`FAsl-Jbve literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/localimage.png b/web/assets/common/plugins/umeditor/lang/en/images/localimage.png new file mode 100644 index 0000000000000000000000000000000000000000..12c8e6aefa8fd16287ac77bbecd7d5b58c3fc837 GIT binary patch literal 3083 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 Z1^`gxH94g;riuUn002ovPDHLkV1oEm+S&jB literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/music.png b/web/assets/common/plugins/umeditor/lang/en/images/music.png new file mode 100644 index 0000000000000000000000000000000000000000..2f495fe92ffecdce42b74fd57dca2687414711fb GIT binary patch literal 91561 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 LNkvXXu0mjf3l)h% literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/rotateleftdisable.png b/web/assets/common/plugins/umeditor/lang/en/images/rotateleftdisable.png new file mode 100644 index 0000000000000000000000000000000000000000..741526e0d5e6eb5c30eb0a62c9b1d6d558ed9cdf GIT binary patch literal 719 zcmV;=0xVLm}k=YQ<2%m+GNILKMZ4Wj)Eo{6dA(i=1OgO`#k70DYI-BmRjYes)SkU2cI0GZBR;y4N3I~Hh!d`9*7LUg-fQ!ZA17DgvC%W<{o)0F! zU*`EmuKGZYN~Kcs0v3uoolcs~W&&`zTnccgjDVT|^mshtI#kAIIINifC7$1T{?yd~ z`f398$nzJ^&xXUj%I~;@1tb~>YHqh%0jWR{cz-&b2!6()Y&M(t%#{JqZzlB@o&~uo zRdFYN7<*g3d>y0SFTSsu}||KmrzxrzXV}0f4+Nl`U`wo^<=3%s|h|0ur}Z0m~6a z)9IA5+3a~Uyo=N{O{`Z26ni!(U6x=a31615Uq6I}2cZ-|0a2}1X+EF7+lbqaCbL*9 zVjpaCBO8zeF31Bljb43c1B|5gdMy+K1i&JZ2&owiAH&aRFjxU~zu$k$sq`Wh)qB?* z7JdW}ya?6MP#9CN2390Mab0C5lljw}edEbwzR6l%S5ujn*P2rK!bg5RKf!pHvr45x zq@5Lg56Rlp|dF?<); zZnrB`Ycv`HPI*-K+S{`L4RzyPXZYcl&002ovPDHLkV1lL= BGwT2V literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/rotateleftenable.png b/web/assets/common/plugins/umeditor/lang/en/images/rotateleftenable.png new file mode 100644 index 0000000000000000000000000000000000000000..e164ddbd62a232f3a89826158c9795f6c082cc89 GIT binary patch literal 952 zcmV;p14sOcP)B{+A~4pJXZ}t_3ogEQ=EtDt@NW=ovC^1YY3D0f#y*iY)MG5<8F z!|f{@kKz7^5#8k3NZf?c9K_t5iFB?APcn|7{XXn#Ybhtak0o(qW)4fuAr!0vET4{< zSc+h47GQ*Qo%FCG#zDtxKn;d}ii3LDciA+S|Xdhl! zB4Tth6pKaVi#F1YAvCEHTFJM^bUHnP0sSYFPoVrcDQm)w^;g8!)bGBFx|JW?%CGA- z&{PEjJ5@v`9|31h11Z`(8l4W74acX!rYt{M#7D#XGmFrJ8p1{(Q)@sv8@F2OUzvR1 zx9$d0kvWXM{tBEwN0$R4YNDW*#-^%Df&-Q6zoW#Uu@ZDmhS%$@-$mxZRD^B@1aSM_ zhL@++3|_o?ixNDPXhWAA>})fj>w5hzGPAR@B4aE>&p+Mbf*>$imMuk5EW2c0000?hzPCHlRk++nh`E3!rKF3htnTQ@ z$gI|MU8lEVHuFTg-PR|Q2`!gP+H5wYX&N~k4)SU74-bY78_Ii7=yw!+~9z)9EC?-%tI1pNtpOgTWwx zYc~hmYPIxUug9z{77N<%_sk-|f_a_y+;xqv4+oe^KHq!n0!Sli2@LjQ?eJI-PQw!5kz2kP;{i1#vW)J3m7@ilvOQx*+0gD21yWM29TG_#>*)V5x3eEte0rs!7XTfq- zNpDVBe^HqJA^?xgte(JHEEaB201ADoR;^a);o$)-i`fNGNCD$l*jUkV&ZOJ#g6-=H z48;P#aDbi7X5{nvH~?d$9K9Fo01LT5x392A!Cv-)3}9cydREAx^dDF>`_*d2002m8 z7y1N?u~>`?4DAa45ev4jw7ek*Sw2XA4L&zdS z9A4&}t`&psbm3W({))<)8k1Mzq-LMgHh4P0I-QOV5C8-H!ohO6T&8$D&Va$Z(UY0s zOeS;lL%|{kl3A%;S#MlC&Amg&cpiN1r-rvC| zuQ1Fer=aUPn>g6re+%A~4HxgfoyFs_jfT}hvT2|~buTAFG7cgsM5)fELnz*RfYsA+ z%+JjZfW5eCXjGl)8+s)4P|lCQu*F>n-SXLP{rpv`@{BNE{IcsI8B@_npSvh)X*hNpW_TLgr2`Ju@0bJqvCruO={o6E(Z3`5{jw@Hv`M-m z=H><1m4WE;;`c%mkz)x|ObZEnAD_J*B%s8ej2PcmwhgRZado>%)jtid7zm=SE zQ*F%`{_{RP6wdfheSj59}E|h^E{{P*E_)R6H^3NW^13gC8C>dV0CgKv@POF4dxfvY6kSFwxaNHki&!>3-Y^~4kn%F>oC=IgN4BfYTUZZWo z4NgH0Fff*Y6!1`OQEtkV%qBhmGZEnCEqwT{hG--_gp%$=?(C?C;zb^jm66PHQ4|^R zkb*cmi1NcmKm&YfVi#crNDyF;_V&?5G literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/en/images/upload.png b/web/assets/common/plugins/umeditor/lang/en/images/upload.png new file mode 100644 index 0000000000000000000000000000000000000000..7bb15b3d6d6799504cf7093a1600bd7ece0d9ef5 GIT binary patch literal 3941 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 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/zh-cn/images/copy.png b/web/assets/common/plugins/umeditor/lang/zh-cn/images/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..b2536aac72e763b9a872b507462458ecb96990f0 GIT binary patch literal 4319 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 N002ovPDHLkV1nv#UQ7T0 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/lang/zh-cn/images/imglabel.png b/web/assets/common/plugins/umeditor/lang/zh-cn/images/imglabel.png new file mode 100644 index 0000000000000000000000000000000000000000..4fc3c888cbdb2bc73f095a49c351dca6d15a186b GIT binary patch literal 2973 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 O0000DSr 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 F06XDw-}wLl literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/themes/default/images/icons.png b/web/assets/common/plugins/umeditor/themes/default/images/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..de9a1bb2bd6b0da151b87771792c79b2c8a69188 GIT binary patch literal 41374 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 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/themes/default/images/pop-bg.png b/web/assets/common/plugins/umeditor/themes/default/images/pop-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a5a30149adf68e9e19df5a467de535a99434da GIT binary patch literal 1003 zcmbVLO>dMy7^a#urb%Pcn?{oe0SV)?XWz+|Al!_T;fRg;kFAKNQrKXm zI?Xv-5Vp4KJdBPB0~2zTWVZPG%Qq1uu`M2GeWjm!WSZ=p7i9l@cNm=?M`kQ;KLlH~ z#RXErLQtnCS!va_IMcQGx;d6bFoUpTTbw&J>hA%M76fRL+KChhfv!t%Q`2?$03Zb- zS>d+(0s-#MlXmKP%(=;0mggPA2DbF$% z)}5@}XcaIiqaw*!LNm}Ph7)?qY>|694ORhLR$uX%p8BU9tiB z>&BJna(I>#c|b~fT131ahZ`-K=k9t#4Ul_dJt-1il<)-8=rko6>tS2uf225xEf1*> zxmplthVCN>T~7(H(>0LcyIoT;T8*o?I(AT?V9ivtE`&ZZR0A7sps0a~yS`_(++J3) zFpEfQH{o^{+|6sbmRFFF(PBvHN!tN?Q_5&Lr8)5SHE@t*F|Ep`IP+LBDU#r*HDqhqn>l)|H+ulospZ?_@`CcBi?__^0M*y z=5lmM#^z|W&Jbp8|xG}z`O@CZGeHnamgr#NSJzjeIbe3> literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/themes/default/images/spacer.gif b/web/assets/common/plugins/umeditor/themes/default/images/spacer.gif new file mode 100644 index 0000000000000000000000000000000000000000..5bfd67a2d6f72ac3a55cbfcea5866e841d22f5d9 GIT binary patch literal 43 mcmZ?wbhEHbWMp7uXkdT>#h)yUAf^t80Ld^gF}W}@SOWlZ0R#L1 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/themes/default/images/videologo.gif b/web/assets/common/plugins/umeditor/themes/default/images/videologo.gif new file mode 100644 index 0000000000000000000000000000000000000000..555af7417d797f962b4990f437a9330b8caf59a4 GIT binary patch literal 1604 zcmdUui#O8?0LOpR^icOEaZ9h0c@`?2(&MTlT{RaS*KvhP-a|&oD=ir^%wrgZVT?Rx z^V&S}+E!yT6WToXKr^qJ5%Vr?{TcUjzW>1IobTth)ve1{+;xF-z$zX1$@cbk;N#jz zM8mU4YCupmEU?Nii0lQg^bRCan?Ab3%VAH658-8xBFJMB`IuzR@?X?_cxg~njWe8Z z7he1{t~NY@;sD2oqwB>InLWHP3|(&rFL;_%YXisG!f|)t`LI~xqj*wKLbXWp)f$eo z4#asOE4^bXVR1zDClZ^-`L@9YE{GEUM6w@>B$|*3C13A^6xxLq**_!Pdr{&MM+{D{ z56z@TzG*@hw6S^P4v136mt~*C)1y;z@#LIHDjO7zE0$NBVk(?tEAPD`x}r!4rSvo+ zGrN|Z-`M*gk?fXS+qo0;d0 zh<&hBzt>Fxc`eUN=*S8tip(N)4%c-Hsa$abS1kGZZD3dwRL~Y&OiQls$!Zy(aK#~| zjPP=1B(V$A*qhVABln1FxZ*mlSSnXYWQzzgJF1owUC)i7_7-#saE!r1=5SG$fWQ*I z?G_O^BB^|7ZhisN&MW7LKFbwbTU$SV{P_O;`{w56#>U3_`uf`1+Un}+%F2pDp;%g4 zT3lS5pP!e@<#Tg$GMVhlmoKxkvokX@)6>(Flao@ZbYfycB9V-bkB^RyibNuzP$&=x zMn*=4hK2?Q2l;$HkH;Gr80hQk>+S93a=9E1r@OnG&1QFYb~2ev27}Sj(b3-C-qzOE z+}zyQ*hr;PDHIBkNX*O2%g)YDPEJltOhlnjzdrQ8MQa@l0suN-^vmE^6Tt3mAau_$ zXbql@q{N)M&$25b1FG78WjFA2Q`{wpcH+plH-T+lc4H{-V^L{To}?sY^7t- zRp-#0EC|Fh>5nV9*osg354U2y3FFpwXQ!`Na;JKQ;uHrYmb=qU|MBr zTOt(Y=@VUlJq+6mQt^WJ9lIJTz?$6jvB@_0Ijg#xyHYhx6QE$9@Q;P`SNru{MQcx_ z>I9eqMm;}}tfJ*J&?Udq;ijL1AIs^TNj%ED8++(4bqQ3YK5><%~obS)3oEv z2`L9EwBilZbt=4c48b+h%ydj29TQf1;FclAt3p@D@Iv$C>k|Ryx={((b>+iEv*M}T zw2%>2(|!*aqioP7@6#WGj~%z23qkbWx? zq>K{ydFDHsgH&Ab#e8p|DBB+i&e7YjgH&Dp_Cc?zbGzF=vu-e#1^&&3UitP6Qvg=W z;+>M!e1J%3O7HX-hEcUA+~cC@8WAyj^bml>?&AT`HEEBQ}9Z=3X$i*Zty)I zU*Zj5^tvjIuB=l9Z9~T)&b;Eg=FK&TO>3g*yVOqt8d@&{JXvVbxz7~cYII$TQHvSS zgJ#LpO3a#6>HScB5@i5vKw}cnyiI*AUkCSPz7s6};*mIoCN(8C-gssBWz<}Twr?3} zMG(-KVyeCT&UjVicgg-NE@GN=DKAW@1zjD3zqggT>WPOqXK6a*HA(U%`Q-^?%QNdz M>|NJq=D?r-0Tclc1poj5 literal 0 HcmV?d00001 diff --git a/web/assets/common/plugins/umeditor/umeditor.config.js b/web/assets/common/plugins/umeditor/umeditor.config.js new file mode 100644 index 0000000..9f0c64d --- /dev/null +++ b/web/assets/common/plugins/umeditor/umeditor.config.js @@ -0,0 +1,255 @@ +/** + * umeditor完整配置项 + * 可以在这里配置整个编辑器的特性 + */ +/**************************提示******************************** + * 所有被注释的配置项均为UEditor默认值。 + * 修改默认配置请首先确保已经完全明确该参数的真实用途。 + * 主要有两种修改方案,一种是取消此处注释,然后修改成对应参数;另一种是在实例化编辑器时传入对应参数。 + * 当升级编辑器时,可直接使用旧版配置文件替换新版配置文件,不用担心旧版配置文件中因缺少新功能所需的参数而导致脚本报错。 + **************************提示********************************/ + + +(function () { + /** + * 编辑器资源文件根路径。它所表示的含义是:以编辑器实例化页面为当前路径,指向编辑器资源文件(即dialog等文件夹)的路径。 + * 鉴于很多同学在使用编辑器的时候出现的种种路径问题,此处强烈建议大家使用"相对于网站根目录的相对路径"进行配置。 + * "相对于网站根目录的相对路径"也就是以斜杠开头的形如"/myProject/umeditor/"这样的路径。 + * 如果站点中有多个不在同一层级的页面需要实例化编辑器,且引用了同一UEditor的时候,此处的URL可能不适用于每个页面的编辑器。 + * 因此,UEditor提供了针对不同页面的编辑器可单独配置的根路径,具体来说,在需要实例化编辑器的页面最顶部写上如下代码即可。当然,需要令此处的URL等于对应的配置。 + * window.UMEDITOR_HOME_URL = "/xxxx/xxxx/"; + */ + var URL = window.UMEDITOR_HOME_URL || (function () { + + function PathStack() { + + this.documentURL = self.document.URL || self.location.href; + + this.separator = '/'; + this.separatorPattern = /\\|\//g; + this.currentDir = './'; + this.currentDirPattern = /^[.]\/]/; + + this.path = this.documentURL; + this.stack = []; + + this.push(this.documentURL); + + } + + PathStack.isParentPath = function (path) { + return path === '..'; + }; + + PathStack.hasProtocol = function (path) { + return !!PathStack.getProtocol(path); + }; + + PathStack.getProtocol = function (path) { + + var protocol = /^[^:]*:\/*/.exec(path); + + return protocol ? protocol[0] : null; + + }; + + PathStack.prototype = { + push: function (path) { + + this.path = path; + + update.call(this); + parse.call(this); + + return this; + + }, + getPath: function () { + return this + ""; + }, + toString: function () { + return this.protocol + (this.stack.concat([''])).join(this.separator); + } + }; + + function update() { + + var protocol = PathStack.getProtocol(this.path || ''); + + if (protocol) { + + //根协议 + this.protocol = protocol; + + //local + this.localSeparator = /\\|\//.exec(this.path.replace(protocol, ''))[0]; + + this.stack = []; + } else { + protocol = /\\|\//.exec(this.path); + protocol && (this.localSeparator = protocol[0]); + } + + } + + function parse() { + + var parsedStack = this.path.replace(this.currentDirPattern, ''); + + if (PathStack.hasProtocol(this.path)) { + parsedStack = parsedStack.replace(this.protocol, ''); + } + + parsedStack = parsedStack.split(this.localSeparator); + parsedStack.length = parsedStack.length - 1; + + for (var i = 0, tempPath, l = parsedStack.length, root = this.stack; i < l; i++) { + tempPath = parsedStack[i]; + if (tempPath) { + if (PathStack.isParentPath(tempPath)) { + root.pop(); + } else { + root.push(tempPath); + } + } + + } + + + } + + var currentPath = document.getElementsByTagName('script'); + + currentPath = currentPath[currentPath.length - 1].src; + + return new PathStack().push(currentPath) + ""; + + + })(); + + /** + * 配置项主体。注意,此处所有涉及到路径的配置别遗漏URL变量。 + */ + window.UMEDITOR_CONFIG = { + + //为编辑器实例添加一个路径,这个不能被注释 + UMEDITOR_HOME_URL: URL + + //图片上传配置区 + , imageUrl: STORE_URL + "/upload/image" //图片上传提交地址 + , imagePath: URL + "php/" //图片修正地址,引用了fixedImagePath,如有特殊需求,可自行配置 + , imageFieldName: "iFile" //图片数据的key,若此处修改,需要在后台对应文件修改对应参数 + + + //工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的从新定义 + // ,toolbar:[ + // 'source | undo redo | bold italic underline strikethrough | superscript subscript | forecolor backcolor | removeformat |', + // 'insertorderedlist insertunorderedlist | selectall cleardoc paragraph | fontfamily fontsize' , + // '| justifyleft justifycenter justifyright justifyjustify |', + // 'link unlink | emotion image video | map', + // '| horizontal print preview fullscreen', 'drafts', 'formula' + // ] + , toolbar: [ + 'source | undo redo | bold italic underline strikethrough | superscript subscript | forecolor backcolor | removeformat |', + 'insertorderedlist insertunorderedlist | selectall paragraph | fontfamily fontsize', + '| justifyleft justifycenter justifyright justifyjustify ', + '| image insertimage video', + '| horizontal' + ] + + //语言配置项,默认是zh-cn。有需要的话也可以使用如下这样的方式来自动多语言切换,当然,前提条件是lang文件夹下存在对应的语言文件: + //lang值也可以通过自动获取 (navigator.language||navigator.browserLanguage ||navigator.userLanguage).toLowerCase() + //,lang:"zh-cn" + //,langPath:URL +"lang/" + + //ie下的链接自动监测 + //,autourldetectinie:false + + //主题配置项,默认是default。有需要的话也可以使用如下这样的方式来自动多主题切换,当然,前提条件是themes文件夹下存在对应的主题文件: + //现有如下皮肤:default + //,theme:'default' + //,themePath:URL +"themes/" + + + // 针对getAllHtml方法,会在对应的head标签中增加该编码设置。 + ,charset:"utf-8" + + //常用配置项目 + //,isShow : true //默认显示编辑器 + + //,initialContent:'欢迎使用UMEDITOR!' //初始化编辑器的内容,也可以通过textarea/script给值,看官网例子 + + //,initialFrameWidth:500 //初始化编辑器宽度,默认500 + //,initialFrameHeight:500 //初始化编辑器高度,默认500 + + //,autoClearinitialContent:true //是否自动清除编辑器初始内容,注意:如果focus属性设置为true,这个也为真,那么编辑器一上来就会触发导致初始化的内容看不到了 + + //,textarea:'editorValue' // 提交表单时,服务器获取编辑器提交内容的所用的参数,多实例时可以给容器name属性,会将name给定的值最为每个实例的键值,不用每次实例化的时候都设置这个值 + + //,focus:false //初始化时,是否让编辑器获得焦点true或false + + //,autoClearEmptyNode : true //getContent时,是否删除空的inlineElement节点(包括嵌套的情况) + + //,fullscreen : false //是否开启初始化时即全屏,默认关闭 + + //,readonly : false //编辑器初始化结束后,编辑区域是否是只读的,默认是false + + //,zIndex : 900 //编辑器层级的基数,默认是900 + + //如果自定义,最好给p标签如下的行高,要不输入中文时,会有跳动感 + //注意这里添加的样式,最好放在.edui-editor-body .edui-body-container这两个的下边,防止跟页面上css冲突 + //,initialStyle:'.edui-editor-body .edui-body-container p{line-height:1em}' + + //,autoSyncData:true //自动同步编辑器要提交的数据 + + //,emotionLocalization:false //是否开启表情本地化,默认关闭。若要开启请确保emotion文件夹下包含官网提供的images表情文件夹 + + //,allHtmlEnabled:false //提交到后台的数据是否包含整个html字符串 + + //fontfamily + //字体设置 +// ,'fontfamily':[ +// { name: 'songti', val: '宋体,SimSun'}, +// ] + + //fontsize + //字号 + //,'fontsize':[10, 11, 12, 14, 16, 18, 20, 24, 36] + + //paragraph + //段落格式 值留空时支持多语言自动识别,若配置,则以配置值为准 + //,'paragraph':{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''} + + //undo + //可以最多回退的次数,默认20 + //,maxUndoCount:20 + //当输入的字符数超过该值时,保存一次现场 + //,maxInputCount:1 + + //imageScaleEnabled + // 是否允许点击文件拖拽改变大小,默认true + ,imageScaleEnabled: false + + //dropFileEnabled + // 是否允许拖放图片到编辑区域,上传并插入,默认true + //,dropFileEnabled:true + + //pasteImageEnabled + // 是否允许粘贴QQ截屏,上传并插入,默认true + //,pasteImageEnabled:true + + //autoHeightEnabled + // 是否自动长高,默认true + ,autoHeightEnabled: false + + //autoFloatEnabled + //是否保持toolbar的位置不动,默认true + , autoFloatEnabled: false + + //浮动时工具栏距离浏览器顶部的高度,用于某些具有固定头部的页面 + , topOffset: 51 + + //填写过滤规则 + //,filterRules: {} + }; +})(); diff --git a/web/assets/common/plugins/umeditor/umeditor.js b/web/assets/common/plugins/umeditor/umeditor.js new file mode 100644 index 0000000..ce2251a --- /dev/null +++ b/web/assets/common/plugins/umeditor/umeditor.js @@ -0,0 +1,10958 @@ +/*! + * UEditor Mini版本 + * version: 1.2.2 + * build: Wed Mar 19 2014 17:14:14 GMT+0800 (中国标准时间) + */ + +(function($){ + + UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {}; + + window.UM = { + plugins : {}, + + commands : {}, + + I18N : {}, + + version : "1.2.2" + }; + + var dom = UM.dom = {}; + /** + * 浏览器判断模块 + * @file + * @module UE.browser + * @since 1.2.6.1 + */ + + /** + * 提供浏览器检测的模块 + * @unfile + * @module UE.browser + */ + var browser = UM.browser = function(){ + var agent = navigator.userAgent.toLowerCase(), + opera = window.opera, + browser = { + /** + * @property {boolean} ie 检测当前浏览器是否为IE + * @example + * ```javascript + * if ( UE.browser.ie ) { + * console.log( '当前浏览器是IE' ); + * } + * ``` + */ + ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent), + + /** + * @property {boolean} opera 检测当前浏览器是否为Opera + * @example + * ```javascript + * if ( UE.browser.opera ) { + * console.log( '当前浏览器是Opera' ); + * } + * ``` + */ + opera : ( !!opera && opera.version ), + + /** + * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器 + * @example + * ```javascript + * if ( UE.browser.webkit ) { + * console.log( '当前浏览器是webkit内核浏览器' ); + * } + * ``` + */ + webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ), + + /** + * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下 + * @example + * ```javascript + * if ( UE.browser.mac ) { + * console.log( '当前浏览器运行在mac平台下' ); + * } + * ``` + */ + mac : ( agent.indexOf( 'macintosh' ) > -1 ), + + /** + * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下 + * @example + * ```javascript + * if ( UE.browser.quirks ) { + * console.log( '当前浏览器运行处于“怪异模式”' ); + * } + * ``` + */ + quirks : ( document.compatMode == 'BackCompat' ) + }; + + /** + * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核 + * @example + * ```javascript + * if ( UE.browser.gecko ) { + * console.log( '当前浏览器内核是gecko内核' ); + * } + * ``` + */ + browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie); + + var version = 0; + + // Internet Explorer 6.0+ + if ( browser.ie ){ + + + var v1 = agent.match(/(?:msie\s([\w.]+))/); + var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); + if(v1 && v2 && v1[1] && v2[1]){ + version = Math.max(v1[1]*1,v2[1]*1); + }else if(v1 && v1[1]){ + version = v1[1]*1; + }else if(v2 && v2[1]){ + version = v2[1]*1; + }else{ + version = 0; + } + + browser.ie11Compat = document.documentMode == 11; + /** + * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie9Compat ) { + * console.log( '当前浏览器运行在IE9兼容模式下' ); + * } + * ``` + */ + browser.ie9Compat = document.documentMode == 9; + + /** + * @property { boolean } ie8 检测浏览器是否是IE8浏览器 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie8 ) { + * console.log( '当前浏览器是IE8浏览器' ); + * } + * ``` + */ + browser.ie8 = !!document.documentMode; + + /** + * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie8Compat ) { + * console.log( '当前浏览器运行在IE8兼容模式下' ); + * } + * ``` + */ + browser.ie8Compat = document.documentMode == 8; + + /** + * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie7Compat ) { + * console.log( '当前浏览器运行在IE7兼容模式下' ); + * } + * ``` + */ + browser.ie7Compat = ( ( version == 7 && !document.documentMode ) + || document.documentMode == 7 ); + + /** + * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式 + * @warning 如果浏览器不是IE, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.ie6Compat ) { + * console.log( '当前浏览器运行在IE6模式或者怪异模式下' ); + * } + * ``` + */ + browser.ie6Compat = ( version < 7 || browser.quirks ); + + browser.ie9above = version > 8; + + browser.ie9below = version < 9; + + } + + // Gecko. + if ( browser.gecko ){ + var geckoRelease = agent.match( /rv:([\d\.]+)/ ); + if ( geckoRelease ) + { + geckoRelease = geckoRelease[1].split( '.' ); + version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1; + } + } + + /** + * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号 + * @warning 如果浏览器不是chrome, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.chrome ) { + * console.log( '当前浏览器是Chrome' ); + * } + * ``` + */ + if (/chrome\/(\d+\.\d)/i.test(agent)) { + browser.chrome = + RegExp['\x241']; + } + + /** + * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号 + * @warning 如果浏览器不是safari, 则该值为undefined + * @example + * ```javascript + * if ( UE.browser.safari ) { + * console.log( '当前浏览器是Safari' ); + * } + * ``` + */ + if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){ + browser.safari = + (RegExp['\x241'] || RegExp['\x242']); + } + + + // Opera 9.50+ + if ( browser.opera ) + version = parseFloat( opera.version() ); + + // WebKit 522+ (Safari 3+) + if ( browser.webkit ) + version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] ); + + /** + * @property { Number } version 检测当前浏览器版本号 + * @remind + *

            + *
          • IE系列返回值为5,6,7,8,9,10等
          • + *
          • gecko系列会返回10900,158900等
          • + *
          • webkit系列会返回其build号 (如 522等)
          • + *
          + * @example + * ```javascript + * console.log( '当前浏览器版本号是: ' + UE.browser.version ); + * ``` + */ + browser.version = version; + + /** + * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容 + * @example + * ```javascript + * if ( UE.browser.isCompatible ) { + * console.log( '浏览器与UEditor能够良好兼容' ); + * } + * ``` + */ + browser.isCompatible = + !browser.mobile && ( + ( browser.ie && version >= 6 ) || + ( browser.gecko && version >= 10801 ) || + ( browser.opera && version >= 9.5 ) || + ( browser.air && version >= 1 ) || + ( browser.webkit && version >= 522 ) || + false ); + return browser; + }(); +//快捷方式 + var ie = browser.ie, + webkit = browser.webkit, + gecko = browser.gecko, + opera = browser.opera; + /** + * @file + * @name UM.Utils + * @short Utils + * @desc UEditor封装使用的静态工具函数 + * @import editor.js + */ + var utils = UM.utils = { + /** + * 遍历数组,对象,nodeList + * @name each + * @grammar UM.utils.each(obj,iterator,[context]) + * @since 1.2.4+ + * @desc + * * obj 要遍历的对象 + * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj + * * context iterator的上下文 + * @example + * UM.utils.each([1,2],function(v,i){ + * console.log(v)//值 + * console.log(i)//索引 + * }) + * UM.utils.each(document.getElementsByTagName('*'),function(n){ + * console.log(n.tagName) + * }) + */ + each : function(obj, iterator, context) { + if (obj == null) return; + if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if(iterator.call(context, obj[i], i, obj) === false) + return false; + } + } else { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if(iterator.call(context, obj[key], key, obj) === false) + return false; + } + } + } + }, + + makeInstance:function (obj) { + var noop = new Function(); + noop.prototype = obj; + obj = new noop; + noop.prototype = null; + return obj; + }, + /** + * 将source对象中的属性扩展到target对象上 + * @name extend + * @grammar UM.utils.extend(target,source) => Object //覆盖扩展 + * @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展 + */ + extend:function (t, s, b) { + if (s) { + for (var k in s) { + if (!b || !t.hasOwnProperty(k)) { + t[k] = s[k]; + } + } + } + return t; + }, + extend2:function (t) { + var a = arguments; + for (var i = 1; i < a.length; i++) { + var x = a[i]; + for (var k in x) { + if (!t.hasOwnProperty(k)) { + t[k] = x[k]; + } + } + } + return t; + }, + /** + * 模拟继承机制,subClass继承superClass + * @name inherits + * @grammar UM.utils.inherits(subClass,superClass) => subClass + * @example + * function SuperClass(){ + * this.name = "小李"; + * } + * SuperClass.prototype = { + * hello:function(str){ + * console.log(this.name + str); + * } + * } + * function SubClass(){ + * this.name = "小张"; + * } + * UM.utils.inherits(SubClass,SuperClass); + * var sub = new SubClass(); + * sub.hello("早上好!"); ==> "小张早上好!" + */ + inherits:function (subClass, superClass) { + var oldP = subClass.prototype, + newP = utils.makeInstance(superClass.prototype); + utils.extend(newP, oldP, true); + subClass.prototype = newP; + return (newP.constructor = subClass); + }, + + /** + * 用指定的context作为fn上下文,也就是this + * @name bind + * @grammar UM.utils.bind(fn,context) => fn + */ + bind:function (fn, context) { + return function () { + return fn.apply(context, arguments); + }; + }, + + /** + * 创建延迟delay执行的函数fn + * @name defer + * @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn + * @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn + * @example + * function test(){ + * console.log("延迟输出!"); + * } + * //非互斥延迟执行 + * var testDefer = UM.utils.defer(test,1000); + * testDefer(); => "延迟输出!"; + * testDefer(); => "延迟输出!"; + * //互斥延迟执行 + * var testDefer1 = UM.utils.defer(test,1000,true); + * testDefer1(); => //本次不执行 + * testDefer1(); => "延迟输出!"; + */ + defer:function (fn, delay, exclusion) { + var timerID; + return function () { + if (exclusion) { + clearTimeout(timerID); + } + timerID = setTimeout(fn, delay); + }; + }, + + /** + * 查找元素item在数组array中的索引, 若找不到返回-1 + * @name indexOf + * @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索 + * @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置 + */ + indexOf:function (array, item, start) { + var index = -1; + start = this.isNumber(start) ? start : 0; + this.each(array, function (v, i) { + if (i >= start && v === item) { + index = i; + return false; + } + }); + return index; + }, + + /** + * 移除数组array中的元素item + * @name removeItem + * @grammar UM.utils.removeItem(array,item) + */ + removeItem:function (array, item) { + for (var i = 0, l = array.length; i < l; i++) { + if (array[i] === item) { + array.splice(i, 1); + i--; + } + } + }, + + /** + * 删除字符串str的首尾空格 + * @name trim + * @grammar UM.utils.trim(str) => String + */ + trim:function (str) { + return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, ''); + }, + + /** + * 将字符串list(以','分隔)或者数组list转成哈希对象 + * @name listToMap + * @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1} + */ + listToMap:function (list) { + if (!list)return {}; + list = utils.isArray(list) ? list : list.split(','); + for (var i = 0, ci, obj = {}; ci = list[i++];) { + obj[ci.toUpperCase()] = obj[ci] = 1; + } + return obj; + }, + + /** + * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符 + * @name unhtml + * @grammar UM.utils.unhtml(str); => String + * @grammar UM.utils.unhtml(str,reg) => String + * @example + * var html = 'You say:"你好!Baidu & UEditor!"'; + * UM.utils.unhtml(html); ==> <body>You say:"你好!Baidu & UEditor!"</body> + * UM.utils.unhtml(html,/[<>]/g) ==> <body>You say:"你好!Baidu & UEditor!"</body> + */ + unhtml:function (str, reg) { + return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) { + if (b) { + return a; + } else { + return { + '<':'<', + '&':'&', + '"':'"', + '>':'>', + "'":''' + }[a] + } + + }) : ''; + }, + /** + * 将str中的转义字符还原成html字符 + * @name html + * @grammar UM.utils.html(str) => String //详细参见
          unhtml + */ + html:function (str) { + return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) { + return { + '<':'<', + '&':'&', + '"':'"', + '>':'>', + ''':"'" + }[m] + }) : ''; + }, + /** + * 将css样式转换为驼峰的形式。如font-size => fontSize + * @name cssStyleToDomStyle + * @grammar UM.utils.cssStyleToDomStyle(cssName) => String + */ + cssStyleToDomStyle:function () { + var test = document.createElement('div').style, + cache = { + 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float' + }; + + return function (cssName) { + return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) { + return match.charAt(1).toUpperCase(); + })); + }; + }(), + /** + * 动态加载文件到doc中,并依据obj来设置属性,加载成功后执行回调函数fn + * @name loadFile + * @grammar UM.utils.loadFile(doc,obj) + * @grammar UM.utils.loadFile(doc,obj,fn) + * @example + * //指定加载到当前document中一个script文件,加载成功后执行function + * utils.loadFile( document, { + * src:"test.js", + * tag:"script", + * type:"text/javascript", + * defer:"defer" + * }, function () { + * console.log('加载成功!') + * }); + */ + loadFile:function () { + var tmpList = []; + + function getItem(doc, obj) { + try { + for (var i = 0, ci; ci = tmpList[i++];) { + if (ci.doc === doc && ci.url == (obj.src || obj.href)) { + return ci; + } + } + } catch (e) { + return null; + } + + } + + return function (doc, obj, fn) { + var item = getItem(doc, obj); + if (item) { + if (item.ready) { + fn && fn(); + } else { + item.funs.push(fn) + } + return; + } + tmpList.push({ + doc:doc, + url:obj.src || obj.href, + funs:[fn] + }); + if (!doc.body) { + var html = []; + for (var p in obj) { + if (p == 'tag')continue; + html.push(p + '="' + obj[p] + '"') + } + doc.write('<' + obj.tag + ' ' + html.join(' ') + ' >'); + return; + } + if (obj.id && doc.getElementById(obj.id)) { + return; + } + var element = doc.createElement(obj.tag); + delete obj.tag; + for (var p in obj) { + element.setAttribute(p, obj[p]); + } + element.onload = element.onreadystatechange = function () { + if (!this.readyState || /loaded|complete/.test(this.readyState)) { + item = getItem(doc, obj); + if (item.funs.length > 0) { + item.ready = 1; + for (var fi; fi = item.funs.pop();) { + fi(); + } + } + element.onload = element.onreadystatechange = null; + } + }; + element.onerror = function () { + throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file umeditor.config.js ') + }; + doc.getElementsByTagName("head")[0].appendChild(element); + } + }(), + /** + * 判断obj对象是否为空 + * @name isEmptyObject + * @grammar UM.utils.isEmptyObject(obj) => true|false + * @example + * UM.utils.isEmptyObject({}) ==>true + * UM.utils.isEmptyObject([]) ==>true + * UM.utils.isEmptyObject("") ==>true + */ + isEmptyObject:function (obj) { + if (obj == null) return true; + if (this.isArray(obj) || this.isString(obj)) return obj.length === 0; + for (var key in obj) if (obj.hasOwnProperty(key)) return false; + return true; + }, + + /** + * 统一将颜色值使用16进制形式表示 + * @name fixColor + * @grammar UM.utils.fixColor(name,value) => value + * @example + * rgb(255,255,255) => "#ffffff" + */ + fixColor:function (name, value) { + if (/color/i.test(name) && /rgba?/.test(value)) { + var array = value.split(","); + if (array.length > 3) + return ""; + value = "#"; + for (var i = 0, color; color = array[i++];) { + color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16); + value += color.length == 1 ? "0" + color : color; + } + value = value.toUpperCase(); + } + return value; + }, + + /** + * 深度克隆对象,从source到target + * @name clone + * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本 + * @grammar UM.utils.clone(source,target) => target包含了source的所有内容,重名会覆盖 + */ + clone:function (source, target) { + var tmp; + target = target || {}; + for (var i in source) { + if (source.hasOwnProperty(i)) { + tmp = source[i]; + if (typeof tmp == 'object') { + target[i] = utils.isArray(tmp) ? [] : {}; + utils.clone(source[i], target[i]) + } else { + target[i] = tmp; + } + } + } + return target; + }, + /** + * 转换cm/pt到px + * @name transUnitToPx + * @grammar UM.utils.transUnitToPx('20pt') => '27px' + * @grammar UM.utils.transUnitToPx('0pt') => '0' + */ + transUnitToPx:function (val) { + if (!/(pt|cm)/.test(val)) { + return val + } + var unit; + val.replace(/([\d.]+)(\w+)/, function (str, v, u) { + val = v; + unit = u; + }); + switch (unit) { + case 'cm': + val = parseFloat(val) * 25; + break; + case 'pt': + val = Math.round(parseFloat(val) * 96 / 72); + } + return val + (val ? 'px' : ''); + }, + /** + * 动态添加css样式 + * @name cssRule + * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上']) + * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色 + * @grammar UM.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc} + * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色 + */ + cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) { + var indexList, index; + doc = doc || document; + if (doc.indexList) { + indexList = doc.indexList; + } else { + indexList = doc.indexList = {}; + } + var sheetStyle; + if (!indexList[key]) { + if (style === undefined) { + return '' + } + sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length); + indexList[key] = index; + } else { + sheetStyle = doc.styleSheets[indexList[key]]; + } + if (style === undefined) { + return sheetStyle.cssText + } + sheetStyle.cssText = style || '' + } : function (key, style, doc) { + doc = doc || document; + var head = doc.getElementsByTagName('head')[0], node; + if (!(node = doc.getElementById(key))) { + if (style === undefined) { + return '' + } + node = doc.createElement('style'); + node.id = key; + head.appendChild(node) + } + if (style === undefined) { + return node.innerHTML + } + if (style !== '') { + node.innerHTML = style; + } else { + head.removeChild(node) + } + } + + }; + /** + * 判断str是否为字符串 + * @name isString + * @grammar UM.utils.isString(str) => true|false + */ + /** + * 判断array是否为数组 + * @name isArray + * @grammar UM.utils.isArray(obj) => true|false + */ + /** + * 判断obj对象是否为方法 + * @name isFunction + * @grammar UM.utils.isFunction(obj) => true|false + */ + /** + * 判断obj对象是否为数字 + * @name isNumber + * @grammar UM.utils.isNumber(obj) => true|false + */ + utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) { + UM.utils['is' + v] = function (obj) { + return Object.prototype.toString.apply(obj) == '[object ' + v + ']'; + } + }); + /** + * @file + * @name UM.EventBase + * @short EventBase + * @import editor.js,core/utils.js + * @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。 + * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。 + */ + var EventBase = UM.EventBase = function () {}; + + EventBase.prototype = { + /** + * 注册事件监听器 + * @name addListener + * @grammar editor.addListener(types,fn) //types为事件名称,多个可用空格分隔 + * @example + * editor.addListener('selectionchange',function(){ + * console.log("选区已经变化!"); + * }) + * editor.addListener('beforegetcontent aftergetcontent',function(type){ + * if(type == 'beforegetcontent'){ + * //do something + * }else{ + * //do something + * } + * console.log(this.getContent) // this是注册的事件的编辑器实例 + * }) + */ + addListener:function (types, listener) { + types = utils.trim(types).split(' '); + for (var i = 0, ti; ti = types[i++];) { + getListener(this, ti, true).push(listener); + } + }, + /** + * 移除事件监听器 + * @name removeListener + * @grammar editor.removeListener(types,fn) //types为事件名称,多个可用空格分隔 + * @example + * //changeCallback为方法体 + * editor.removeListener("selectionchange",changeCallback); + */ + removeListener:function (types, listener) { + types = utils.trim(types).split(' '); + for (var i = 0, ti; ti = types[i++];) { + utils.removeItem(getListener(this, ti) || [], listener); + } + }, + /** + * 触发事件 + * @name fireEvent + * @grammar editor.fireEvent(types) //types为事件名称,多个可用空格分隔 + * @example + * editor.fireEvent("selectionchange"); + */ + fireEvent:function () { + var types = arguments[0]; + types = utils.trim(types).split(' '); + for (var i = 0, ti; ti = types[i++];) { + var listeners = getListener(this, ti), + r, t, k; + if (listeners) { + k = listeners.length; + while (k--) { + if(!listeners[k])continue; + t = listeners[k].apply(this, arguments); + if(t === true){ + return t; + } + if (t !== undefined) { + r = t; + } + } + } + if (t = this['on' + ti.toLowerCase()]) { + r = t.apply(this, arguments); + } + } + return r; + } + }; + /** + * 获得对象所拥有监听类型的所有监听器 + * @public + * @function + * @param {Object} obj 查询监听器的对象 + * @param {String} type 事件类型 + * @param {Boolean} force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组 + * @returns {Array} 监听器数组 + */ + function getListener(obj, type, force) { + var allListeners; + type = type.toLowerCase(); + return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) ) + && ( allListeners[type] || force && ( allListeners[type] = [] ) ) ); + } + + +///import editor.js +///import core/dom/dom.js +///import core/utils.js + /** + * dtd html语义化的体现类 + * @constructor + * @namespace dtd + */ + var dtd = dom.dtd = (function() { + function _( s ) { + for (var k in s) { + s[k.toUpperCase()] = s[k]; + } + return s; + } + var X = utils.extend2; + var A = _({isindex:1,fieldset:1}), + B = _({input:1,button:1,select:1,textarea:1,label:1}), + C = X( _({a:1}), B ), + D = X( {iframe:1}, C ), + E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}), + F = _({ins:1,del:1,script:1,style:1}), + G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ), + H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ), + I = X( _({p:1}), H ), + J = X( _({iframe:1}), H, B ), + K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}), + + L = X( _({a:0}), J ),//a不能被切开,所以把他 + M = _({tr:1}), + N = _({'#':1}), + O = X( _({param:1}), K ), + P = X( _({form:1}), A, D, E, I ), + Q = _({li:1,ol:1,ul:1}), + R = _({style:1,script:1}), + S = _({base:1,link:1,meta:1,title:1}), + T = X( S, R ), + U = _({head:1,body:1}), + V = _({html:1}); + + var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}), + + empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1}); + + return _({ + + // $ 表示自定的属性 + + // body外的元素列表. + $nonBodyContent: X( V, U, S ), + + //块结构元素列表 + $block : block, + + //内联元素列表 + $inline : L, + + $inlineWithA : X(_({a:1}),L), + + $body : X( _({script:1,style:1}), block ), + + $cdata : _({script:1,style:1}), + + //自闭和元素 + $empty : empty, + + //不是自闭合,但不能让range选中里边 + $nonChild : _({iframe:1,textarea:1}), + //列表元素列表 + $listItem : _({dd:1,dt:1,li:1}), + + //列表根元素列表 + $list: _({ul:1,ol:1,dl:1}), + + //不能认为是空的元素 + $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,video:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}), + + //如果没有子节点就可以删除的元素列表,像span,a + $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}), + + $removeEmptyBlock : _({'p':1,'div':1}), + + //在table元素里的元素列表 + $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}), + //不转换的标签 + $notTransContent : _({pre:1,script:1,style:1,textarea:1}), + html: U, + head: T, + style: N, + script: N, + body: P, + base: {}, + link: {}, + meta: {}, + title: N, + col : {}, + tr : _({td:1,th:1}), + img : {}, + embed: {}, + colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}), + noscript : P, + td : P, + br : {}, + th : P, + center : P, + kbd : L, + button : X( I, E ), + basefont : {}, + h5 : L, + h4 : L, + samp : L, + h6 : L, + ol : Q, + h1 : L, + h3 : L, + option : N, + h2 : L, + form : X( A, D, E, I ), + select : _({optgroup:1,option:1}), + font : L, + ins : L, + menu : Q, + abbr : L, + label : L, + table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}), + code : L, + tfoot : M, + cite : L, + li : P, + input : {}, + iframe : P, + strong : L, + textarea : N, + noframes : P, + big : L, + small : L, + //trace: + span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}), + hr : L, + dt : L, + sub : L, + optgroup : _({option:1}), + param : {}, + bdo : L, + 'var' : L, + div : P, + object : O, + sup : L, + dd : P, + strike : L, + area : {}, + dir : Q, + map : X( _({area:1,form:1,p:1}), A, F, E ), + applet : O, + dl : _({dt:1,dd:1}), + del : L, + isindex : {}, + fieldset : X( _({legend:1}), K ), + thead : M, + ul : Q, + acronym : L, + b : L, + a : X( _({a:1}), J ), + blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P), + caption : L, + i : L, + u : L, + tbody : M, + s : L, + address : X( D, I ), + tt : L, + legend : L, + q : L, + pre : X( G, C ), + p : X(_({'a':1}),L), + em :L, + dfn : L + }); + })(); + + /** + * @file + * @name UM.dom.domUtils + * @short DomUtils + * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js + * @desc UEditor封装的底层dom操作库 + */ + + function getDomNode(node, start, ltr, startFromChild, fn, guard) { + var tmpNode = startFromChild && node[start], + parent; + !tmpNode && (tmpNode = node[ltr]); + while (!tmpNode && (parent = (parent || node).parentNode)) { + if (parent.tagName == 'BODY' || guard && !guard(parent)) { + return null; + } + tmpNode = parent[ltr]; + } + if (tmpNode && fn && !fn(tmpNode)) { + return getDomNode(tmpNode, start, ltr, false, fn); + } + return tmpNode; + } + var attrFix = ie && browser.version < 9 ? { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder" + } : { + tabindex: "tabIndex", + readonly: "readOnly" + }, + styleBlock = utils.listToMap([ + '-webkit-box', '-moz-box', 'block' , + 'list-item' , 'table' , 'table-row-group' , + 'table-header-group', 'table-footer-group' , + 'table-row' , 'table-column-group' , 'table-column' , + 'table-cell' , 'table-caption' + ]); + var domUtils = dom.domUtils = { + //节点常量 + NODE_ELEMENT: 1, + NODE_DOCUMENT: 9, + NODE_TEXT: 3, + NODE_COMMENT: 8, + NODE_DOCUMENT_FRAGMENT: 11, + + //位置关系 + POSITION_IDENTICAL: 0, + POSITION_DISCONNECTED: 1, + POSITION_FOLLOWING: 2, + POSITION_PRECEDING: 4, + POSITION_IS_CONTAINED: 8, + POSITION_CONTAINS: 16, + //ie6使用其他的会有一段空白出现 + fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B', + //-------------------------Node部分-------------------------------- + keys: { + /*Backspace*/ 8: 1, /*Delete*/ 46: 1, + /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1, + 37: 1, 38: 1, 39: 1, 40: 1, + 13: 1 /*enter*/ + }, + breakParent:function (node, parent) { + var tmpNode, + parentClone = node, + clone = node, + leftNodes, + rightNodes; + do { + parentClone = parentClone.parentNode; + if (leftNodes) { + tmpNode = parentClone.cloneNode(false); + tmpNode.appendChild(leftNodes); + leftNodes = tmpNode; + tmpNode = parentClone.cloneNode(false); + tmpNode.appendChild(rightNodes); + rightNodes = tmpNode; + } else { + leftNodes = parentClone.cloneNode(false); + rightNodes = leftNodes.cloneNode(false); + } + while (tmpNode = clone.previousSibling) { + leftNodes.insertBefore(tmpNode, leftNodes.firstChild); + } + while (tmpNode = clone.nextSibling) { + rightNodes.appendChild(tmpNode); + } + clone = parentClone; + } while (parent !== parentClone); + tmpNode = parent.parentNode; + tmpNode.insertBefore(leftNodes, parent); + tmpNode.insertBefore(rightNodes, parent); + tmpNode.insertBefore(node, rightNodes); + domUtils.remove(parent); + return node; + }, + trimWhiteTextNode:function (node) { + function remove(dir) { + var child; + while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) { + node.removeChild(child); + } + } + remove('firstChild'); + remove('lastChild'); + }, + /** + * 获取节点A相对于节点B的位置关系 + * @name getPosition + * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB) => Number + * @example + * switch (returnValue) { + * case 0: //相等,同一节点 + * case 1: //无关,节点不相连 + * case 2: //跟随,即节点A头部位于节点B头部的后面 + * case 4: //前置,即节点A头部位于节点B头部的前面 + * case 8: //被包含,即节点A被节点B包含 + * case 10://组合类型,即节点A满足跟随节点B且被节点B包含。实际上,如果被包含,必定跟随,所以returnValue事实上不会存在8的情况。 + * case 16://包含,即节点A包含节点B + * case 20://组合类型,即节点A满足前置节点A且包含节点B。同样,如果包含,必定前置,所以returnValue事实上也不会存在16的情况 + * } + */ + getPosition: function (nodeA, nodeB) { + // 如果两个节点是同一个节点 + if (nodeA === nodeB) { + // domUtils.POSITION_IDENTICAL + return 0; + } + var node, + parentsA = [nodeA], + parentsB = [nodeB]; + node = nodeA; + while (node = node.parentNode) { + // 如果nodeB是nodeA的祖先节点 + if (node === nodeB) { + // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING + return 10; + } + parentsA.push(node); + } + node = nodeB; + while (node = node.parentNode) { + // 如果nodeA是nodeB的祖先节点 + if (node === nodeA) { + // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING + return 20; + } + parentsB.push(node); + } + parentsA.reverse(); + parentsB.reverse(); + if (parentsA[0] !== parentsB[0]) { + // domUtils.POSITION_DISCONNECTED + return 1; + } + var i = -1; + while (i++, parentsA[i] === parentsB[i]) { + } + nodeA = parentsA[i]; + nodeB = parentsB[i]; + while (nodeA = nodeA.nextSibling) { + if (nodeA === nodeB) { + // domUtils.POSITION_PRECEDING + return 4 + } + } + // domUtils.POSITION_FOLLOWING + return 2; + }, + + /** + * 返回节点node在父节点中的索引位置 + * @name getNodeIndex + * @grammar UM.dom.domUtils.getNodeIndex(node) => Number //索引值从0开始 + */ + getNodeIndex: function (node, ignoreTextNode) { + var preNode = node, + i = 0; + while (preNode = preNode.previousSibling) { + if (ignoreTextNode && preNode.nodeType == 3) { + if (preNode.nodeType != preNode.nextSibling.nodeType) { + i++; + } + continue; + } + i++; + } + return i; + }, + + /** + * 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含 + * @name inDoc + * @grammar UM.dom.domUtils.inDoc(node,doc) => true|false + */ + inDoc: function (node, doc) { + return domUtils.getPosition(node, doc) == 10; + }, + /** + * 查找node节点的祖先节点 + * @name findParent + * @grammar UM.dom.domUtils.findParent(node) => Element // 直接返回node节点的父节点 + * @grammar UM.dom.domUtils.findParent(node,filterFn) => Element //filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回 + * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf) => Element //includeSelf指定是否包含自身 + */ + findParent: function (node, filterFn, includeSelf) { + if (node && !domUtils.isBody(node)) { + node = includeSelf ? node : node.parentNode; + while (node) { + if (!filterFn || filterFn(node) || domUtils.isBody(node)) { + return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node; + } + node = node.parentNode; + } + } + return null; + }, + /** + * 通过tagName查找node节点的祖先节点 + * @name findParentByTagName + * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames) => Element //tagNames支持数组,区分大小写 + * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf) => Element //includeSelf指定是否包含自身 + * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn) => Element //excludeFn指定例外过滤条件,返回true时忽略该节点 + */ + findParentByTagName: function (node, tagNames, includeSelf, excludeFn) { + tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]); + return domUtils.findParent(node, function (node) { + return tagNames[node.tagName] && !(excludeFn && excludeFn(node)); + }, includeSelf); + }, + /** + * 查找节点node的祖先节点集合 + * @name findParents + * @grammar UM.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身 + * @grammar UM.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身 + * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取 + * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个 + */ + findParents: function (node, includeSelf, filterFn, closerFirst) { + var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : []; + while (node = domUtils.findParent(node, filterFn)) { + parents.push(node); + } + return closerFirst ? parents : parents.reverse(); + }, + + /** + * 在节点node后面插入新节点newNode + * @name insertAfter + * @grammar UM.dom.domUtils.insertAfter(node,newNode) => newNode + */ + insertAfter: function (node, newNode) { + return node.parentNode.insertBefore(newNode, node.nextSibling); + }, + + /** + * 删除节点node,并根据keepChildren指定是否保留子节点 + * @name remove + * @grammar UM.dom.domUtils.remove(node) => node + * @grammar UM.dom.domUtils.remove(node,keepChildren) => node + */ + remove: function (node, keepChildren) { + + var parent = node.parentNode, + child; + if (parent) { + if (keepChildren && node.hasChildNodes()) { + while (child = node.firstChild) { + parent.insertBefore(child, node); + } + } + parent.removeChild(node); + } + return node; + }, + + + /** + * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点, + * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。 + * @method getNextDomNode + * @param { Node } node 需要获取其后的兄弟节点的节点对象 + * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL + * @example + * ```html + * + *
          + * + *
          + * xxx + * + * + * ``` + * @example + * ```html + * + *
          + * + * xxx + *
          + * xxx + * + * + * ``` + */ + + /** + * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点, + * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false, + * 则执行getNextDomNode(Node node)的查找过程。 + * @method getNextDomNode + * @param { Node } node 需要获取其后的兄弟节点的节点对象 + * @param { Boolean } startFromChild 查找过程是否从其子节点开始 + * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL + * @see UE.dom.domUtils.getNextDomNode(Node) + */ + getNextDomNode:function (node, startFromChild, filterFn, guard) { + return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard); + }, + getPreDomNode:function (node, startFromChild, filterFn, guard) { + return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard); + }, + + /** + * 检测节点node是否属于bookmark节点 + * @name isBookmarkNode + * @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false + */ + isBookmarkNode: function (node) { + return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id); + }, + /** + * 获取节点node所在的window对象 + * @name getWindow + * @grammar UM.dom.domUtils.getWindow(node) => window对象 + */ + getWindow: function (node) { + var doc = node.ownerDocument || node; + return doc.defaultView || doc.parentWindow; + }, + + /** + * 获取离nodeA与nodeB最近的公共的祖先节点 + * @method getCommonAncestor + * @param { Node } nodeA 第一个节点 + * @param { Node } nodeB 第二个节点 + * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。 + * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。 + * @example + * ```javascript + * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild ); + * //output: true + * console.log( commonAncestor.tagName.toLowerCase() === 'body' ); + * ``` + */ + getCommonAncestor:function (nodeA, nodeB) { + if (nodeA === nodeB) + return nodeA; + var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1; + while (parent = parent.parentNode) { + if (parent === nodeB) { + return parent; + } + parentsA.push(parent); + } + parent = nodeB; + while (parent = parent.parentNode) { + if (parent === nodeA) + return parent; + parentsB.push(parent); + } + parentsA.reverse(); + parentsB.reverse(); + while (i++, parentsA[i] === parentsB[i]) { + } + return i == 0 ? null : parentsA[i - 1]; + + }, + /** + * 清除node节点左右连续为空的兄弟inline节点 + * @method clearEmptySibling + * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, + * 则这些兄弟节点将被删除 + * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点 + * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点 + * @example + * ```html + * + *
          + * + * + * + * xxx + * + * + * + * ``` + */ + + /** + * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, + * 则忽略对右边兄弟节点的操作。 + * @method clearEmptySibling + * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, + * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 + * 则这些兄弟节点将被删除 + * @see UE.dom.domUtils.clearEmptySibling(Node) + */ + + /** + * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, + * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。 + * @method clearEmptySibling + * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, + * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 + * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作 + * 则这些兄弟节点将被删除 + * @see UE.dom.domUtils.clearEmptySibling(Node) + */ + clearEmptySibling:function (node, ignoreNext, ignorePre) { + function clear(next, dir) { + var tmpNode; + while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next) + //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了 + || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) { + tmpNode = next[dir]; + domUtils.remove(next); + next = tmpNode; + } + } + !ignoreNext && clear(node.nextSibling, 'nextSibling'); + !ignorePre && clear(node.previousSibling, 'previousSibling'); + }, + + /** + * 将一个文本节点node拆分成两个文本节点,offset指定拆分位置 + * @name split + * @grammar UM.dom.domUtils.split(node,offset) => TextNode //返回从切分位置开始的后一个文本节点 + */ + split: function (node, offset) { + var doc = node.ownerDocument; + if (browser.ie && offset == node.nodeValue.length) { + var next = doc.createTextNode(''); + return domUtils.insertAfter(node, next); + } + var retval = node.splitText(offset); + //ie8下splitText不会跟新childNodes,我们手动触发他的更新 + if (browser.ie8) { + var tmpNode = doc.createTextNode(''); + domUtils.insertAfter(retval, tmpNode); + domUtils.remove(tmpNode); + } + return retval; + }, + + /** + * 检测节点node是否为空节点(包括空格、换行、占位符等字符) + * @name isWhitespace + * @grammar UM.dom.domUtils.isWhitespace(node) => true|false + */ + isWhitespace: function (node) { + return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue); + }, + /** + * 获取元素element相对于viewport的位置坐标 + * @name getXY + * @grammar UM.dom.domUtils.getXY(element) => Object //返回坐标对象{x:left,y:top} + */ + getXY: function (element) { + var x = 0, y = 0; + while (element.offsetParent) { + y += element.offsetTop; + x += element.offsetLeft; + element = element.offsetParent; + } + return { 'x': x, 'y': y}; + }, + /** + * 检查节点node是否是空inline节点 + * @name isEmptyInlineElement + * @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0 + * @example + * => 1 + * => 1 + * => 1 + * xx => 0 + */ + isEmptyInlineElement: function (node) { + if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) { + return 0; + } + node = node.firstChild; + while (node) { + //如果是创建的bookmark就跳过 + if (domUtils.isBookmarkNode(node)) { + return 0; + } + if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) || + node.nodeType == 3 && !domUtils.isWhitespace(node) + ) { + return 0; + } + node = node.nextSibling; + } + return 1; + + }, + + + /** + * 检查节点node是否为块元素 + * @name isBlockElm + * @grammar UM.dom.domUtils.isBlockElm(node) => true|false + */ + isBlockElm: function (node) { + return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName]; + }, + + + /** + * 原生方法getElementsByTagName的封装 + * @name getElementsByTagName + * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组 + */ + getElementsByTagName: function (node, name, filter) { + if (filter && utils.isString(filter)) { + var className = filter; + filter = function (node) { + var result = false; + $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) { + if ($(node).hasClass(v)) { + result = true; + return false; + } + }) + return result; + } + } + name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' '); + var arr = []; + for (var n = 0, ni; ni = name[n++];) { + var list = node.getElementsByTagName(ni); + for (var i = 0, ci; ci = list[i++];) { + if (!filter || filter(ci)) + arr.push(ci); + } + } + return arr; + }, + + + /** + * 设置节点node及其子节点不会被选中 + * @name unSelectable + * @grammar UM.dom.domUtils.unSelectable(node) + */ + unSelectable: ie && browser.ie9below || browser.opera ? function (node) { + //for ie9 + node.onselectstart = function () { + return false; + }; + node.onclick = node.onkeyup = node.onkeydown = function () { + return false; + }; + node.unselectable = 'on'; + node.setAttribute("unselectable", "on"); + for (var i = 0, ci; ci = node.all[i++];) { + switch (ci.tagName.toLowerCase()) { + case 'iframe' : + case 'textarea' : + case 'input' : + case 'select' : + break; + default : + ci.unselectable = 'on'; + node.setAttribute("unselectable", "on"); + } + } + } : function (node) { + node.style.MozUserSelect = + node.style.webkitUserSelect = + node.style.msUserSelect = + node.style.KhtmlUserSelect = 'none'; + }, + /** + * 删除节点node上的属性attrNames,attrNames为属性名称数组 + * @name removeAttributes + * @grammar UM.dom.domUtils.removeAttributes(node,attrNames) + * @example + * //Before remove + * xxxxx + * //Remove + * UM.dom.domUtils.removeAttributes(node,["id","name"]); + * //After remove + * xxxxx + */ + removeAttributes: function (node, attrNames) { + attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' '); + for (var i = 0, ci; ci = attrNames[i++];) { + ci = attrFix[ci] || ci; + switch (ci) { + case 'className': + node[ci] = ''; + break; + case 'style': + node.style.cssText = ''; + !browser.ie && node.removeAttributeNode(node.getAttributeNode('style')) + } + node.removeAttribute(ci); + } + }, + /** + * 在doc下创建一个标签名为tag,属性为attrs的元素 + * @name createElement + * @grammar UM.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点 + */ + createElement: function (doc, tag, attrs) { + return domUtils.setAttributes(doc.createElement(tag), attrs) + }, + /** + * 为节点node添加属性attrs,attrs为属性键值对 + * @name setAttributes + * @grammar UM.dom.domUtils.setAttributes(node,attrs) => node + */ + setAttributes: function (node, attrs) { + for (var attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + var value = attrs[attr]; + switch (attr) { + case 'class': + //ie下要这样赋值,setAttribute不起作用 + node.className = value; + break; + case 'style' : + node.style.cssText = node.style.cssText + ";" + value; + break; + case 'innerHTML': + node[attr] = value; + break; + case 'value': + node.value = value; + break; + default: + node.setAttribute(attrFix[attr] || attr, value); + } + } + } + return node; + }, + + /** + * 获取元素element的计算样式 + * @name getComputedStyle + * @grammar UM.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值 + * @example + * getComputedStyle(document.body,"font-size") => "15px" + * getComputedStyle(form,"color") => "#ffccdd" + */ + getComputedStyle: function (element, styleName) { + return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName))); + }, + + /** + * 阻止事件默认行为 + * @param {Event} evt 需要组织的事件对象 + */ + preventDefault: function (evt) { + evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); + }, + + /** + * 删除元素element指定的样式 + * @method removeStyle + * @param { Element } element 需要删除样式的元素 + * @param { String } styleName 需要删除的样式名 + * @example + * ```html + * + * + * + * ``` + */ + removeStyle:function (element, name) { + if(browser.ie ){ + //针对color先单独处理一下 + if(name == 'color'){ + name = '(^|;)' + name; + } + element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'') + }else{ + if (element.style.removeProperty) { + element.style.removeProperty (name); + }else { + element.style.removeAttribute (utils.cssStyleToDomStyle(name)); + } + } + + + if (!element.style.cssText) { + domUtils.removeAttributes(element, ['style']); + } + }, + + /** + * 获取元素element的某个样式值 + * @name getStyle + * @grammar UM.dom.domUtils.getStyle(element,name) => String + */ + getStyle: function (element, name) { + var value = element.style[ utils.cssStyleToDomStyle(name) ]; + return utils.fixColor(name, value); + }, + /** + * 为元素element设置样式属性值 + * @name setStyle + * @grammar UM.dom.domUtils.setStyle(element,name,value) + */ + setStyle: function (element, name, value) { + element.style[utils.cssStyleToDomStyle(name)] = value; + if (!utils.trim(element.style.cssText)) { + this.removeAttributes(element, 'style') + } + }, + + /** + * 删除_moz_dirty属性 + * @function + */ + removeDirtyAttr: function (node) { + for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) { + ci.removeAttribute('_moz_dirty'); + } + node.removeAttribute('_moz_dirty'); + }, + /** + * 返回子节点的数量 + * @function + * @param {Node} node 父节点 + * @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量 + * @return {Number} 符合条件子节点的数量 + */ + getChildCount: function (node, fn) { + var count = 0, first = node.firstChild; + fn = fn || function () { + return 1; + }; + while (first) { + if (fn(first)) { + count++; + } + first = first.nextSibling; + } + return count; + }, + + /** + * 判断是否为空节点 + * @function + * @param {Node} node 节点 + * @return {Boolean} 是否为空节点 + */ + isEmptyNode: function (node) { + return !node.firstChild || domUtils.getChildCount(node, function (node) { + return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node) + }) == 0 + }, + + /** + * 判断节点是否为br + * @function + * @param {Node} node 节点 + */ + isBr: function (node) { + return node.nodeType == 1 && node.tagName == 'BR'; + }, + isFillChar: function (node, isInStart) { + return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length + }, + + isEmptyBlock: function (node, reg) { + if (!node || node.nodeType != 1) + return 0; + reg = reg || new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g'); + if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) { + return 0; + } + for (var n in dtd.$isNotEmpty) { + if (node.getElementsByTagName(n).length) { + return 0; + } + } + return 1; + }, + + //判断是否是编辑器自定义的参数 + isCustomeNode: function (node) { + return node.nodeType == 1 && node.getAttribute('_ue_custom_node_'); + }, + fillNode: function (doc, node) { + var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br'); + node.innerHTML = ''; + node.appendChild(tmpNode); + }, + isBoundaryNode: function (node, dir) { + var tmp; + while (!domUtils.isBody(node)) { + tmp = node; + node = node.parentNode; + if (tmp !== node[dir]) { + return false; + } + } + return true; + }, + isFillChar: function (node, isInStart) { + return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length + }, + isBody: function(node){ + return $(node).hasClass('edui-body-container'); + } + }; + var fillCharReg = new RegExp(domUtils.fillChar, 'g'); +///import editor.js +///import core/utils.js +///import core/browser.js +///import core/dom/dom.js +///import core/dom/dtd.js +///import core/dom/domUtils.js + /** + * @file + * @name UM.dom.Range + * @anthor zhanyi + * @short Range + * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js + * @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性 + */ + (function () { + var guid = 0, + fillChar = domUtils.fillChar, + fillData; + + /** + * 更新range的collapse状态 + * @param {Range} range range对象 + */ + function updateCollapse(range) { + range.collapsed = + range.startContainer && range.endContainer && + range.startContainer === range.endContainer && + range.startOffset == range.endOffset; + } + + function selectOneNode(rng){ + return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1 + } + function setEndPoint(toStart, node, offset, range) { + //如果node是自闭合标签要处理 + if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) { + offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1); + node = node.parentNode; + } + if (toStart) { + range.startContainer = node; + range.startOffset = offset; + if (!range.endContainer) { + range.collapse(true); + } + } else { + range.endContainer = node; + range.endOffset = offset; + if (!range.startContainer) { + range.collapse(false); + } + } + updateCollapse(range); + return range; + } + + + /** + * @name Range + * @grammar new UM.dom.Range(document) => Range 实例 + * @desc 创建一个跟document绑定的空的Range实例 + * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode + * - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符 + * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode + * - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符 + * - ***document*** 跟range关联的document对象 + * - ***collapsed*** 是否是闭合状态 + */ + var Range = dom.Range = function (document,body) { + var me = this; + me.startContainer = + me.startOffset = + me.endContainer = + me.endOffset = null; + me.document = document; + me.collapsed = true; + me.body = body; + }; + + /** + * 删除fillData + * @param doc + * @param excludeNode + */ + function removeFillData(doc, excludeNode) { + try { + if (fillData && domUtils.inDoc(fillData, doc)) { + if (!fillData.nodeValue.replace(fillCharReg, '').length) { + var tmpNode = fillData.parentNode; + domUtils.remove(fillData); + while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) && + //safari的contains有bug + (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode)) + ) { + fillData = tmpNode.parentNode; + domUtils.remove(tmpNode); + tmpNode = fillData; + } + } else { + fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, ''); + } + } + } catch (e) { + } + } + + /** + * + * @param node + * @param dir + */ + function mergeSibling(node, dir) { + var tmpNode; + node = node[dir]; + while (node && domUtils.isFillChar(node)) { + tmpNode = node[dir]; + domUtils.remove(node); + node = tmpNode; + } + } + + function execContentsAction(range, action) { + //调整边界 + //range.includeBookmark(); + var start = range.startContainer, + end = range.endContainer, + startOffset = range.startOffset, + endOffset = range.endOffset, + doc = range.document, + frag = doc.createDocumentFragment(), + tmpStart, tmpEnd; + if (start.nodeType == 1) { + start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode(''))); + } + if (end.nodeType == 1) { + end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode(''))); + } + if (start === end && start.nodeType == 3) { + frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset))); + //is not clone + if (action) { + start.deleteData(startOffset, endOffset - startOffset); + range.collapse(true); + } + return frag; + } + var current, currentLevel, clone = frag, + startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true); + for (var i = 0; startParents[i] == endParents[i];) { + i++; + } + for (var j = i, si; si = startParents[j]; j++) { + current = si.nextSibling; + if (si == start) { + if (!tmpStart) { + if (range.startContainer.nodeType == 3) { + clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset))); + //is not clone + if (action) { + start.deleteData(startOffset, start.nodeValue.length - startOffset); + } + } else { + clone.appendChild(!action ? start.cloneNode(true) : start); + } + } + } else { + currentLevel = si.cloneNode(false); + clone.appendChild(currentLevel); + } + while (current) { + if (current === end || current === endParents[j]) { + break; + } + si = current.nextSibling; + clone.appendChild(!action ? current.cloneNode(true) : current); + current = si; + } + clone = currentLevel; + } + clone = frag; + if (!startParents[i]) { + clone.appendChild(startParents[i - 1].cloneNode(false)); + clone = clone.firstChild; + } + for (var j = i, ei; ei = endParents[j]; j++) { + current = ei.previousSibling; + if (ei == end) { + if (!tmpEnd && range.endContainer.nodeType == 3) { + clone.appendChild(doc.createTextNode(end.substringData(0, endOffset))); + //is not clone + if (action) { + end.deleteData(0, endOffset); + } + } + } else { + currentLevel = ei.cloneNode(false); + clone.appendChild(currentLevel); + } + //如果两端同级,右边第一次已经被开始做了 + if (j != i || !startParents[i]) { + while (current) { + if (current === start) { + break; + } + ei = current.previousSibling; + clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild); + current = ei; + } + } + clone = currentLevel; + } + if (action) { + range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true); + } + tmpStart && domUtils.remove(tmpStart); + tmpEnd && domUtils.remove(tmpEnd); + return frag; + } + Range.prototype = { + /** + * @name deleteContents + * @grammar range.deleteContents() => Range + * @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态 + * @example + * DOM Element : + * xx[xxx]x + * //执行方法后 + * xx|x + * 注意range改变了 + * range.startContainer => b + * range.startOffset => 2 + * range.endContainer => b + * range.endOffset => 2 + * range.collapsed => true + */ + deleteContents:function () { + var txt; + if (!this.collapsed) { + execContentsAction(this, 1); + } + if (browser.webkit) { + txt = this.startContainer; + if (txt.nodeType == 3 && !txt.nodeValue.length) { + this.setStartBefore(txt).collapse(true); + domUtils.remove(txt); + } + } + return this; + }, + inFillChar : function(){ + var start = this.startContainer; + if(this.collapsed && start.nodeType == 3 + && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length + ){ + return true; + } + return false; + }, + /** + * @name setStart + * @grammar range.setStart(node,offset) => Range + * @desc 设置range的开始位置位于node节点内,偏移量为offset + * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符 + */ + setStart:function (node, offset) { + return setEndPoint(true, node, offset, this); + }, + /** + * 设置range的结束位置位于node节点,偏移量为offset + * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符 + * @name setEnd + * @grammar range.setEnd(node,offset) => Range + */ + setEnd:function (node, offset) { + return setEndPoint(false, node, offset, this); + }, + /** + * 将Range开始位置设置到node节点之后 + * @name setStartAfter + * @grammar range.setStartAfter(node) => Range + * @example + * xxx|xx + * 执行setStartAfter(i)后 + * range.startContainer =>b + * range.startOffset =>2 + */ + setStartAfter:function (node) { + return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1); + }, + /** + * 将Range开始位置设置到node节点之前 + * @name setStartBefore + * @grammar range.setStartBefore(node) => Range + * @example + * xxx|xx + * 执行setStartBefore(i)后 + * range.startContainer =>b + * range.startOffset =>1 + */ + setStartBefore:function (node) { + return this.setStart(node.parentNode, domUtils.getNodeIndex(node)); + }, + /** + * 将Range结束位置设置到node节点之后 + * @name setEndAfter + * @grammar range.setEndAfter(node) => Range + * @example + * xxx|xx + * setEndAfter(i)后 + * range.endContainer =>b + * range.endtOffset =>2 + */ + setEndAfter:function (node) { + return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1); + }, + /** + * 将Range结束位置设置到node节点之前 + * @name setEndBefore + * @grammar range.setEndBefore(node) => Range + * @example + * xxx|xx + * 执行setEndBefore(i)后 + * range.endContainer =>b + * range.endtOffset =>1 + */ + setEndBefore:function (node) { + return this.setEnd(node.parentNode, domUtils.getNodeIndex(node)); + }, + /** + * 将Range开始位置设置到node节点内的开始位置 + * @name setStartAtFirst + * @grammar range.setStartAtFirst(node) => Range + */ + setStartAtFirst:function (node) { + return this.setStart(node, 0); + }, + /** + * 将Range开始位置设置到node节点内的结束位置 + * @name setStartAtLast + * @grammar range.setStartAtLast(node) => Range + */ + setStartAtLast:function (node) { + return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); + }, + /** + * 将Range结束位置设置到node节点内的开始位置 + * @name setEndAtFirst + * @grammar range.setEndAtFirst(node) => Range + */ + setEndAtFirst:function (node) { + return this.setEnd(node, 0); + }, + /** + * 将Range结束位置设置到node节点内的结束位置 + * @name setEndAtLast + * @grammar range.setEndAtLast(node) => Range + */ + setEndAtLast:function (node) { + return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length); + }, + + /** + * 选中完整的指定节点,并返回包含该节点的range + * @name selectNode + * @grammar range.selectNode(node) => Range + */ + selectNode:function (node) { + return this.setStartBefore(node).setEndAfter(node); + }, + /** + * 选中node内部的所有节点,并返回对应的range + * @name selectNodeContents + * @grammar range.selectNodeContents(node) => Range + * @example + * xx[xxxx]xxx + * 执行后 + * [xxxxxxxxx] + * range.startContainer =>b + * range.startOffset =>0 + * range.endContainer =>b + * range.endOffset =>3 + */ + selectNodeContents:function (node) { + return this.setStart(node, 0).setEndAtLast(node); + }, + + /** + * 克隆一个新的range对象 + * @name cloneRange + * @grammar range.cloneRange() => Range + */ + cloneRange:function () { + var me = this; + return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset); + + }, + + /** + * 让选区闭合到尾部,若toStart为真,则闭合到头部 + * @name collapse + * @grammar range.collapse() => Range + * @grammar range.collapse(true) => Range //闭合选区到头部 + */ + collapse:function (toStart) { + var me = this; + if (toStart) { + me.endContainer = me.startContainer; + me.endOffset = me.startOffset; + } else { + me.startContainer = me.endContainer; + me.startOffset = me.endOffset; + } + me.collapsed = true; + return me; + }, + + /** + * 调整range的边界,使其"收缩"到最小的位置 + * @name shrinkBoundary + * @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见adjustmentBoundary + * @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置 + * @example + * xx[xxxxx] ==> xx[xxxxx] + * x[xx]xxx ==> x[xx]xxx + * [xxxxxxxxxxx] ==> [xxxxxxxxxxx] + */ + shrinkBoundary:function (ignoreEnd) { + var me = this, child, + collapsed = me.collapsed; + function check(node){ + return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName] + } + while (me.startContainer.nodeType == 1 //是element + && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element + && check(child)) { + me.setStart(child, 0); + } + if (collapsed) { + return me.collapse(true); + } + if (!ignoreEnd) { + while (me.endContainer.nodeType == 1//是element + && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错 + && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element + && check(child)) { + me.setEnd(child, child.childNodes.length); + } + } + return me; + }, + + /** + * 调整边界容器,如果是textNode,就调整到elementNode上 + * @name trimBoundary + * @grammar range.trimBoundary([ignoreEnd]) => Range //true忽略结束边界 + * @example + * DOM Element : + * |xxx + * startContainer = xxx; startOffset = 0 + * //执行后本方法后 + * startContainer = ; startOffset = 0 + * @example + * Dom Element : + * xx|x + * startContainer = xxx; startOffset = 2 + * //执行本方法后,xxx被实实在在地切分成两个TextNode + * startContainer = ; startOffset = 1 + */ + trimBoundary:function (ignoreEnd) { + this.txtToElmBoundary(); + var start = this.startContainer, + offset = this.startOffset, + collapsed = this.collapsed, + end = this.endContainer; + if (start.nodeType == 3) { + if (offset == 0) { + this.setStartBefore(start); + } else { + if (offset >= start.nodeValue.length) { + this.setStartAfter(start); + } else { + var textNode = domUtils.split(start, offset); + //跟新结束边界 + if (start === end) { + this.setEnd(textNode, this.endOffset - offset); + } else if (start.parentNode === end) { + this.endOffset += 1; + } + this.setStartBefore(textNode); + } + } + if (collapsed) { + return this.collapse(true); + } + } + if (!ignoreEnd) { + offset = this.endOffset; + end = this.endContainer; + if (end.nodeType == 3) { + if (offset == 0) { + this.setEndBefore(end); + } else { + offset < end.nodeValue.length && domUtils.split(end, offset); + this.setEndAfter(end); + } + } + } + return this; + }, + /** + * 如果选区在文本的边界上,就扩展选区到文本的父节点上 + * @name txtToElmBoundary + * @example + * Dom Element : + * |xxx + * startContainer = xxx; startOffset = 0 + * //本方法执行后 + * startContainer = ; startOffset = 0 + * @example + * Dom Element : + * xxx| + * startContainer = xxx; startOffset = 3 + * //本方法执行后 + * startContainer = ; startOffset = 1 + */ + txtToElmBoundary:function (ignoreCollapsed) { + function adjust(r, c) { + var container = r[c + 'Container'], + offset = r[c + 'Offset']; + if (container.nodeType == 3) { + if (!offset) { + r['set' + c.replace(/(\w)/, function (a) { + return a.toUpperCase(); + }) + 'Before'](container); + } else if (offset >= container.nodeValue.length) { + r['set' + c.replace(/(\w)/, function (a) { + return a.toUpperCase(); + }) + 'After' ](container); + } + } + } + + if (ignoreCollapsed || !this.collapsed) { + adjust(this, 'start'); + adjust(this, 'end'); + } + return this; + }, + + /** + * 在当前选区的开始位置前插入一个节点或者fragment,range的开始位置会在插入节点的前边 + * @name insertNode + * @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment + * @example + * Range : + * xxx[x

          xxxx

          xxxx]x

          sdfsdf

          + * 待插入Node : + *

          ssss

          + * 执行本方法后的Range : + * xxx[

          ssss

          x

          xxxx

          xxxx]x

          sdfsdf

          + */ + insertNode:function (node) { + var first = node, length = 1; + if (node.nodeType == 11) { + first = node.firstChild; + length = node.childNodes.length; + } + this.trimBoundary(true); + var start = this.startContainer, + offset = this.startOffset; + var nextNode = start.childNodes[ offset ]; + if (nextNode) { + start.insertBefore(node, nextNode); + } else { + start.appendChild(node); + } + if (first.parentNode === this.endContainer) { + this.endOffset = this.endOffset + length; + } + return this.setStartBefore(first); + }, + /** + * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾 + * @name setCursor + * @grammar range.setCursor([toEnd]) => Range //toEnd为true时,光标闭合到选区的末尾 + */ + setCursor:function (toEnd, noFillData) { + return this.collapse(!toEnd).select(noFillData); + }, + /** + * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置 + * @name createBookmark + * @grammar range.createBookmark([serialize]) => Object //{start:开始标记,end:结束标记,id:serialize} serialize为真时,开始结束标记是插入节点的id,否则是插入节点的引用 + */ + createBookmark:function (serialize, same) { + var endNode, + startNode = this.document.createElement('span'); + startNode.style.cssText = 'display:none;line-height:0px;'; + startNode.appendChild(this.document.createTextNode('\u200D')); + startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++); + + if (!this.collapsed) { + endNode = startNode.cloneNode(true); + endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++); + } + this.insertNode(startNode); + if (endNode) { + this.collapse().insertNode(endNode).setEndBefore(endNode); + } + this.setStartAfter(startNode); + return { + start:serialize ? startNode.id : startNode, + end:endNode ? serialize ? endNode.id : endNode : null, + id:serialize + } + }, + /** + * 移动边界到书签位置,并删除插入的书签节点 + * @name moveToBookmark + * @grammar range.moveToBookmark(bookmark) => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的 + */ + moveToBookmark:function (bookmark) { + var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start, + end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end; + this.setStartBefore(start); + domUtils.remove(start); + if (end) { + this.setEndBefore(end); + domUtils.remove(end); + } else { + this.collapse(true); + } + return this; + }, + + /** + * 调整Range的边界,使其"缩小"到最合适的位置 + * @name adjustmentBoundary + * @grammar range.adjustmentBoundary() => Range //参见shrinkBoundary + * @example + * xx[xxxxx] ==> xx[xxxxx] + * x[xx]xxx ==> x[xx]xxx + */ + adjustmentBoundary:function () { + if (!this.collapsed) { + while (!domUtils.isBody(this.startContainer) && + this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length && + this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length + ) { + + this.setStartAfter(this.startContainer); + } + while (!domUtils.isBody(this.endContainer) && !this.endOffset && + this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length + ) { + this.setEndBefore(this.endContainer); + } + } + return this; + }, + + /** + * 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点 + * @name getClosedNode + * @grammar range.getClosedNode() => node|null + * @example + * xxxx[]xxx + */ + getClosedNode:function () { + var node; + if (!this.collapsed) { + var range = this.cloneRange().adjustmentBoundary().shrinkBoundary(); + if (selectOneNode(range)) { + var child = range.startContainer.childNodes[range.startOffset]; + if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) { + node = child; + } + } + } + return node; + }, + /** + * 根据当前range选中内容节点(在页面上表现为反白显示) + * @name select + * @grammar range.select(); => Range + */ + select:browser.ie ? function (noFillData, textRange) { + var nativeRange; + if (!this.collapsed) + this.shrinkBoundary(); + var node = this.getClosedNode(); + if (node && !textRange) { + try { + nativeRange = this.document.body.createControlRange(); + nativeRange.addElement(node); + nativeRange.select(); + } catch (e) {} + return this; + } + var bookmark = this.createBookmark(), + start = bookmark.start, + end; + nativeRange = this.document.body.createTextRange(); + nativeRange.moveToElementText(start); + nativeRange.moveStart('character', 1); + if (!this.collapsed) { + var nativeRangeEnd = this.document.body.createTextRange(); + end = bookmark.end; + nativeRangeEnd.moveToElementText(end); + nativeRange.setEndPoint('EndToEnd', nativeRangeEnd); + } else { + if (!noFillData && this.startContainer.nodeType != 3) { + //使用|x固定住光标 + var tmpText = this.document.createTextNode(fillChar), + tmp = this.document.createElement('span'); + tmp.appendChild(this.document.createTextNode(fillChar)); + start.parentNode.insertBefore(tmp, start); + start.parentNode.insertBefore(tmpText, start); + //当点b,i,u时,不能清除i上边的b + removeFillData(this.document, tmpText); + fillData = tmpText; + mergeSibling(tmp, 'previousSibling'); + mergeSibling(start, 'nextSibling'); + nativeRange.moveStart('character', -1); + nativeRange.collapse(true); + } + } + this.moveToBookmark(bookmark); + tmp && domUtils.remove(tmp); + //IE在隐藏状态下不支持range操作,catch一下 + try { + nativeRange.select(); + } catch (e) { + } + return this; + } : function (notInsertFillData) { + function checkOffset(rng){ + + function check(node,offset,dir){ + if(node.nodeType == 3 && node.nodeValue.length < offset){ + rng[dir + 'Offset'] = node.nodeValue.length + } + } + check(rng.startContainer,rng.startOffset,'start'); + check(rng.endContainer,rng.endOffset,'end'); + } + var win = domUtils.getWindow(this.document), + sel = win.getSelection(), + txtNode; + //FF下关闭自动长高时滚动条在关闭dialog时会跳 + //ff下如果不body.focus将不能定位闭合光标到编辑器内 + browser.gecko ? this.body.focus() : win.focus(); + if (sel) { + sel.removeAllRanges(); + // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断 + // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR' + if (this.collapsed && !notInsertFillData) { +// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点 +// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) { +// var tmp = this.document.createTextNode(''); +// this.insertNode(tmp).setStart(tmp, 0).collapse(true); +// } +// + //处理光标落在文本节点的情况 + //处理以下的情况 + //|xxxx + //xxxx|xxxx + //xxxx| + var start = this.startContainer,child = start; + if(start.nodeType == 1){ + child = start.childNodes[this.startOffset]; + + } + if( !(start.nodeType == 3 && this.startOffset) && + (child ? + (!child.previousSibling || child.previousSibling.nodeType != 3) + : + (!start.lastChild || start.lastChild.nodeType != 3) + ) + ){ + txtNode = this.document.createTextNode(fillChar); + //跟着前边走 + this.insertNode(txtNode); + removeFillData(this.document, txtNode); + mergeSibling(txtNode, 'previousSibling'); + mergeSibling(txtNode, 'nextSibling'); + fillData = txtNode; + this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true); + } + } + var nativeRange = this.document.createRange(); + if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){ + var child = this.startContainer.childNodes[this.startOffset]; + if(!child){ + //往前靠拢 + child = this.startContainer.lastChild; + if( child && domUtils.isBr(child)){ + this.setStartBefore(child).collapse(true); + } + }else{ + //向后靠拢 + while(child && domUtils.isBlockElm(child)){ + if(child.nodeType == 1 && child.childNodes[0]){ + child = child.childNodes[0] + }else{ + break; + } + } + child && this.setStartBefore(child).collapse(true) + } + + } + //是createAddress最后一位算的不准,现在这里进行微调 + checkOffset(this); + nativeRange.setStart(this.startContainer, this.startOffset); + nativeRange.setEnd(this.endContainer, this.endOffset); + sel.addRange(nativeRange); + } + return this; + }, + + + createAddress : function(ignoreEnd,ignoreTxt){ + var addr = {},me = this; + + function getAddress(isStart){ + var node = isStart ? me.startContainer : me.endContainer; + var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}), + addrs = []; + for(var i = 0,ci;ci = parents[i++];){ + addrs.push(domUtils.getNodeIndex(ci,ignoreTxt)); + } + var firstIndex = 0; + + if(ignoreTxt){ + if(node.nodeType == 3){ + var tmpNode = node.previousSibling; + while(tmpNode && tmpNode.nodeType == 3){ + firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length; + tmpNode = tmpNode.previousSibling; + } + firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 ) + }else{ + node = node.childNodes[ isStart ? me.startOffset : me.endOffset]; + if(node){ + firstIndex = domUtils.getNodeIndex(node,ignoreTxt); + }else{ + node = isStart ? me.startContainer : me.endContainer; + var first = node.firstChild; + while(first){ + if(domUtils.isFillChar(first)){ + first = first.nextSibling; + continue; + } + firstIndex++; + if(first.nodeType == 3){ + while( first && first.nodeType == 3){ + first = first.nextSibling; + } + }else{ + first = first.nextSibling; + } + } + } + } + + }else{ + firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset + } + if(firstIndex < 0){ + firstIndex = 0; + } + addrs.push(firstIndex); + return addrs; + } + addr.startAddress = getAddress(true); + if(!ignoreEnd){ + addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress(); + } + return addr; + }, + moveToAddress : function(addr,ignoreEnd){ + var me = this; + function getNode(address,isStart){ + var tmpNode = me.body, + parentNode,offset; + for(var i= 0,ci,l=address.length;i '); + this.cloneRange().insertNode($span.get(0)); + var winScrollTop = $(window).scrollTop(), + winHeight = $(window).height(), + spanTop = $span.offset().top; + if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){ + if(spanTop > winScrollTop + winHeight){ + window.scrollTo(0,spanTop - winHeight + $span.height()) + }else{ + window.scrollTo(0,winScrollTop - spanTop) + } + + } + $span.remove(); + }, + getOffset : function(){ + var bk = this.createBookmark(); + var offset = $(bk.start).css('display','inline-block').offset(); + this.moveToBookmark(bk); + return offset + } + }; + })(); +///import editor.js +///import core/browser.js +///import core/dom/dom.js +///import core/dom/dtd.js +///import core/dom/domUtils.js +///import core/dom/Range.js + /** + * @class UM.dom.Selection Selection类 + */ + (function () { + + function getBoundaryInformation( range, start ) { + var getIndex = domUtils.getNodeIndex; + range = range.duplicate(); + range.collapse( start ); + var parent = range.parentElement(); + //如果节点里没有子节点,直接退出 + if ( !parent.hasChildNodes() ) { + return {container:parent, offset:0}; + } + var siblings = parent.children, + child, + testRange = range.duplicate(), + startIndex = 0, endIndex = siblings.length - 1, index = -1, + distance; + while ( startIndex <= endIndex ) { + index = Math.floor( (startIndex + endIndex) / 2 ); + child = siblings[index]; + testRange.moveToElementText( child ); + var position = testRange.compareEndPoints( 'StartToStart', range ); + if ( position > 0 ) { + endIndex = index - 1; + } else if ( position < 0 ) { + startIndex = index + 1; + } else { + //trace:1043 + return {container:parent, offset:getIndex( child )}; + } + } + if ( index == -1 ) { + testRange.moveToElementText( parent ); + testRange.setEndPoint( 'StartToStart', range ); + distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; + siblings = parent.childNodes; + if ( !distance ) { + child = siblings[siblings.length - 1]; + return {container:child, offset:child.nodeValue.length}; + } + + var i = siblings.length; + while ( distance > 0 ){ + distance -= siblings[ --i ].nodeValue.length; + } + return {container:siblings[i], offset:-distance}; + } + testRange.collapse( position > 0 ); + testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range ); + distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; + if ( !distance ) { + return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ? + {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} : + {container:child, offset:position > 0 ? 0 : child.childNodes.length} + } + while ( distance > 0 ) { + try { + var pre = child; + child = child[position > 0 ? 'previousSibling' : 'nextSibling']; + distance -= child.nodeValue.length; + } catch ( e ) { + return {container:parent, offset:getIndex( pre )}; + } + } + return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance} + } + + /** + * 将ieRange转换为Range对象 + * @param {Range} ieRange ieRange对象 + * @param {Range} range Range对象 + * @return {Range} range 返回转换后的Range对象 + */ + function transformIERangeToRange( ieRange, range ) { + if ( ieRange.item ) { + range.selectNode( ieRange.item( 0 ) ); + } else { + var bi = getBoundaryInformation( ieRange, true ); + range.setStart( bi.container, bi.offset ); + if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) { + bi = getBoundaryInformation( ieRange, false ); + range.setEnd( bi.container, bi.offset ); + } + } + return range; + } + + /** + * 获得ieRange + * @param {Selection} sel Selection对象 + * @return {ieRange} 得到ieRange + */ + function _getIERange( sel,txtRange ) { + var ieRange; + //ie下有可能报错 + try { + ieRange = sel.getNative(txtRange).createRange(); + } catch ( e ) { + return null; + } + var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement(); + if ( ( el.ownerDocument || el ) === sel.document ) { + return ieRange; + } + return null; + } + + var Selection = dom.Selection = function ( doc,body ) { + var me = this; + me.document = doc; + me.body = body; + if ( browser.ie9below ) { + $( body).on('beforedeactivate', function () { + me._bakIERange = me.getIERange(); + } ).on('activate', function () { + try { + var ieNativRng = _getIERange( me ); + if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) { + me._bakIERange.select(); + } + } catch ( ex ) { + } + me._bakIERange = null; + } ); + } + }; + + Selection.prototype = { + hasNativeRange : function(){ + var rng; + if(!browser.ie || browser.ie9above){ + var nativeSel = this.getNative(); + if(!nativeSel.rangeCount){ + return false; + } + rng = nativeSel.getRangeAt(0); + }else{ + rng = _getIERange(this); + } + return this.rangeInBody(rng); + }, + /** + * 获取原生seleciton对象 + * @public + * @function + * @name UM.dom.Selection.getNative + * @return {Selection} 获得selection对象 + */ + getNative:function (txtRange) { + var doc = this.document; + try { + return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection(); + } catch ( e ) { + return null; + } + }, + /** + * 获得ieRange + * @public + * @function + * @name UM.dom.Selection.getIERange + * @return {ieRange} 返回ie原生的Range + */ + getIERange:function (txtRange) { + var ieRange = _getIERange( this,txtRange ); + if ( !ieRange || !this.rangeInBody(ieRange,txtRange)) { + if ( this._bakIERange ) { + return this._bakIERange; + } + } + return ieRange; + }, + rangeInBody : function(rng,txtRange){ + var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer; + + return node === this.body || domUtils.inDoc(node,this.body); + }, + /** + * 缓存当前选区的range和选区的开始节点 + * @public + * @function + * @name UM.dom.Selection.cache + */ + cache:function () { + this.clear(); + this._cachedRange = this.getRange(); + this._cachedStartElement = this.getStart(); + this._cachedStartElementPath = this.getStartElementPath(); + }, + + getStartElementPath:function () { + if ( this._cachedStartElementPath ) { + return this._cachedStartElementPath; + } + var start = this.getStart(); + if ( start ) { + return domUtils.findParents( start, true, null, true ) + } + return []; + }, + /** + * 清空缓存 + * @public + * @function + * @name UM.dom.Selection.clear + */ + clear:function () { + this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null; + }, + /** + * 编辑器是否得到了选区 + */ + isFocus:function () { + return this.hasNativeRange() + + }, + /** + * 获取选区对应的Range + * @public + * @function + * @name UM.dom.Selection.getRange + * @returns {UM.dom.Range} 得到Range对象 + */ + getRange:function () { + var me = this; + function optimze( range ) { + var child = me.body.firstChild, + collapsed = range.collapsed; + while ( child && child.firstChild ) { + range.setStart( child, 0 ); + child = child.firstChild; + } + if ( !range.startContainer ) { + range.setStart( me.body, 0 ) + } + if ( collapsed ) { + range.collapse( true ); + } + } + + if ( me._cachedRange != null ) { + return this._cachedRange; + } + var range = new dom.Range( me.document,me.body ); + if ( browser.ie9below ) { + var nativeRange = me.getIERange(); + if ( nativeRange && this.rangeInBody(nativeRange)) { + + try{ + transformIERangeToRange( nativeRange, range ); + }catch(e){ + optimze( range ); + } + + } else { + optimze( range ); + } + } else { + var sel = me.getNative(); + if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) { + var firstRange = sel.getRangeAt( 0 ); + var lastRange = sel.getRangeAt( sel.rangeCount - 1 ); + range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset ); + if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) { + optimze( range ); + } + } else { + //trace:1734 有可能已经不在dom树上了,标识的节点 + if ( this._bakRange && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){ + return this._bakRange; + } + optimze( range ); + } + } + + return this._bakRange = range; + }, + + /** + * 获取开始元素,用于状态反射 + * @public + * @function + * @name UM.dom.Selection.getStart + * @return {Element} 获得开始元素 + */ + getStart:function () { + if ( this._cachedStartElement ) { + return this._cachedStartElement; + } + var range = browser.ie9below ? this.getIERange() : this.getRange(), + tmpRange, + start, tmp, parent; + if ( browser.ie9below ) { + if ( !range ) { + //todo 给第一个值可能会有问题 + return this.document.body.firstChild; + } + //control元素 + if ( range.item ){ + return range.item( 0 ); + } + tmpRange = range.duplicate(); + //修正ie下x[xx] 闭合后 x|xx + tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 ); + tmpRange.collapse( 1 ); + start = tmpRange.parentElement(); + parent = tmp = range.parentElement(); + while ( tmp = tmp.parentNode ) { + if ( tmp == start ) { + start = parent; + break; + } + } + } else { + start = range.startContainer; + if ( start.nodeType == 1 && start.hasChildNodes() ){ + start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )]; + } + if ( start.nodeType == 3 ){ + return start.parentNode; + } + } + return start; + }, + /** + * 得到选区中的文本 + * @public + * @function + * @name UM.dom.Selection.getText + * @return {String} 选区中包含的文本 + */ + getText:function () { + var nativeSel, nativeRange; + if ( this.isFocus() && (nativeSel = this.getNative()) ) { + nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 ); + return browser.ie9below ? nativeRange.text : nativeRange.toString(); + } + return ''; + } + }; + })(); + /** + * @file + * @name UM.Editor + * @short Editor + * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js + * @desc 编辑器主类,包含编辑器提供的大部分公用接口 + */ + (function () { + var uid = 0, _selectionChangeTimer; + + /** + * @private + * @ignore + * @param form 编辑器所在的form元素 + * @param editor 编辑器实例对象 + */ + function setValue(form, editor) { + var textarea; + if (editor.textarea) { + if (utils.isString(editor.textarea)) { + for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) { + if (ti.id == 'umeditor_textarea_' + editor.options.textarea) { + textarea = ti; + break; + } + } + } else { + textarea = editor.textarea; + } + } + if (!textarea) { + form.appendChild(textarea = domUtils.createElement(document, 'textarea', { + 'name': editor.options.textarea, + 'id': 'umeditor_textarea_' + editor.options.textarea, + 'style': "display:none" + })); + //不要产生多个textarea + editor.textarea = textarea; + } + textarea.value = editor.hasContents() ? + (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) : + '' + } + function loadPlugins(me){ + //初始化插件 + for (var pi in UM.plugins) { + if(me.options.excludePlugins.indexOf(pi) == -1){ + UM.plugins[pi].call(me); + me.plugins[pi] = 1; + } + } + me.langIsReady = true; + + me.fireEvent("langReady"); + } + function checkCurLang(I18N){ + for(var lang in I18N){ + return lang + } + } + /** + * UEditor编辑器类 + * @name Editor + * @desc 创建一个跟编辑器实例 + * - ***container*** 编辑器容器对象 + * - ***iframe*** 编辑区域所在的iframe对象 + * - ***window*** 编辑区域所在的window + * - ***document*** 编辑区域所在的document对象 + * - ***body*** 编辑区域所在的body对象 + * - ***selection*** 编辑区域的选区对象 + */ + var Editor = UM.Editor = function (options) { + var me = this; + me.uid = uid++; + EventBase.call(me); + me.commands = {}; + me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true); + me.shortcutkeys = {}; + me.inputRules = []; + me.outputRules = []; + //设置默认的常用属性 + me.setOpt({ + isShow: true, + initialContent: '', + initialStyle:'', + autoClearinitialContent: false, + textarea: 'editorValue', + focus: false, + focusInEnd: true, + autoClearEmptyNode: true, + fullscreen: false, + readonly: false, + zIndex: 999, + enterTag: 'p', + lang: 'zh-cn', + langPath: me.options.UMEDITOR_HOME_URL + 'lang/', + theme: 'default', + themePath: me.options.UMEDITOR_HOME_URL + 'themes/', + allHtmlEnabled: false, + autoSyncData : true, + autoHeightEnabled : true, + excludePlugins:'' + }); + me.plugins = {}; + if(!utils.isEmptyObject(UM.I18N)){ + //修改默认的语言类型 + me.options.lang = checkCurLang(UM.I18N); + loadPlugins(me) + }else{ + utils.loadFile(document, { + src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js", + tag: "script", + type: "text/javascript", + defer: "defer" + }, function () { + loadPlugins(me) + }); + } + + }; + Editor.prototype = { + /** + * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready,就马上执行fn,fn的中的this是编辑器实例。 + * 大部分的实例接口都需要放在该方法内部执行,否则在IE下可能会报错。 + * @name ready + * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function + * @example + * var editor = new UM.ui.Editor(); + * editor.render("myEditor"); + * editor.ready(function(){ + * editor.setContent("欢迎使用UEditor!"); + * }) + */ + ready: function (fn) { + var me = this; + if (fn) { + me.isReady ? fn.apply(me) : me.addListener('ready', fn); + } + }, + /** + * 为编辑器设置默认参数值。若用户配置为空,则以默认配置为准 + * @grammar editor.setOpt(key,value); //传入一个键、值对 + * @grammar editor.setOpt({ key:value}); //传入一个json对象 + */ + setOpt: function (key, val) { + var obj = {}; + if (utils.isString(key)) { + obj[key] = val + } else { + obj = key; + } + utils.extend(this.options, obj, true); + }, + getOpt:function(key){ + return this.options[key] || '' + }, + /** + * 销毁编辑器实例对象 + * @name destroy + * @grammar editor.destroy(); + */ + destroy: function () { + + var me = this; + me.fireEvent('destroy'); + var container = me.container; + if(container === document.body){ + container = me.container; + } + var textarea = me.textarea; + if (!textarea) { + textarea = document.createElement('textarea'); + container.parentNode.insertBefore(textarea, container); + } else { + textarea.style.display = '' + } + + textarea.style.width = me.body.offsetWidth + 'px'; + textarea.style.height = me.body.offsetHeight + 'px'; + textarea.value = me.getContent(); + textarea.id = me.key; + if(container.contains(textarea)){ + $(textarea).insertBefore(container); + } + container.innerHTML = ''; + + domUtils.remove(container); + UM.clearCache(me.id); + //trace:2004 + for (var p in me) { + if (me.hasOwnProperty(p)) { + delete this[p]; + } + } + + }, + initialCont : function(holder){ + + if(holder){ + holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name')); + if (holder && /script|textarea/ig.test(holder.tagName)) { + var newDiv = document.createElement('div'); + holder.parentNode.insertBefore(newDiv, holder); + this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml(); + holder.className && (newDiv.className = holder.className); + holder.style.cssText && (newDiv.style.cssText = holder.style.cssText); + + if (/textarea/i.test(holder.tagName)) { + this.textarea = holder; + this.textarea.style.display = 'none'; + + } else { + holder.parentNode.removeChild(holder); + holder.id && (newDiv.id = holder.id); + } + holder = newDiv; + holder.innerHTML = ''; + } + return holder; + }else{ + return null; + } + + }, + /** + * 渲染编辑器的DOM到指定容器,必须且只能调用一次 + * @name render + * @grammar editor.render(containerId); //可以指定一个容器ID + * @grammar editor.render(containerDom); //也可以直接指定容器对象 + */ + render: function (container) { + var me = this, + options = me.options, + getStyleValue=function(attr){ + return parseInt($(container).css(attr)); + }; + + if (utils.isString(container)) { + container = document.getElementById(container); + } + if (container) { + this.id = container.getAttribute('id'); + UM.setEditor(this); + utils.cssRule('edui-style-body',me.options.initialStyle,document); + + container = this.initialCont(container); + + container.className += ' edui-body-container'; + + if(options.initialFrameWidth){ + options.minFrameWidth = options.initialFrameWidth + }else{ + //都没给值,先写死了 + options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth; + } + if(options.initialFrameHeight){ + options.minFrameHeight = options.initialFrameHeight + }else{ + + options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight; + } + + container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth - + getStyleValue("padding-left")- + getStyleValue("padding-right") +'px'; + + var height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") ); + if(this.options.autoHeightEnabled){ + container.style.minHeight = height +'px'; + container.style.height = ''; + if(browser.ie && browser.version <= 6){ + container.style.height = height ; + container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"'); + } + }else{ + $(container).height(height) + } + container.style.zIndex = options.zIndex; + this._setup(container); + + } + }, + /** + * 编辑器初始化 + * @private + * @ignore + * @param {Element} doc 编辑器Iframe中的文档对象 + */ + _setup: function (cont) { + var me = this, + options = me.options; + + cont.contentEditable = true; + document.body.spellcheck = false; + + me.document = document; + me.window = document.defaultView || document.parentWindow; + me.body = cont; + me.$body = $(cont); + me.selection = new dom.Selection(document,me.body); + me._isEnabled = false; + //gecko初始化就能得到range,无法判断isFocus了 + var geckoSel; + if (browser.gecko && (geckoSel = this.selection.getNative())) { + geckoSel.removeAllRanges(); + } + this._initEvents(); + //为form提交提供一个隐藏的textarea + for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) { + if (form.tagName == 'FORM') { + me.form = form; + if(me.options.autoSyncData){ + $(cont).on('blur',function(){ + setValue(form,me); + }) + }else{ + $(form).on('submit', function () { + setValue(this, me); + }) + } + break; + } + } + if (options.initialContent) { + if (options.autoClearinitialContent) { + var oldExecCommand = me.execCommand; + me.execCommand = function () { + me.fireEvent('firstBeforeExecCommand'); + return oldExecCommand.apply(me, arguments); + }; + this._setDefaultContent(options.initialContent); + } else + this.setContent(options.initialContent, false, true); + } + + //编辑器不能为空内容 + + if (domUtils.isEmptyNode(me.body)) { + me.body.innerHTML = '

          ' + (browser.ie ? '' : '
          ') + '

          '; + } + //如果要求focus, 就把光标定位到内容开始 + if (options.focus) { + setTimeout(function () { + me.focus(me.options.focusInEnd); + //如果自动清除开着,就不需要做selectionchange; + !me.options.autoClearinitialContent && me._selectionChange(); + }, 0); + } + if (!me.container) { + me.container = cont.parentNode; + } + + me._bindshortcutKeys(); + me.isReady = 1; + me.fireEvent('ready'); + options.onready && options.onready.call(me); + if(!browser.ie || browser.ie9above){ + + $(me.body).on( 'blur focus', function (e) { + var nSel = me.selection.getNative(); + //chrome下会出现alt+tab切换时,导致选区位置不对 + if (e.type == 'blur') { + if(nSel.rangeCount > 0 ){ + me._bakRange = nSel.getRangeAt(0); + } + } else { + try { + me._bakRange && nSel.addRange(me._bakRange) + } catch (e) { + } + me._bakRange = null; + } + }); + } + + !options.isShow && me.setHide(); + options.readonly && me.setDisabled(); + }, + /** + * 同步编辑器的数据,为提交数据做准备,主要用于你是手动提交的情况 + * @name sync + * @grammar editor.sync(); //从编辑器的容器向上查找,如果找到就同步数据 + * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下 + * @desc + * 后台取得数据得键值使用你容器上得''name''属性,如果没有就使用参数传入的''textarea'' + * @example + * editor.sync(); + * form.sumbit(); //form变量已经指向了form元素 + * + */ + sync: function (formId) { + var me = this, + form = formId ? document.getElementById(formId) : + domUtils.findParent(me.body.parentNode, function (node) { + return node.tagName == 'FORM' + }, true); + form && setValue(form, me); + }, + /** + * 设置编辑器高度 + * @name setHeight + * @grammar editor.setHeight(number); //纯数值,不带单位 + */ + setHeight: function (height,notSetHeight) { + !notSetHeight && (this.options.initialFrameHeight = height); + if(this.options.autoHeightEnabled){ + $(this.body).css({ + 'min-height':height + 'px' + }); + if(browser.ie && browser.version <= 6 && this.container){ + this.container.style.height = height ; + this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"'); + } + }else{ + $(this.body).height(height) + } + this.fireEvent('resize'); + }, + /** + * 设置编辑器宽度 + * @name setWidth + * @grammar editor.setWidth(number); //纯数值,不带单位 + */ + setWidth:function(width){ + this.$container && this.$container.width(width); + $(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1); + this.fireEvent('resize'); + }, + addshortcutkey: function (cmd, keys) { + var obj = {}; + if (keys) { + obj[cmd] = keys + } else { + obj = cmd; + } + utils.extend(this.shortcutkeys, obj) + }, + _bindshortcutKeys: function () { + var me = this, shortcutkeys = this.shortcutkeys; + me.addListener('keydown', function (type, e) { + var keyCode = e.keyCode || e.which; + for (var i in shortcutkeys) { + var tmp = shortcutkeys[i].split(','); + for (var t = 0, ti; ti = tmp[t++];) { + ti = ti.split(':'); + var key = ti[0], param = ti[1]; + if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) { + if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0) + && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1) + && keyCode == RegExp.$3 + ) || + keyCode == RegExp.$1 + ) { + if (me.queryCommandState(i,param) != -1) + me.execCommand(i, param); + domUtils.preventDefault(e); + } + } + } + + } + }); + }, + /** + * 获取编辑器内容 + * @name getContent + * @grammar editor.getContent() => String //若编辑器中只包含字符"<p><br /></p/>"会返回空。 + * @grammar editor.getContent(fn) => String + * @example + * getContent默认是会现调用hasContents来判断编辑器是否为空,如果是,就直接返回空字符串 + * 你也可以传入一个fn来接替hasContents的工作,定制判断的规则 + * editor.getContent(function(){ + * return false //编辑器没有内容 ,getContent直接返回空 + * }) + */ + getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) { + var me = this; + if (cmd && utils.isFunction(cmd)) { + fn = cmd; + cmd = ''; + } + if (fn ? !fn() : !this.hasContents()) { + return ''; + } + me.fireEvent('beforegetcontent'); + var root = UM.htmlparser(me.body.innerHTML,ignoreBlank); + me.filterOutputRule(root); + me.fireEvent('aftergetcontent',root); + return root.toHtml(formatter); + }, + /** + * 取得完整的html代码,可以直接显示成完整的html文档 + * @name getAllHtml + * @grammar editor.getAllHtml() => String + */ + getAllHtml: function () { + var me = this, + headHtml = [], + html = ''; + me.fireEvent('getAllHtml', headHtml); + if (browser.ie && browser.version > 8) { + var headHtmlForIE9 = ''; + utils.each(me.document.styleSheets, function (si) { + headHtmlForIE9 += ( si.href ? '' : ''); + }); + utils.each(me.document.getElementsByTagName('script'), function (si) { + headHtmlForIE9 += si.outerHTML; + }); + } + return '' + (me.options.charset ? '' : '') + + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '' + + '' + me.getContent(null, null, true) + ''; + }, + /** + * 得到编辑器的纯文本内容,但会保留段落格式 + * @name getPlainTxt + * @grammar editor.getPlainTxt() => String + */ + getPlainTxt: function () { + var reg = new RegExp(domUtils.fillChar, 'g'), + html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理 + html = html.replace(/<(p|div)[^>]*>(| )<\/\1>/gi, '\n') + .replace(//gi, '\n') + .replace(/<[^>/]+>/g, '') + .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) { + return dtd.$block[c] ? '\n' : b ? b : ''; + }); + //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0 + return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/ /g, ' '); + }, + + /** + * 获取编辑器中的纯文本内容,没有段落格式 + * @name getContentTxt + * @grammar editor.getContentTxt() => String + */ + getContentTxt: function () { + var reg = new RegExp(domUtils.fillChar, 'g'); + //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0 + return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' '); + }, + + /** + * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值,则必须放在ready方法内部执行 + * @name setContent + * @grammar editor.setContent(html) + * @example + * var editor = new UM.ui.Editor() + * editor.ready(function(){ + * //需要ready后执行,否则可能报错 + * editor.setContent("欢迎使用UEditor!"); + * }) + */ + setContent: function (html, isAppendTo, notFireSelectionchange) { + var me = this; + + me.fireEvent('beforesetcontent', html); + var root = UM.htmlparser(html); + me.filterInputRule(root); + html = root.toHtml(); + + + me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html; + + + function isCdataDiv(node){ + return node.tagName == 'DIV' && node.getAttribute('cdata_tag'); + } + //给文本或者inline节点套p标签 + if (me.options.enterTag == 'p') { + + var child = this.body.firstChild, tmpNode; + if (!child || child.nodeType == 1 && + (dtd.$cdata[child.tagName] || isCdataDiv(child) || + domUtils.isCustomeNode(child) + ) + && child === this.body.lastChild) { + this.body.innerHTML = '

          ' + (browser.ie ? ' ' : '
          ') + '

          ' + this.body.innerHTML; + + } else { + var p = me.document.createElement('p'); + while (child) { + while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) { + tmpNode = child.nextSibling; + p.appendChild(child); + child = tmpNode; + } + if (p.firstChild) { + if (!child) { + me.body.appendChild(p); + break; + } else { + child.parentNode.insertBefore(p, child); + p = me.document.createElement('p'); + } + } + child = child.nextSibling; + } + } + } + me.fireEvent('aftersetcontent'); + me.fireEvent('contentchange'); + + !notFireSelectionchange && me._selectionChange(); + //清除保存的选区 + me._bakRange = me._bakIERange = me._bakNativeRange = null; + //trace:1742 setContent后gecko能得到焦点问题 + var geckoSel; + if (browser.gecko && (geckoSel = this.selection.getNative())) { + geckoSel.removeAllRanges(); + } + if(me.options.autoSyncData){ + me.form && setValue(me.form,me); + } + }, + + /** + * 让编辑器获得焦点,toEnd确定focus位置 + * @name focus + * @grammar editor.focus([toEnd]) //默认focus到编辑器头部,toEnd为true时focus到内容尾部 + */ + focus: function (toEnd) { + try { + var me = this, + rng = me.selection.getRange(); + if (toEnd) { + rng.setStartAtLast(me.body.lastChild).setCursor(false, true); + } else { + rng.select(true); + } + this.fireEvent('focus'); + } catch (e) { + } + }, + /** + * 使编辑区域失去焦点 + */ + blur:function(){ + var sel = this.selection.getNative(); + sel.empty ? sel.empty() : sel.removeAllRanges(); + this.fireEvent('blur') + }, + /** + * 判断编辑器当前是否获得了焦点 + */ + isFocus : function(){ + if(this.fireEvent('isfocus')===true){ + return true; + } + return this.selection.isFocus(); + }, + + /** + * 初始化UE事件及部分事件代理 + * @private + * @ignore + */ + _initEvents: function () { + var me = this, + cont = me.body, + _proxyDomEvent = function(){ + me._proxyDomEvent.apply(me, arguments); + }; + + $(cont) + .on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent) + .on( 'focus blur', _proxyDomEvent) + .on('mouseup keydown', function (evt) { + //特殊键不触发selectionchange + if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) { + return; + } + if (evt.button == 2)return; + me._selectionChange(250, evt); + }); + }, + /** + * 触发事件代理 + * @private + * @ignore + */ + _proxyDomEvent: function (evt) { + return this.fireEvent(evt.type.replace(/^on/, ''), evt); + }, + /** + * 变化选区 + * @private + * @ignore + */ + _selectionChange: function (delay, evt) { + var me = this; + //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1) +// if ( !me.selection.isFocus() ){ +// return; +// } + + + var hackForMouseUp = false; + var mouseX, mouseY; + if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') { + var range = this.selection.getRange(); + if (!range.collapsed) { + hackForMouseUp = true; + mouseX = evt.clientX; + mouseY = evt.clientY; + } + } + clearTimeout(_selectionChangeTimer); + _selectionChangeTimer = setTimeout(function () { + if (!me.selection.getNative()) { + return; + } + //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值. + //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响 + var ieRange; + if (hackForMouseUp && me.selection.getNative().type == 'None') { + ieRange = me.document.body.createTextRange(); + try { + ieRange.moveToPoint(mouseX, mouseY); + } catch (ex) { + ieRange = null; + } + } + var bakGetIERange; + if (ieRange) { + bakGetIERange = me.selection.getIERange; + me.selection.getIERange = function () { + return ieRange; + }; + } + me.selection.cache(); + if (bakGetIERange) { + me.selection.getIERange = bakGetIERange; + } + if (me.selection._cachedRange && me.selection._cachedStartElement) { + me.fireEvent('beforeselectionchange'); + // 第二个参数causeByUi为true代表由用户交互造成的selectionchange. + me.fireEvent('selectionchange', !!evt); + me.fireEvent('afterselectionchange'); + me.selection.clear(); + } + }, delay || 50); + }, + _callCmdFn: function (fnName, args) { + args = Array.prototype.slice.call(args,0); + var cmdName = args.shift().toLowerCase(), + cmd, cmdFn; + cmd = this.commands[cmdName] || UM.commands[cmdName]; + cmdFn = cmd && cmd[fnName]; + //没有querycommandstate或者没有command的都默认返回0 + if ((!cmd || !cmdFn) && fnName == 'queryCommandState') { + return 0; + } else if (cmdFn) { + return cmdFn.apply(this, [cmdName].concat(args)); + } + }, + + /** + * 执行编辑命令cmdName,完成富文本编辑效果 + * @name execCommand + * @grammar editor.execCommand(cmdName) => {*} + */ + execCommand: function (cmdName) { + if(!this.isFocus()){ + var bakRange = this.selection._bakRange; + if(bakRange){ + bakRange.select() + }else{ + this.focus(true) + } + + } + cmdName = cmdName.toLowerCase(); + var me = this, + result, + cmd = me.commands[cmdName] || UM.commands[cmdName]; + if (!cmd || !cmd.execCommand) { + return null; + } + if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) { + me.__hasEnterExecCommand = true; + if (me.queryCommandState.apply(me,arguments) != -1) { + me.fireEvent('saveScene'); + me.fireEvent('beforeexeccommand', cmdName); + result = this._callCmdFn('execCommand', arguments); + (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange'); + me.fireEvent('afterexeccommand', cmdName); + me.fireEvent('saveScene'); + } + me.__hasEnterExecCommand = false; + } else { + result = this._callCmdFn('execCommand', arguments); + (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange') + } + (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange(); + return result; + }, + /** + * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态 + * @name queryCommandState + * @grammar editor.queryCommandState(cmdName) => (-1|0|1) + * @desc + * * ''-1'' 当前命令不可用 + * * ''0'' 当前命令可用 + * * ''1'' 当前命令已经执行过了 + */ + queryCommandState: function (cmdName) { + try{ + return this._callCmdFn('queryCommandState', arguments); + }catch(e){ + return 0 + } + + }, + + /** + * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值 + * @name queryCommandValue + * @grammar editor.queryCommandValue(cmdName) => {*} + */ + queryCommandValue: function (cmdName) { + try{ + return this._callCmdFn('queryCommandValue', arguments); + }catch(e){ + return null + } + }, + /** + * 检查编辑区域中是否有内容,若包含tags中的节点类型,直接返回true + * @name hasContents + * @desc + * 默认有文本内容,或者有以下节点都不认为是空 + * {table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1} + * @grammar editor.hasContents() => (true|false) + * @grammar editor.hasContents(tags) => (true|false) //若文档中包含tags数组里对应的tag,直接返回true + * @example + * editor.hasContents(['span']) //如果编辑器里有这些,不认为是空 + */ + hasContents: function (tags) { + if (tags) { + for (var i = 0, ci; ci = tags[i++];) { + if (this.body.getElementsByTagName(ci).length > 0) { + return true; + } + } + } + if (!domUtils.isEmptyBlock(this.body)) { + return true + } + //随时添加,定义的特殊标签如果存在,不能认为是空 + tags = ['div']; + for (i = 0; ci = tags[i++];) { + var nodes = domUtils.getElementsByTagName(this.body, ci); + for (var n = 0, cn; cn = nodes[n++];) { + if (domUtils.isCustomeNode(cn)) { + return true; + } + } + } + return false; + }, + /** + * 重置编辑器,可用来做多个tab使用同一个编辑器实例 + * @name reset + * @desc + * * 清空编辑器内容 + * * 清空回退列表 + * @grammar editor.reset() + */ + reset: function () { + this.fireEvent('reset'); + }, + isEnabled: function(){ + return this._isEnabled != true; + }, + + setEnabled: function () { + var me = this, range; + + me.body.contentEditable = true; + + /* 恢复选区 */ + if (me.lastBk) { + range = me.selection.getRange(); + try { + range.moveToBookmark(me.lastBk); + delete me.lastBk + } catch (e) { + range.setStartAtFirst(me.body).collapse(true) + } + range.select(true); + } + + /* 恢复query函数 */ + if (me.bkqueryCommandState) { + me.queryCommandState = me.bkqueryCommandState; + delete me.bkqueryCommandState; + } + + /* 恢复原生事件 */ + if (me._bkproxyDomEvent) { + me._proxyDomEvent = me._bkproxyDomEvent; + delete me._bkproxyDomEvent; + } + + /* 触发事件 */ + me.fireEvent('setEnabled'); + }, + /** + * 设置当前编辑区域可以编辑 + * @name enable + * @grammar editor.enable() + */ + enable: function () { + return this.setEnabled(); + }, + setDisabled: function (except, keepDomEvent) { + var me = this; + + me.body.contentEditable = false; + me._except = except ? utils.isArray(except) ? except : [except] : []; + + /* 备份最后的选区 */ + if (!me.lastBk) { + me.lastBk = me.selection.getRange().createBookmark(true); + } + + /* 备份并重置query函数 */ + if(!me.bkqueryCommandState) { + me.bkqueryCommandState = me.queryCommandState; + me.queryCommandState = function (type) { + if (utils.indexOf(me._except, type) != -1) { + return me.bkqueryCommandState.apply(me, arguments); + } + return -1; + }; + } + + /* 备份并墙原生事件 */ + if(!keepDomEvent && !me._bkproxyDomEvent) { + me._bkproxyDomEvent = me._proxyDomEvent; + me._proxyDomEvent = function () { + return false; + }; + } + + /* 触发事件 */ + me.fireEvent('selectionchange'); + me.fireEvent('setDisabled', me._except); + }, + /** 设置当前编辑区域不可编辑,except中的命令除外 + * @name disable + * @grammar editor.disable() + * @grammar editor.disable(except) //例外的命令,也即即使设置了disable,此处配置的命令仍然可以执行 + * @example + * //禁用工具栏中除加粗和插入图片之外的所有功能 + * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array + */ + disable: function (except) { + return this.setDisabled(except); + }, + /** + * 设置默认内容 + * @ignore + * @private + * @param {String} cont 要存入的内容 + */ + _setDefaultContent: function () { + function clear() { + var me = this; + if (me.document.getElementById('initContent')) { + me.body.innerHTML = '

          ' + (ie ? '' : '
          ') + '

          '; + me.removeListener('firstBeforeExecCommand focus', clear); + setTimeout(function () { + me.focus(); + me._selectionChange(); + }, 0) + } + } + + return function (cont) { + var me = this; + me.body.innerHTML = '

          ' + cont + '

          '; + + me.addListener('firstBeforeExecCommand focus', clear); + } + }(), + /** + * show方法的兼容版本 + * @private + * @ignore + */ + setShow: function () { + var me = this, range = me.selection.getRange(); + if (me.container.style.display == 'none') { + //有可能内容丢失了 + try { + range.moveToBookmark(me.lastBk); + delete me.lastBk + } catch (e) { + range.setStartAtFirst(me.body).collapse(true) + } + //ie下focus实效,所以做了个延迟 + setTimeout(function () { + range.select(true); + }, 100); + me.container.style.display = ''; + } + + }, + /** + * 显示编辑器 + * @name show + * @grammar editor.show() + */ + show: function () { + return this.setShow(); + }, + /** + * hide方法的兼容版本 + * @private + * @ignore + */ + setHide: function () { + var me = this; + if (!me.lastBk) { + me.lastBk = me.selection.getRange().createBookmark(true); + } + me.container.style.display = 'none' + }, + /** + * 隐藏编辑器 + * @name hide + * @grammar editor.hide() + */ + hide: function () { + return this.setHide(); + }, + /** + * 根据制定的路径,获取对应的语言资源 + * @name getLang + * @grammar editor.getLang(path) => (JSON|String) 路径根据的是lang目录下的语言文件的路径结构 + * @example + * editor.getLang('contextMenu.delete') //如果当前是中文,那返回是的是删除 + */ + getLang: function (path) { + var lang = UM.I18N[this.options.lang]; + if (!lang) { + throw Error("not import language file"); + } + path = (path || "").split("."); + for (var i = 0, ci; ci = path[i++];) { + lang = lang[ci]; + if (!lang)break; + } + return lang; + }, + /** + * 计算编辑器当前内容的长度 + * @name getContentLength + * @grammar editor.getContentLength(ingoneHtml,tagNames) => + * @example + * editor.getLang(true) + */ + getContentLength: function (ingoneHtml, tagNames) { + var count = this.getContent(false,false,true).length; + if (ingoneHtml) { + tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']); + count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length; + for (var i = 0, ci; ci = tagNames[i++];) { + count += this.body.getElementsByTagName(ci).length; + } + } + return count; + }, + addInputRule: function (rule,ignoreUndo) { + rule.ignoreUndo = ignoreUndo; + this.inputRules.push(rule); + }, + filterInputRule: function (root,isUndoLoad) { + for (var i = 0, ci; ci = this.inputRules[i++];) { + if(isUndoLoad && ci.ignoreUndo){ + continue; + } + ci.call(this, root) + } + }, + addOutputRule: function (rule,ignoreUndo) { + rule.ignoreUndo = ignoreUndo; + this.outputRules.push(rule) + }, + filterOutputRule: function (root,isUndoLoad) { + for (var i = 0, ci; ci = this.outputRules[i++];) { + if(isUndoLoad && ci.ignoreUndo){ + continue; + } + ci.call(this, root) + } + } + }; + utils.inherits(Editor, EventBase); + })(); + + /** + * @file + * @name UM.filterWord + * @short filterWord + * @desc 用来过滤word粘贴过来的字符串 + * @import editor.js,core/utils.js + * @anthor zhanyi + */ + var filterWord = UM.filterWord = function () { + + //是否是word过来的内容 + function isWordDocument( str ) { + return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str ); + } + //去掉小数 + function transUnit( v ) { + v = v.replace( /[\d.]+\w+/g, function ( m ) { + return utils.transUnitToPx(m); + } ); + return v; + } + + function filterPasteWord( str ) { + return str.replace(/[\t\r\n]+/g,' ') + .replace( //ig, "" ) + //转换图片 + .replace(/]*>[\s\S]*?.<\/v:shape>/gi,function(str){ + //opera能自己解析出image所这里直接返回空 + if(browser.opera){ + return ''; + } + try{ + //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中 + if(/Bitmap/i.test(str)){ + return ''; + } + var width = str.match(/width:([ \d.]*p[tx])/i)[1], + height = str.match(/height:([ \d.]*p[tx])/i)[1], + src = str.match(/src=\s*"([^"]*)"/i)[1]; + return ''; + } catch(e){ + return ''; + } + }) + //针对wps添加的多余标签处理 + .replace(/<\/?div[^>]*>/g,'') + //去掉多余的属性 + .replace( /v:\w+=(["']?)[^'"]+\1/g, '' ) + .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" ) + .replace( /

          ]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "

          $1

          " ) + //去掉多余的属性 + .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){ + //保留list的标示 + return name == 'class' && val == 'MsoListParagraph' ? str : '' + }) + //清除多余的font/span不能匹配 有可能是空格 + .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){ + return c.replace(/[\t\r\n ]+/g,' ') + }) + //处理style的问题 + .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) { + var n = [], + s = style.replace( /^\s+|\s+$/, '' ) + .replace(/'/g,'\'') + .replace( /"/gi, "'" ) + .split( /;\s*/g ); + + for ( var i = 0,v; v = s[i];i++ ) { + + var name, value, + parts = v.split( ":" ); + + if ( parts.length == 2 ) { + name = parts[0].toLowerCase(); + value = parts[1].toLowerCase(); + if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0 + || + /^(margin)\w*/.test(name) && /^0\w+$/.test(value) + ){ + continue; + } + + switch ( name ) { + case "mso-padding-alt": + case "mso-padding-top-alt": + case "mso-padding-right-alt": + case "mso-padding-bottom-alt": + case "mso-padding-left-alt": + case "mso-margin-alt": + case "mso-margin-top-alt": + case "mso-margin-right-alt": + case "mso-margin-bottom-alt": + case "mso-margin-left-alt": + //ie下会出现挤到一起的情况 + //case "mso-table-layout-alt": + case "mso-height": + case "mso-width": + case "mso-vertical-align-alt": + //trace:1819 ff下会解析出padding在table上 + if(!/]/.test(html)) { + return UM.htmlparser(html).children[0] + } else { + return new uNode({ + type:'element', + children:[], + tagName:html + }) + } + }; + uNode.createText = function (data,noTrans) { + return new UM.uNode({ + type:'text', + 'data':noTrans ? data : utils.unhtml(data || '') + }) + }; + function nodeToHtml(node, arr, formatter, current) { + switch (node.type) { + case 'root': + for (var i = 0, ci; ci = node.children[i++];) { + //插入新行 + if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { + insertLine(arr, current, true); + insertIndent(arr, current) + } + nodeToHtml(ci, arr, formatter, current) + } + break; + case 'text': + isText(node, arr); + break; + case 'element': + isElement(node, arr, formatter, current); + break; + case 'comment': + isComment(node, arr, formatter); + } + return arr; + } + + function isText(node, arr) { + if(node.parentNode.tagName == 'pre'){ + //源码模式下输入html标签,不能做转换处理,直接输出 + arr.push(node.data) + }else{ + arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,'  ')) + } + + } + + function isElement(node, arr, formatter, current) { + var attrhtml = ''; + if (node.attrs) { + attrhtml = []; + var attrs = node.attrs; + for (var a in attrs) { + //这里就针对 + //

          '

          + //这里边的\"做转换,要不用innerHTML直接被截断了,属性src + //有可能做的不够 + attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) { + return '"' + }) : utils.unhtml(attrs[a])) + '"' : '')) + } + attrhtml = attrhtml.join(' '); + } + arr.push('<' + node.tagName + + (attrhtml ? ' ' + attrhtml : '') + + (dtd.$empty[node.tagName] ? '\/' : '' ) + '>' + ); + //插入新行 + if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { + if(node.children && node.children.length){ + current = insertLine(arr, current, true); + insertIndent(arr, current) + } + + } + if (node.children && node.children.length) { + for (var i = 0, ci; ci = node.children[i++];) { + if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) { + insertLine(arr, current); + insertIndent(arr, current) + } + nodeToHtml(ci, arr, formatter, current) + } + } + if (!dtd.$empty[node.tagName]) { + if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') { + + if(node.children && node.children.length){ + current = insertLine(arr, current); + insertIndent(arr, current) + } + } + arr.push('<\/' + node.tagName + '>'); + } + + } + + function isComment(node, arr) { + arr.push(''); + } + + function getNodeById(root, id) { + var node; + if (root.type == 'element' && root.getAttr('id') == id) { + return root; + } + if (root.children && root.children.length) { + for (var i = 0, ci; ci = root.children[i++];) { + if (node = getNodeById(ci, id)) { + return node; + } + } + } + } + + function getNodesByTagName(node, tagName, arr) { + if (node.type == 'element' && node.tagName == tagName) { + arr.push(node); + } + if (node.children && node.children.length) { + for (var i = 0, ci; ci = node.children[i++];) { + getNodesByTagName(ci, tagName, arr) + } + } + } + function nodeTraversal(root,fn){ + if(root.children && root.children.length){ + for(var i= 0,ci;ci=root.children[i];){ + nodeTraversal(ci,fn); + //ci被替换的情况,这里就不再走 fn了 + if(ci.parentNode ){ + if(ci.children && ci.children.length){ + fn(ci) + } + if(ci.parentNode) i++ + } + } + }else{ + fn(root) + } + + } + uNode.prototype = { + + /** + * 当前节点对象,转换成html文本 + * @method toHtml + * @return { String } 返回转换后的html字符串 + * @example + * ```javascript + * node.toHtml(); + * ``` + */ + + /** + * 当前节点对象,转换成html文本 + * @method toHtml + * @param { Boolean } formatter 是否格式化返回值 + * @return { String } 返回转换后的html字符串 + * @example + * ```javascript + * node.toHtml( true ); + * ``` + */ + toHtml:function (formatter) { + var arr = []; + nodeToHtml(this, arr, formatter, 0); + return arr.join('') + }, + + /** + * 获取节点的html内容 + * @method innerHTML + * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 + * @return { String } 返回节点的html内容 + * @example + * ```javascript + * var htmlstr = node.innerHTML(); + * ``` + */ + + /** + * 设置节点的html内容 + * @method innerHTML + * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 + * @param { String } htmlstr 传入要设置的html内容 + * @return { UM.uNode } 返回节点本身 + * @example + * ```javascript + * node.innerHTML('text'); + * ``` + */ + innerHTML:function (htmlstr) { + if (this.type != 'element' || dtd.$empty[this.tagName]) { + return this; + } + if (utils.isString(htmlstr)) { + if(this.children){ + for (var i = 0, ci; ci = this.children[i++];) { + ci.parentNode = null; + } + } + this.children = []; + var tmpRoot = UM.htmlparser(htmlstr); + for (var i = 0, ci; ci = tmpRoot.children[i++];) { + this.children.push(ci); + ci.parentNode = this; + } + return this; + } else { + var tmpRoot = new UM.uNode({ + type:'root', + children:this.children + }); + return tmpRoot.toHtml(); + } + }, + + /** + * 获取节点的纯文本内容 + * @method innerText + * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 + * @return { String } 返回节点的存文本内容 + * @example + * ```javascript + * var textStr = node.innerText(); + * ``` + */ + + /** + * 设置节点的纯文本内容 + * @method innerText + * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点 + * @param { String } textStr 传入要设置的文本内容 + * @return { UM.uNode } 返回节点本身 + * @example + * ```javascript + * node.innerText('text'); + * ``` + */ + innerText:function (textStr,noTrans) { + if (this.type != 'element' || dtd.$empty[this.tagName]) { + return this; + } + if (textStr) { + if(this.children){ + for (var i = 0, ci; ci = this.children[i++];) { + ci.parentNode = null; + } + } + this.children = []; + this.appendChild(uNode.createText(textStr,noTrans)); + return this; + } else { + return this.toHtml().replace(/<[^>]+>/g, ''); + } + }, + + /** + * 获取当前对象的data属性 + * @method getData + * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性 + * @example + * ```javascript + * node.getData(); + * ``` + */ + getData:function () { + if (this.type == 'element') + return ''; + return this.data + }, + + /** + * 获取当前节点下的第一个子节点 + * @method firstChild + * @return { UM.uNode } 返回第一个子节点 + * @example + * ```javascript + * node.firstChild(); //返回第一个子节点 + * ``` + */ + firstChild:function () { +// if (this.type != 'element' || dtd.$empty[this.tagName]) { +// return this; +// } + return this.children ? this.children[0] : null; + }, + + /** + * 获取当前节点下的最后一个子节点 + * @method lastChild + * @return { UM.uNode } 返回最后一个子节点 + * @example + * ```javascript + * node.lastChild(); //返回最后一个子节点 + * ``` + */ + lastChild:function () { +// if (this.type != 'element' || dtd.$empty[this.tagName] ) { +// return this; +// } + return this.children ? this.children[this.children.length - 1] : null; + }, + + /** + * 获取和当前节点有相同父亲节点的前一个节点 + * @method previousSibling + * @return { UM.uNode } 返回前一个节点 + * @example + * ```javascript + * node.children[2].previousSibling(); //返回子节点node.children[1] + * ``` + */ + previousSibling : function(){ + var parent = this.parentNode; + for (var i = 0, ci; ci = parent.children[i]; i++) { + if (ci === this) { + return i == 0 ? null : parent.children[i-1]; + } + } + + }, + + /** + * 获取和当前节点有相同父亲节点的后一个节点 + * @method nextSibling + * @return { UM.uNode } 返回后一个节点,找不到返回null + * @example + * ```javascript + * node.children[2].nextSibling(); //如果有,返回子节点node.children[3] + * ``` + */ + nextSibling : function(){ + var parent = this.parentNode; + for (var i = 0, ci; ci = parent.children[i++];) { + if (ci === this) { + return parent.children[i]; + } + } + }, + + /** + * 用新的节点替换当前节点 + * @method replaceChild + * @param { UM.uNode } target 要替换成该节点参数 + * @param { UM.uNode } source 要被替换掉的节点 + * @return { UM.uNode } 返回替换之后的节点对象 + * @example + * ```javascript + * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点 + * ``` + */ + replaceChild:function (target, source) { + if (this.children) { + if(target.parentNode){ + target.parentNode.removeChild(target); + } + for (var i = 0, ci; ci = this.children[i]; i++) { + if (ci === source) { + this.children.splice(i, 1, target); + source.parentNode = null; + target.parentNode = this; + return target; + } + } + } + }, + + /** + * 在节点的子节点列表最后位置插入一个节点 + * @method appendChild + * @param { UM.uNode } node 要插入的节点 + * @return { UM.uNode } 返回刚插入的子节点 + * @example + * ```javascript + * node.appendChild( newNode ); //在node内插入子节点newNode + * ``` + */ + appendChild:function (node) { + if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) { + if (!this.children) { + this.children = [] + } + if(node.parentNode){ + node.parentNode.removeChild(node); + } + for (var i = 0, ci; ci = this.children[i]; i++) { + if (ci === node) { + this.children.splice(i, 1); + break; + } + } + this.children.push(node); + node.parentNode = this; + return node; + } + + + }, + + /** + * 在传入节点的前面插入一个节点 + * @method insertBefore + * @param { UM.uNode } target 要插入的节点 + * @param { UM.uNode } source 在该参数节点前面插入 + * @return { UM.uNode } 返回刚插入的子节点 + * @example + * ```javascript + * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode + * ``` + */ + insertBefore:function (target, source) { + if (this.children) { + if(target.parentNode){ + target.parentNode.removeChild(target); + } + for (var i = 0, ci; ci = this.children[i]; i++) { + if (ci === source) { + this.children.splice(i, 0, target); + target.parentNode = this; + return target; + } + } + + } + }, + + /** + * 在传入节点的后面插入一个节点 + * @method insertAfter + * @param { UM.uNode } target 要插入的节点 + * @param { UM.uNode } source 在该参数节点后面插入 + * @return { UM.uNode } 返回刚插入的子节点 + * @example + * ```javascript + * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode + * ``` + */ + insertAfter:function (target, source) { + if (this.children) { + if(target.parentNode){ + target.parentNode.removeChild(target); + } + for (var i = 0, ci; ci = this.children[i]; i++) { + if (ci === source) { + this.children.splice(i + 1, 0, target); + target.parentNode = this; + return target; + } + + } + } + }, + + /** + * 从当前节点的子节点列表中,移除节点 + * @method removeChild + * @param { UM.uNode } node 要移除的节点引用 + * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置 + * @return { * } 返回刚移除的子节点 + * @example + * ```javascript + * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置 + * ``` + */ + removeChild:function (node,keepChildren) { + if (this.children) { + for (var i = 0, ci; ci = this.children[i]; i++) { + if (ci === node) { + this.children.splice(i, 1); + ci.parentNode = null; + if(keepChildren && ci.children && ci.children.length){ + for(var j= 0,cj;cj=ci.children[j];j++){ + this.children.splice(i+j,0,cj); + cj.parentNode = this; + + } + } + return ci; + } + } + } + }, + + /** + * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值 + * @method getAttr + * @param { String } attrName 要获取的属性名称 + * @return { * } 返回attrs对象下的属性值 + * @example + * ```javascript + * node.getAttr('title'); + * ``` + */ + getAttr:function (attrName) { + return this.attrs && this.attrs[attrName.toLowerCase()] + }, + + /** + * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值 + * @method setAttr + * @param { String } attrName 要设置的属性名称 + * @param { * } attrVal 要设置的属性值,类型视设置的属性而定 + * @return { * } 返回attrs对象下的属性值 + * @example + * ```javascript + * node.setAttr('title','标题'); + * ``` + */ + setAttr:function (attrName, attrVal) { + if (!attrName) { + delete this.attrs; + return; + } + if(!this.attrs){ + this.attrs = {}; + } + if (utils.isObject(attrName)) { + for (var a in attrName) { + if (!attrName[a]) { + delete this.attrs[a] + } else { + this.attrs[a.toLowerCase()] = attrName[a]; + } + } + } else { + if (!attrVal) { + delete this.attrs[attrName] + } else { + this.attrs[attrName.toLowerCase()] = attrVal; + } + + } + }, + hasAttr: function( attrName ){ + var attrVal = this.getAttr( attrName ); + return ( attrVal !== null ) && ( attrVal !== undefined ); + }, + /** + * 获取当前节点在父节点下的位置索引 + * @method getIndex + * @return { Number } 返回索引数值,如果没有父节点,返回-1 + * @example + * ```javascript + * node.getIndex(); + * ``` + */ + getIndex:function(){ + var parent = this.parentNode; + for(var i= 0,ci;ci=parent.children[i];i++){ + if(ci === this){ + return i; + } + } + return -1; + }, + + /** + * 在当前节点下,根据id查找节点 + * @method getNodeById + * @param { String } id 要查找的id + * @return { UM.uNode } 返回找到的节点 + * @example + * ```javascript + * node.getNodeById('textId'); + * ``` + */ + getNodeById:function (id) { + var node; + if (this.children && this.children.length) { + for (var i = 0, ci; ci = this.children[i++];) { + if (node = getNodeById(ci, id)) { + return node; + } + } + } + }, + + /** + * 在当前节点下,根据元素名称查找节点列表 + * @method getNodesByTagName + * @param { String } tagNames 要查找的元素名称 + * @return { Array } 返回找到的节点列表 + * @example + * ```javascript + * node.getNodesByTagName('span'); + * ``` + */ + getNodesByTagName:function (tagNames) { + tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' '); + var arr = [], me = this; + utils.each(tagNames, function (tagName) { + if (me.children && me.children.length) { + for (var i = 0, ci; ci = me.children[i++];) { + getNodesByTagName(ci, tagName, arr) + } + } + }); + return arr; + }, + + /** + * 根据样式名称,获取节点的样式值 + * @method getStyle + * @param { String } name 要获取的样式名称 + * @return { String } 返回样式值 + * @example + * ```javascript + * node.getStyle('font-size'); + * ``` + */ + getStyle:function (name) { + var cssStyle = this.getAttr('style'); + if (!cssStyle) { + return '' + } + var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i'); + var match = cssStyle.match(reg); + if (match && match[0]) { + return match[2] + } + return ''; + }, + + /** + * 给节点设置样式 + * @method setStyle + * @param { String } name 要设置的的样式名称 + * @param { String } val 要设置的的样值 + * @example + * ```javascript + * node.setStyle('font-size', '12px'); + * ``` + */ + setStyle:function (name, val) { + function exec(name, val) { + var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi'); + cssStyle = cssStyle.replace(reg, '$1'); + if (val) { + cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle + } + + } + + var cssStyle = this.getAttr('style'); + if (!cssStyle) { + cssStyle = ''; + } + if (utils.isObject(name)) { + for (var a in name) { + exec(a, name[a]) + } + } else { + exec(name, val) + } + this.setAttr('style', utils.trim(cssStyle)) + }, + hasClass: function( className ){ + if( this.hasAttr('class') ) { + var classNames = this.getAttr('class').split(/\s+/), + hasClass = false; + $.each(classNames, function(key, item){ + if( item === className ) { + hasClass = true; + } + }); + return hasClass; + } else { + return false; + } + }, + addClass: function( className ){ + + var classes = null, + hasClass = false; + + if( this.hasAttr('class') ) { + + classes = this.getAttr('class'); + classes = classes.split(/\s+/); + + classes.forEach( function( item ){ + + if( item===className ) { + hasClass = true; + return; + } + + } ); + + !hasClass && classes.push( className ); + + this.setAttr('class', classes.join(" ")); + + } else { + this.setAttr('class', className); + } + + }, + removeClass: function( className ){ + if( this.hasAttr('class') ) { + var cl = this.getAttr('class'); + cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),''); + this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' ')); + } + }, + /** + * 传入一个函数,递归遍历当前节点下的所有节点 + * @method traversal + * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数 + * @example + * ```javascript + * traversal(node, function(){ + * console.log(node.type); + * }); + * ``` + */ + traversal:function(fn){ + if(this.children && this.children.length){ + nodeTraversal(this,fn); + } + return this; + } + } + })(); + +//html字符串转换成uNode节点 +//by zhanyi + var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) { + //todo 原来的方式 [^"'<>\/] 有\/就不能配对上
          这样的标签了 + //先去掉了,加上的原因忘了,这里先记录 + var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g, + re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g; + + //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除 + var allowEmptyTags = { + b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1, + sub:1,img:1,video:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1 + }; + htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), ''); + if(!ignoreBlank){ + htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){ + //br暂时单独处理 + if(b && allowEmptyTags[b.toLowerCase()]){ + return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,''); + } + return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),''); + }); + } + + var notTransAttrs = { + 'href':1, + 'src':1 + }; + + var uNode = UM.uNode, + needParentNode = { + 'td':'tr', + 'tr':['tbody','thead','tfoot'], + 'tbody':'table', + 'th':'tr', + 'thead':'table', + 'tfoot':'table', + 'caption':'table', + 'li':['ul', 'ol'], + 'dt':'dl', + 'dd':'dl', + 'option':'select' + }, + needChild = { + 'ol':'li', + 'ul':'li' + }; + + function text(parent, data) { + + if(needChild[parent.tagName]){ + var tmpNode = uNode.createElement(needChild[parent.tagName]); + parent.appendChild(tmpNode); + tmpNode.appendChild(uNode.createText(data)); + parent = tmpNode; + }else{ + + parent.appendChild(uNode.createText(data)); + } + } + + function element(parent, tagName, htmlattr) { + var needParentTag; + if (needParentTag = needParentNode[tagName]) { + var tmpParent = parent,hasParent; + while(tmpParent.type != 'root'){ + if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){ + parent = tmpParent; + hasParent = true; + break; + } + tmpParent = tmpParent.parentNode; + } + if(!hasParent){ + parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag) + } + } + //按dtd处理嵌套 +// if(parent.type != 'root' && !dtd[parent.tagName][tagName]) +// parent = parent.parentNode; + var elm = new uNode({ + parentNode:parent, + type:'element', + tagName:tagName.toLowerCase(), + //是自闭合的处理一下 + children:dtd.$empty[tagName] ? null : [] + }); + //如果属性存在,处理属性 + if (htmlattr) { + var attrs = {}, match; + while (match = re_attr.exec(htmlattr)) { + attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4]) + } + elm.attrs = attrs; + } + + parent.children.push(elm); + //如果是自闭合节点返回父亲节点 + return dtd.$empty[tagName] ? parent : elm + } + + function comment(parent, data) { + parent.children.push(new uNode({ + type:'comment', + data:data, + parentNode:parent + })); + } + + var match, currentIndex = 0, nextIndex = 0; + //设置根节点 + var root = new uNode({ + type:'root', + children:[] + }); + var currentParent = root; + + while (match = re_tag.exec(htmlstr)) { + currentIndex = match.index; + try{ + if (currentIndex > nextIndex) { + //text node + text(currentParent, htmlstr.slice(nextIndex, currentIndex)); + } + if (match[3]) { + + if(dtd.$cdata[currentParent.tagName]){ + text(currentParent, match[0]); + }else{ + //start tag + currentParent = element(currentParent, match[3].toLowerCase(), match[4]); + } + + + } else if (match[1]) { + if(currentParent.type != 'root'){ + if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){ + text(currentParent, match[0]); + }else{ + var tmpParent = currentParent; + while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){ + currentParent = currentParent.parentNode; + if(currentParent.type == 'root'){ + currentParent = tmpParent; + throw 'break' + } + } + //end tag + currentParent = currentParent.parentNode; + } + + } + + } else if (match[2]) { + //comment + comment(currentParent, match[2]) + } + }catch(e){} + + nextIndex = re_tag.lastIndex; + + } + //如果结束是文本,就有可能丢掉,所以这里手动判断一下 + //例如
        8. sdfsdfsdf
        9. sdfsdfsdfsdf + if (nextIndex < htmlstr.length) { + text(currentParent, htmlstr.slice(nextIndex)); + } + return root; + }; + /** + * @file + * @name UM.filterNode + * @short filterNode + * @desc 根据给定的规则过滤节点 + * @import editor.js,core/utils.js + * @anthor zhanyi + */ + var filterNode = UM.filterNode = function () { + function filterNode(node,rules){ + switch (node.type) { + case 'text': + break; + case 'element': + var val; + if(val = rules[node.tagName]){ + if(val === '-'){ + node.parentNode.removeChild(node) + }else if(utils.isFunction(val)){ + var parentNode = node.parentNode, + index = node.getIndex(); + val(node); + if(node.parentNode){ + if(node.children){ + for(var i = 0,ci;ci=node.children[i];){ + filterNode(ci,rules); + if(ci.parentNode){ + i++; + } + } + } + }else{ + for(var i = index,ci;ci=parentNode.children[i];){ + filterNode(ci,rules); + if(ci.parentNode){ + i++; + } + } + } + + + }else{ + var attrs = val['$']; + if(attrs && node.attrs){ + var tmpAttrs = {},tmpVal; + for(var a in attrs){ + tmpVal = node.getAttr(a); + //todo 只先对style单独处理 + if(a == 'style' && utils.isArray(attrs[a])){ + var tmpCssStyle = []; + utils.each(attrs[a],function(v){ + var tmp; + if(tmp = node.getStyle(v)){ + tmpCssStyle.push(v + ':' + tmp); + } + }); + tmpVal = tmpCssStyle.join(';') + } + if(tmpVal){ + tmpAttrs[a] = tmpVal; + } + + } + node.attrs = tmpAttrs; + } + if(node.children){ + for(var i = 0,ci;ci=node.children[i];){ + filterNode(ci,rules); + if(ci.parentNode){ + i++; + } + } + } + } + }else{ + //如果不在名单里扣出子节点并删除该节点,cdata除外 + if(dtd.$cdata[node.tagName]){ + node.parentNode.removeChild(node) + }else{ + var parentNode = node.parentNode, + index = node.getIndex(); + node.parentNode.removeChild(node,true); + for(var i = index,ci;ci=parentNode.children[i];){ + filterNode(ci,rules); + if(ci.parentNode){ + i++; + } + } + } + } + break; + case 'comment': + node.parentNode.removeChild(node) + } + + } + return function(root,rules){ + if(utils.isEmptyObject(rules)){ + return root; + } + var val; + if(val = rules['-']){ + utils.each(val.split(' '),function(k){ + rules[k] = '-' + }) + } + for(var i= 0,ci;ci=root.children[i];){ + filterNode(ci,rules); + if(ci.parentNode){ + i++; + } + } + return root; + } + }(); +///import core + /** + * @description 插入内容 + * @name baidu.editor.execCommand + * @param {String} cmdName inserthtml插入内容的命令 + * @param {String} html 要插入的内容 + * @author zhanyi + */ + UM.commands['inserthtml'] = { + execCommand: function (command,html,notNeedFilter){ + var me = this, + range, + div; + if(!html){ + return; + } + if(me.fireEvent('beforeinserthtml',html) === true){ + return; + } + range = me.selection.getRange(); + div = range.document.createElement( 'div' ); + div.style.display = 'inline'; + + if (!notNeedFilter) { + var root = UM.htmlparser(html); + //如果给了过滤规则就先进行过滤 + if(me.options.filterRules){ + UM.filterNode(root,me.options.filterRules); + } + //执行默认的处理 + me.filterInputRule(root); + html = root.toHtml() + } + div.innerHTML = utils.trim( html ); + + if ( !range.collapsed ) { + var tmpNode = range.startContainer; + if(domUtils.isFillChar(tmpNode)){ + range.setStartBefore(tmpNode) + } + tmpNode = range.endContainer; + if(domUtils.isFillChar(tmpNode)){ + range.setEndAfter(tmpNode) + } + range.txtToElmBoundary(); + //结束边界可能放到了br的前边,要把br包含进来 + // x[xxx]
          + if(range.endContainer && range.endContainer.nodeType == 1){ + tmpNode = range.endContainer.childNodes[range.endOffset]; + if(tmpNode && domUtils.isBr(tmpNode)){ + range.setEndAfter(tmpNode); + } + } + if(range.startOffset == 0){ + tmpNode = range.startContainer; + if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){ + tmpNode = range.endContainer; + if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){ + me.body.innerHTML = '

          '+(browser.ie ? '' : '
          ')+'

          '; + range.setStart(me.body.firstChild,0).collapse(true) + + } + } + } + !range.collapsed && range.deleteContents(); + if(range.startContainer.nodeType == 1){ + var child = range.startContainer.childNodes[range.startOffset],pre; + if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){ + range.setEnd(pre,pre.childNodes.length).collapse(); + while(child.firstChild){ + pre.appendChild(child.firstChild); + } + domUtils.remove(child); + } + } + + } + + + var child,parent,pre,tmp,hadBreak = 0, nextNode; + //如果当前位置选中了fillchar要干掉,要不会产生空行 + if(range.inFillChar()){ + child = range.startContainer; + if(domUtils.isFillChar(child)){ + range.setStartBefore(child).collapse(true); + domUtils.remove(child); + }else if(domUtils.isFillChar(child,true)){ + child.nodeValue = child.nodeValue.replace(fillCharReg,''); + range.startOffset--; + range.collapsed && range.collapse(true) + } + } + while ( child = div.firstChild ) { + if(hadBreak){ + var p = me.document.createElement('p'); + while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){ + nextNode = child.nextSibling; + p.appendChild(child); + child = nextNode; + } + if(p.firstChild){ + + child = p + } + } + range.insertNode( child ); + nextNode = child.nextSibling; + if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){ + + parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } ); + if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){ + if(!dtd[parent.tagName][child.nodeName]){ + pre = parent; + }else{ + tmp = child.parentNode; + while (tmp !== parent){ + pre = tmp; + tmp = tmp.parentNode; + + } + } + + + domUtils.breakParent( child, pre || tmp ); + //去掉break后前一个多余的节点

          |<[p> ==>

          |

          + var pre = child.previousSibling; + domUtils.trimWhiteTextNode(pre); + if(!pre.childNodes.length){ + domUtils.remove(pre); + } + //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位 + + if(!browser.ie && + (next = child.nextSibling) && + domUtils.isBlockElm(next) && + next.lastChild && + !domUtils.isBr(next.lastChild)){ + next.appendChild(me.document.createElement('br')); + } + hadBreak = 1; + } + } + var next = child.nextSibling; + if(!div.firstChild && next && domUtils.isBlockElm(next)){ + + range.setStart(next,0).collapse(true); + break; + } + range.setEndAfter( child ).collapse(); + + } + + child = range.startContainer; + + if(nextNode && domUtils.isBr(nextNode)){ + domUtils.remove(nextNode) + } + //用chrome可能有空白展位符 + if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){ + if(nextNode = child.nextSibling){ + domUtils.remove(child); + if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){ + + range.setStart(nextNode,0).collapse(true).shrinkBoundary() + } + }else{ + + try{ + child.innerHTML = browser.ie ? domUtils.fillChar : '
          '; + }catch(e){ + range.setStartBefore(child); + domUtils.remove(child) + } + + } + + } + //加上true因为在删除表情等时会删两次,第一次是删的fillData + try{ + if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){ + var start = range.startContainer,pre = start.childNodes[range.startOffset-1]; + if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){ + var txt = this.document.createTextNode(domUtils.fillChar); + range.insertNode(txt).setStart(txt,0).collapse(true); + } + } + setTimeout(function(){ + range.select(true); + }) + + }catch(e){} + + + setTimeout(function(){ + range = me.selection.getRange(); + range.scrollIntoView(); + me.fireEvent('afterinserthtml'); + },200); + } + }; + +///import core +///import plugins\inserthtml.js +///commands 插入图片,操作图片的对齐方式 +///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter +///commandsTitle 图片,默认,居左,居右,居中 +///commandsDialog dialogs\image + /** + * Created by . + * User: zhanyi + * for image + */ + UM.commands['insertimage'] = { + execCommand:function (cmd, opt) { + opt = utils.isArray(opt) ? opt : [opt]; + if (!opt.length) { + return; + } + var me = this; + var html = [], str = '', ci; + ci = opt[0]; + if (opt.length == 1) { + str = '' + ci.alt + ''; + if (ci['floatStyle'] == 'center') { + str = '

          ' + str + '

          '; + } + html.push(str); + + } else { + for (var i = 0; ci = opt[i++];) { + str = '

          '; + html.push(str); + } + } + + me.execCommand('insertHtml', html.join(''), true); + } + }; +///import core +///commands 段落格式,居左,居右,居中,两端对齐 +///commandsName JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify +///commandsTitle 居左对齐,居中对齐,居右对齐,两端对齐 + /** + * @description 居左右中 + * @name UM.execCommand + * @param {String} cmdName justify执行对齐方式的命令 + * @param {String} align 对齐方式:left居左,right居右,center居中,justify两端对齐 + * @author zhanyi + */ + UM.plugins['justify']=function(){ + var me = this; + $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){ + me.commands[cmdName] = { + execCommand:function (cmdName) { + return this.document.execCommand(cmdName); + }, + queryCommandValue: function (cmdName) { + var val = this.document.queryCommandValue(cmdName); + return val === true || val === 'true' ? cmdName.replace(/justify/,'') : ''; + }, + queryCommandState: function (cmdName) { + return this.document.queryCommandState(cmdName) ? 1 : 0 + } + }; + }) + }; + +///import core +///import plugins\removeformat.js +///commands 字体颜色,背景色,字号,字体,下划线,删除线 +///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough +///commandsTitle 字体颜色,背景色,字号,字体,下划线,删除线 + /** + * @description 字体 + * @name UM.execCommand + * @param {String} cmdName 执行的功能名称 + * @param {String} value 传入的值 + */ + UM.plugins['font'] = function () { + var me = this, + fonts = { + 'forecolor': 'forecolor', + 'backcolor': 'backcolor', + 'fontsize': 'fontsize', + 'fontfamily': 'fontname' + }, + cmdNameToStyle = { + 'forecolor': 'color', + 'backcolor': 'background-color', + 'fontsize': 'font-size', + 'fontfamily': 'font-family' + }, + cmdNameToAttr = { + 'forecolor': 'color', + 'fontsize': 'size', + 'fontfamily': 'face' + }; + me.setOpt({ + 'fontfamily': [ + { name: 'songti', val: '宋体,SimSun'}, + { name: 'yahei', val: '微软雅黑,Microsoft YaHei'}, + { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'}, + { name: 'heiti', val: '黑体, SimHei'}, + { name: 'lishu', val: '隶书, SimLi'}, + { name: 'andaleMono', val: 'andale mono'}, + { name: 'arial', val: 'arial, helvetica,sans-serif'}, + { name: 'arialBlack', val: 'arial black,avant garde'}, + { name: 'comicSansMs', val: 'comic sans ms'}, + { name: 'impact', val: 'impact,chicago'}, + { name: 'timesNewRoman', val: 'times new roman'}, + { name: 'sans-serif',val:'sans-serif'} + ], + 'fontsize': [10, 12, 16, 18,24, 32,48] + }); + + me.addOutputRule(function (root) { + utils.each(root.getNodesByTagName('font'), function (node) { + if (node.tagName == 'font') { + var cssStyle = []; + for (var p in node.attrs) { + switch (p) { + case 'size': + var val = node.attrs[p]; + $.each({ + '10':'1', + '12':'2', + '16':'3', + '18':'4', + '24':'5', + '32':'6', + '48':'7' + },function(k,v){ + if(v == val){ + val = k; + return false; + } + }); + cssStyle.push('font-size:' + val + 'px'); + break; + case 'color': + cssStyle.push('color:' + node.attrs[p]); + break; + case 'face': + cssStyle.push('font-family:' + node.attrs[p]); + break; + case 'style': + cssStyle.push(node.attrs[p]); + } + } + node.attrs = { + 'style': cssStyle.join(';') + }; + } + node.tagName = 'span'; + if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){ + $.each(node.attrs,function(k,v){ + + node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v; + }) + node.parentNode.removeChild(node,true); + } + }); + }); + for(var p in fonts){ + (function (cmd) { + me.commands[cmd] = { + execCommand: function (cmdName,value) { + if(value == 'transparent'){ + return; + } + var rng = this.selection.getRange(); + if(rng.collapsed){ + var span = $('').css(cmdNameToStyle[cmdName],value)[0]; + rng.insertNode(span).setStart(span,0).setCursor(); + }else{ + if(cmdName == 'fontsize'){ + value = { + '10':'1', + '12':'2', + '16':'3', + '18':'4', + '24':'5', + '32':'6', + '48':'7' + }[(value+"").replace(/px/,'')] + } + this.document.execCommand(fonts[cmdName],false, value); + if(browser.gecko){ + $.each(this.$body.find('a'),function(i,a){ + var parent = a.parentNode; + if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){ + var cloneNode = parent.cloneNode(false); + cloneNode.innerHTML = a.innerHTML; + $(a).html('').append(cloneNode).insertBefore(parent); + + $(parent).remove(); + } + }); + } + if(!browser.ie){ + var nativeRange = this.selection.getNative().getRangeAt(0); + var common = nativeRange.commonAncestorContainer; + var rng = this.selection.getRange(), + bk = rng.createBookmark(true); + + $(common).find('a').each(function(i,n){ + var parent = n.parentNode; + if(parent.nodeName == 'FONT'){ + var font = parent.cloneNode(false); + font.innerHTML = n.innerHTML; + $(n).html('').append(font); + } + }); + rng.moveToBookmark(bk).select() + } + return true + } + + }, + queryCommandValue: function (cmdName) { + var start = me.selection.getStart(); + var val = $(start).css(cmdNameToStyle[cmdName]); + if(val === undefined){ + val = $(start).attr(cmdNameToAttr[cmdName]) + } + return val ? utils.fixColor(cmdName,val).replace(/px/,'') : ''; + }, + queryCommandState: function (cmdName) { + return this.queryCommandValue(cmdName) + } + }; + })(p); + } + }; +///import core +///commands 超链接,取消链接 +///commandsName Link,Unlink +///commandsTitle 超链接,取消链接 +///commandsDialog dialogs\link + /** + * 超链接 + * @function + * @name UM.execCommand + * @param {String} cmdName link插入超链接 + * @param {Object} options url地址,title标题,target是否打开新页 + * @author zhanyi + */ + /** + * 取消链接 + * @function + * @name UM.execCommand + * @param {String} cmdName unlink取消链接 + * @author zhanyi + */ + + UM.plugins['link'] = function(){ + var me = this; + + me.setOpt('autourldetectinie',false); + //在ie下禁用autolink + if(browser.ie && this.options.autourldetectinie === false){ + this.addListener('keyup',function(cmd,evt){ + var me = this,keyCode = evt.keyCode; + if(keyCode == 13 || keyCode == 32){ + var rng = me.selection.getRange(); + var start = rng.startContainer; + if(keyCode == 13){ + if(start.nodeName == 'P'){ + var pre = start.previousSibling; + if(pre && pre.nodeType == 1){ + var pre = pre.lastChild; + if(pre && pre.nodeName == 'A' && !pre.getAttribute('_href')){ + domUtils.remove(pre,true); + } + } + } + }else if(keyCode == 32){ + if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){ + start = start.previousSibling; + if(start && start.nodeName == 'A' && !start.getAttribute('_href')){ + domUtils.remove(start,true); + } + } + } + + } + + + }); + } + + this.addOutputRule(function(root){ + $.each(root.getNodesByTagName('a'),function(i,a){ + var _href = utils.html(a.getAttr('_href')); + if(!/^(ftp|https?|\/|file)/.test(_href)){ + _href = 'http://' + _href; + } + a.setAttr('href', _href); + a.setAttr('_href') + if(a.getAttr('title')==''){ + a.setAttr('title') + } + }) + }); + this.addInputRule(function(root){ + $.each(root.getNodesByTagName('a'),function(i,a){ + a.setAttr('_href', utils.html(a.getAttr('href'))); + }) + }); + me.commands['link'] = { + execCommand : function( cmdName, opt ) { + + var me = this; + var rng = me.selection.getRange(); + if(rng.collapsed){ + var start = rng.startContainer; + if(start = domUtils.findParentByTagName(start,'a',true)){ + $(start).attr(opt); + rng.selectNode(start).select() + }else{ + rng.insertNode($('' +opt.href+'').attr(opt)[0]).select() + + } + + }else{ + me.document.execCommand('createlink',false,'_umeditor_link'); + utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){ + + return n.getAttribute('href') == '_umeditor_link' + }),function(l){ + if($(l).text() == '_umeditor_link'){ + $(l).text(opt.href); + } + domUtils.setAttributes(l,opt); + rng.selectNode(l).select() + }) + } + + }, + queryCommandState:function(){ + return this.queryCommandValue('link') ? 1 : 0; + }, + queryCommandValue:function(){ + var path = this.selection.getStartElementPath(); + var result; + $.each(path,function(i,n){ + if(n.nodeName == "A"){ + result = n; + return false; + } + }) + return result; + } + }; + me.commands['unlink'] = { + execCommand : function() { + this.document.execCommand('unlink'); + } + }; + }; +///import core +///commands 打印 +///commandsName Print +///commandsTitle 打印 + /** + * @description 打印 + * @name baidu.editor.execCommand + * @param {String} cmdName print打印编辑器内容 + * @author zhanyi + */ + UM.commands['print'] = { + execCommand : function(){ + var me = this, + id = 'editor_print_' + +new Date(); + + $('').attr('id', id) + .css({ + width:'0px', + height:'0px', + 'overflow':'hidden', + 'float':'left', + 'position':'absolute', + top:'-10000px', + left:'-10000px' + }) + .appendTo(me.$container.find('.edui-dialog-container')); + + var w = window.open('', id, ''), + d = w.document; + d.open(); + d.write('
          '+this.getContent(null,null,true)+'
          '); + d.close(); + }, + notNeedUndo : 1 + }; +///import core +///commands 格式 +///commandsName Paragraph +///commandsTitle 段落格式 + /** + * 段落样式 + * @function + * @name UM.execCommand + * @param {String} cmdName paragraph插入段落执行命令 + * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' + * @param {String} attrs 标签的属性 + * @author zhanyi + */ + UM.plugins['paragraph'] = function() { + var me = this; + me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''}); + me.commands['paragraph'] = { + execCommand : function( cmdName, style ) { + return this.document.execCommand('formatBlock',false,'<' + style + '>'); + }, + queryCommandValue : function() { + try{ + var val = this.document.queryCommandValue('formatBlock') + }catch(e){ + } + return val ; + } + }; + }; + +///import core +///import plugins\inserthtml.js +///commands 分割线 +///commandsName Horizontal +///commandsTitle 分隔线 + /** + * 分割线 + * @function + * @name UM.execCommand + * @param {String} cmdName horizontal插入分割线 + */ + UM.plugins['horizontal'] = function(){ + var me = this; + me.commands['horizontal'] = { + execCommand : function( ) { + this.document.execCommand('insertHorizontalRule'); + var rng = me.selection.getRange().txtToElmBoundary(true), + start = rng.startContainer; + if(domUtils.isBody(rng.startContainer)){ + var next = rng.startContainer.childNodes[rng.startOffset]; + if(!next){ + next = $('

          ').appendTo(rng.startContainer).html(browser.ie ? ' ' : '
          ')[0] + } + rng.setStart(next,0).setCursor() + }else{ + + while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){ + + var parent = start.parentNode; + parent.appendChild(start.firstChild); + parent.removeChild(start); + start = parent; + } + while(dtd.$inline[start.tagName]){ + start = start.parentNode; + } + if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){ + var hr = start.lastChild; + $(hr).insertBefore(start); + rng.setStart(start,0).setCursor(); + }else{ + hr = $('hr',start)[0]; + domUtils.breakParent(hr,start); + var pre = hr.previousSibling; + if(pre && domUtils.isEmptyBlock(pre)){ + $(pre).remove() + } + rng.setStart(hr.nextSibling,0).setCursor(); + } + + } + } + }; + + }; + +///import core +///commands 图片选择 +///commandsName image +///commandsTitle 图片选择 + + /** + * + * 图片选择 + * @function + * @name UM.execCommand + * @param {String} cmdName image 图片选择 + */ + UM.commands['image'] = { + execCommand : function() { + var me = this; + $.fileLibrary({ + type: 'image', + done: function (images) { + console.log(images); + var html = []; + images.forEach(function (item) { + html.push('

          '); + }); + me.execCommand('insertHtml', html.join(''), true); + } + }); + } + }; + + +///import core +///commands 清空文档 +///commandsName ClearDoc +///commandsTitle 清空文档 + /** + * + * 清空文档 + * @function + * @name UM.execCommand + * @param {String} cmdName cleardoc清空文档 + */ + + UM.commands['cleardoc'] = { + execCommand : function() { + var me = this, + range = me.selection.getRange(); + me.body.innerHTML = "

          "+(ie ? "" : "
          ")+"

          "; + range.setStart(me.body.firstChild,0).setCursor(false,true); + setTimeout(function(){ + me.fireEvent("clearDoc"); + },0); + + } + }; + + +///import core +///commands 撤销和重做 +///commandsName Undo,Redo +///commandsTitle 撤销,重做 + /** + * @description 回退 + * @author zhanyi + */ + + UM.plugins['undo'] = function () { + var saveSceneTimer; + var me = this, + maxUndoCount = me.options.maxUndoCount || 20, + maxInputCount = me.options.maxInputCount || 20, + fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的 + var noNeedFillCharTags = { + ol:1,ul:1,table:1,tbody:1,tr:1,body:1 + }; + var orgState = me.options.autoClearEmptyNode; + function compareAddr(indexA, indexB) { + if (indexA.length != indexB.length) + return 0; + for (var i = 0, l = indexA.length; i < l; i++) { + if (indexA[i] != indexB[i]) + return 0 + } + return 1; + } + + function compareRangeAddress(rngAddrA, rngAddrB) { + if (rngAddrA.collapsed != rngAddrB.collapsed) { + return 0; + } + if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) { + return 0; + } + return 1; + } + + function UndoManager() { + this.list = []; + this.index = 0; + this.hasUndo = false; + this.hasRedo = false; + this.undo = function () { + if (this.hasUndo) { + if (!this.list[this.index - 1] && this.list.length == 1) { + this.reset(); + return; + } + while (this.list[this.index].content == this.list[this.index - 1].content) { + this.index--; + if (this.index == 0) { + return this.restore(0); + } + } + this.restore(--this.index); + } + }; + this.redo = function () { + if (this.hasRedo) { + while (this.list[this.index].content == this.list[this.index + 1].content) { + this.index++; + if (this.index == this.list.length - 1) { + return this.restore(this.index); + } + } + this.restore(++this.index); + } + }; + + this.restore = function () { + var me = this.editor; + var scene = this.list[this.index]; + var root = UM.htmlparser(scene.content.replace(fillchar, '')); + me.options.autoClearEmptyNode = false; + me.filterInputRule(root,true); + me.options.autoClearEmptyNode = orgState; + //trace:873 + //去掉展位符 + me.body.innerHTML = root.toHtml(); + me.fireEvent('afterscencerestore'); + //处理undo后空格不展位的问题 + if (browser.ie) { + utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){ + if(domUtils.isEmptyNode(node)){ + domUtils.fillNode(me.document, node); + } + }) + } + + try{ + var rng = new dom.Range(me.document,me.body).moveToAddress(scene.address); + if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){ + var tmpNode = rng.startContainer.childNodes[rng.startOffset]; + if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){ + rng.insertNode(me.document.createTextNode(' ')).collapse(true); + } + } + rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]); + }catch(e){} + + this.update(); + this.clearKey(); + //不能把自己reset了 + me.fireEvent('reset', true); + }; + + this.getScene = function () { + var me = this.editor; + var rng = me.selection.getRange(), + rngAddress = rng.createAddress(false,true); + me.fireEvent('beforegetscene'); + var root = UM.htmlparser(me.body.innerHTML,true); + me.options.autoClearEmptyNode = false; + me.filterOutputRule(root,true); + me.options.autoClearEmptyNode = orgState; + var cont = root.toHtml(); + browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>')); + me.fireEvent('aftergetscene'); + return { + address:rngAddress, + content:cont + } + }; + this.save = function (notCompareRange,notSetCursor) { + clearTimeout(saveSceneTimer); + var currentScene = this.getScene(notSetCursor), + lastScene = this.list[this.index]; + //内容相同位置相同不存 + if (lastScene && lastScene.content == currentScene.content && + ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) ) + ) { + return; + } + this.list = this.list.slice(0, this.index + 1); + this.list.push(currentScene); + //如果大于最大数量了,就把最前的剔除 + if (this.list.length > maxUndoCount) { + this.list.shift(); + } + this.index = this.list.length - 1; + this.clearKey(); + //跟新undo/redo状态 + this.update(); + + }; + this.update = function () { + this.hasRedo = !!this.list[this.index + 1]; + this.hasUndo = !!this.list[this.index - 1]; + }; + this.reset = function () { + this.list = []; + this.index = 0; + this.hasUndo = false; + this.hasRedo = false; + this.clearKey(); + }; + this.clearKey = function () { + keycont = 0; + lastKeyCode = null; + }; + } + + me.undoManger = new UndoManager(); + me.undoManger.editor = me; + function saveScene() { + this.undoManger.save(); + } + + me.addListener('saveScene', function () { + var args = Array.prototype.splice.call(arguments,1); + this.undoManger.save.apply(this.undoManger,args); + }); + + me.addListener('beforeexeccommand', saveScene); + me.addListener('afterexeccommand', saveScene); + + me.addListener('reset', function (type, exclude) { + if (!exclude) { + this.undoManger.reset(); + } + }); + me.commands['redo'] = me.commands['undo'] = { + execCommand:function (cmdName) { + this.undoManger[cmdName](); + }, + queryCommandState:function (cmdName) { + return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1; + }, + notNeedUndo:1 + }; + + var keys = { + // /*Backspace*/ 8:1, /*Delete*/ 46:1, + /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1, + 37:1, 38:1, 39:1, 40:1 + + }, + keycont = 0, + lastKeyCode; + //输入法状态下不计算字符数 + var inputType = false; + me.addListener('ready', function () { + $(this.body).on('compositionstart', function () { + inputType = true; + }).on('compositionend', function () { + inputType = false; + }) + }); + //快捷键 + me.addshortcutkey({ + "Undo":"ctrl+90", //undo + "Redo":"ctrl+89,shift+ctrl+z" //redo + + }); + var isCollapsed = true; + me.addListener('keydown', function (type, evt) { + + var me = this; + var keyCode = evt.keyCode || evt.which; + if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { + if (inputType) + return; + + if(!me.selection.getRange().collapsed){ + me.undoManger.save(false,true); + isCollapsed = false; + return; + } + if (me.undoManger.list.length == 0) { + me.undoManger.save(true); + } + clearTimeout(saveSceneTimer); + function save(cont){ + + if (cont.selection.getRange().collapsed) + cont.fireEvent('contentchange'); + cont.undoManger.save(false,true); + cont.fireEvent('selectionchange'); + } + saveSceneTimer = setTimeout(function(){ + if(inputType){ + var interalTimer = setInterval(function(){ + if(!inputType){ + save(me); + clearInterval(interalTimer) + } + },300) + return; + } + save(me); + },200); + + lastKeyCode = keyCode; + keycont++; + if (keycont >= maxInputCount ) { + save(me) + } + } + }); + me.addListener('keyup', function (type, evt) { + var keyCode = evt.keyCode || evt.which; + if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) { + if (inputType) + return; + if(!isCollapsed){ + this.undoManger.save(false,true); + isCollapsed = true; + } + } + }); + + }; + +///import core +///import plugins/inserthtml.js +///import plugins/undo.js +///import plugins/serialize.js +///commands 粘贴 +///commandsName PastePlain +///commandsTitle 纯文本粘贴模式 + /** + * @description 粘贴 + * @author zhanyi + */ + UM.plugins['paste'] = function () { + function getClipboardData(callback) { + var doc = this.document; + if (doc.getElementById('baidu_pastebin')) { + return; + } + var range = this.selection.getRange(), + bk = range.createBookmark(), + //创建剪贴的容器div + pastebin = doc.createElement('div'); + pastebin.id = 'baidu_pastebin'; + // Safari 要求div必须有内容,才能粘贴内容进来 + browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar)); + this.body.appendChild(pastebin); + //trace:717 隐藏的span不能得到top + //bk.start.innerHTML = ' '; + bk.start.style.display = ''; + + pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" + + //要在现在光标平行的位置加入,否则会出现跳动的问题 + $(bk.start).position().top + 'px'; + + range.selectNodeContents(pastebin).select(true); + + setTimeout(function () { + if (browser.webkit) { + for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) { + if (domUtils.isEmptyNode(pi)) { + domUtils.remove(pi); + } else { + pastebin = pi; + break; + } + } + } + try { + pastebin.parentNode.removeChild(pastebin); + } catch (e) { + } + range.moveToBookmark(bk).select(true); + callback(pastebin); + }, 0); + } + + var me = this; + + + function filter(div) { + var html; + if (div.firstChild) { + //去掉cut中添加的边界值 + var nodes = domUtils.getElementsByTagName(div, 'span'); + for (var i = 0, ni; ni = nodes[i++];) { + if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') { + domUtils.remove(ni); + } + } + + if (browser.webkit) { + + var brs = div.querySelectorAll('div br'); + for (var i = 0, bi; bi = brs[i++];) { + var pN = bi.parentNode; + if (pN.tagName == 'DIV' && pN.childNodes.length == 1) { + pN.innerHTML = '


          '; + domUtils.remove(pN); + } + } + var divs = div.querySelectorAll('#baidu_pastebin'); + for (var i = 0, di; di = divs[i++];) { + var tmpP = me.document.createElement('p'); + di.parentNode.insertBefore(tmpP, di); + while (di.firstChild) { + tmpP.appendChild(di.firstChild); + } + domUtils.remove(di); + } + + var metas = div.querySelectorAll('meta'); + for (var i = 0, ci; ci = metas[i++];) { + domUtils.remove(ci); + } + + var brs = div.querySelectorAll('br'); + for (i = 0; ci = brs[i++];) { + if (/^apple-/i.test(ci.className)) { + domUtils.remove(ci); + } + } + } + if (browser.gecko) { + var dirtyNodes = div.querySelectorAll('[_moz_dirty]'); + for (i = 0; ci = dirtyNodes[i++];) { + ci.removeAttribute('_moz_dirty'); + } + } + if (!browser.ie) { + var spans = div.querySelectorAll('span.Apple-style-span'); + for (var i = 0, ci; ci = spans[i++];) { + domUtils.remove(ci, true); + } + } + + //ie下使用innerHTML会产生多余的\r\n字符,也会产生 这里过滤掉 + html = div.innerHTML;//.replace(/>(?:(\s| )*?)<'); + + //过滤word粘贴过来的冗余属性 + html = UM.filterWord(html); + //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签 + var root = UM.htmlparser(html); + //如果给了过滤规则就先进行过滤 + if (me.options.filterRules) { + UM.filterNode(root, me.options.filterRules); + } + //执行默认的处理 + me.filterInputRule(root); + //针对chrome的处理 + if (browser.webkit) { + var br = root.lastChild(); + if (br && br.type == 'element' && br.tagName == 'br') { + root.removeChild(br) + } + utils.each(me.body.querySelectorAll('div'), function (node) { + if (domUtils.isEmptyBlock(node)) { + domUtils.remove(node) + } + }) + } + html = {'html': root.toHtml()}; + me.fireEvent('beforepaste', html, root); + //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴 + if(!html.html){ + return; + } + + me.execCommand('insertHtml', html.html, true); + me.fireEvent("afterpaste", html); + } + } + + + me.addListener('ready', function () { + $(me.body).on( 'cut', function () { + var range = me.selection.getRange(); + if (!range.collapsed && me.undoManger) { + me.undoManger.save(); + } + }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) { + //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理 + if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) { + return; + } + getClipboardData.call(me, function (div) { + filter(div); + }); + }); + + }); + }; + + +///import core +///commands 有序列表,无序列表 +///commandsName InsertOrderedList,InsertUnorderedList +///commandsTitle 有序列表,无序列表 + /** + * 有序列表 + * @function + * @name UM.execCommand + * @param {String} cmdName insertorderlist插入有序列表 + * @param {String} style 值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman + * @author zhanyi + */ + /** + * 无序链接 + * @function + * @name UM.execCommand + * @param {String} cmdName insertunorderlist插入无序列表 + * * @param {String} style 值为:circle,disc,square + * @author zhanyi + */ + + UM.plugins['list'] = function () { + var me = this; + + me.setOpt( { + 'insertorderedlist':{ + 'decimal':'', + 'lower-alpha':'', + 'lower-roman':'', + 'upper-alpha':'', + 'upper-roman':'' + }, + 'insertunorderedlist':{ + 'circle':'', + 'disc':'', + 'square':'' + } + } ); + + this.addInputRule(function(root){ + utils.each(root.getNodesByTagName('li'), function (node) { + if(node.children.length == 0){ + node.parentNode.removeChild(node); + } + }) + }); + me.commands['insertorderedlist'] = + me.commands['insertunorderedlist'] = { + execCommand:function (cmdName) { + this.document.execCommand(cmdName); + var rng = this.selection.getRange(), + bk = rng.createBookmark(true); + + this.$body.find('ol,ul').each(function(i,n){ + var parent = n.parentNode; + if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){ + $(n).children().each(function(j,li){ + var p = parent.cloneNode(false); + $(p).append(li.innerHTML); + $(li).html('').append(p); + }); + $(n).insertBefore(parent); + $(parent).remove(); + } + + if(dtd.$inline[parent.tagName]){ + if(parent.tagName == 'SPAN'){ + + $(n).children().each(function(k,li){ + var span = parent.cloneNode(false); + if(li.firstChild.nodeName != 'P'){ + + while(li.firstChild){ + span.appendChild(li.firstChild) + }; + $('

          ').appendTo(li).append(span); + }else{ + while(li.firstChild){ + span.appendChild(li.firstChild) + }; + $(li.firstChild).append(span); + } + }) + + } + domUtils.remove(parent,true) + } + }); + + + + + rng.moveToBookmark(bk).select(); + return true; + }, + queryCommandState:function (cmdName) { + return this.document.queryCommandState(cmdName); + } + }; + }; + + +///import core +///import plugins/serialize.js +///import plugins/undo.js +///commands 查看源码 +///commandsName Source +///commandsTitle 查看源码 + (function (){ + var sourceEditors = { + textarea: function (editor, holder){ + var textarea = holder.ownerDocument.createElement('textarea'); + textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0'; + // todo: IE下只有onresize属性可用... 很纠结 + if (browser.ie && browser.version < 8) { + + textarea.style.width = holder.offsetWidth + 'px'; + textarea.style.height = holder.offsetHeight + 'px'; + holder.onresize = function (){ + textarea.style.width = holder.offsetWidth + 'px'; + textarea.style.height = holder.offsetHeight + 'px'; + }; + } + holder.appendChild(textarea); + return { + container : textarea, + setContent: function (content){ + textarea.value = content; + }, + getContent: function (){ + return textarea.value; + }, + select: function (){ + var range; + if (browser.ie) { + range = textarea.createTextRange(); + range.collapse(true); + range.select(); + } else { + //todo: chrome下无法设置焦点 + textarea.setSelectionRange(0, 0); + textarea.focus(); + } + }, + dispose: function (){ + holder.removeChild(textarea); + // todo + holder.onresize = null; + textarea = null; + holder = null; + } + }; + } + }; + + UM.plugins['source'] = function (){ + var me = this; + var opt = this.options; + var sourceMode = false; + var sourceEditor; + + opt.sourceEditor = 'textarea'; + + me.setOpt({ + sourceEditorFirst:false + }); + function createSourceEditor(holder){ + return sourceEditors.textarea(me, holder); + } + + var bakCssText; + //解决在源码模式下getContent不能得到最新的内容问题 + var oldGetContent = me.getContent, + bakAddress; + + me.commands['source'] = { + execCommand: function (){ + + sourceMode = !sourceMode; + if (sourceMode) { + bakAddress = me.selection.getRange().createAddress(false,true); + me.undoManger && me.undoManger.save(true); + if(browser.gecko){ + me.body.contentEditable = false; + } + +// bakCssText = me.body.style.cssText; + me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;'; + + + me.fireEvent('beforegetcontent'); + var root = UM.htmlparser(me.body.innerHTML); + me.filterOutputRule(root); + root.traversal(function (node) { + if (node.type == 'element') { + switch (node.tagName) { + case 'td': + case 'th': + case 'caption': + if(node.children && node.children.length == 1){ + if(node.firstChild().tagName == 'br' ){ + node.removeChild(node.firstChild()) + } + }; + break; + case 'pre': + node.innerText(node.innerText().replace(/ /g,' ')) + + } + } + }); + + me.fireEvent('aftergetcontent'); + + var content = root.toHtml(true); + + sourceEditor = createSourceEditor(me.body.parentNode); + + sourceEditor.setContent(content); + + var getStyleValue=function(attr){ + return parseInt($(me.body).css(attr)); + }; + $(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right")) + .height($(me.body).height()); + setTimeout(function (){ + sourceEditor.select(); + }); + //重置getContent,源码模式下取值也能是最新的数据 + me.getContent = function (){ + return sourceEditor.getContent() || '

          ' + (browser.ie ? '' : '
          ')+'

          '; + }; + } else { + me.$body.css({ + 'position':'', + 'left':'', + 'top':'' + }); +// me.body.style.cssText = bakCssText; + var cont = sourceEditor.getContent() || '

          ' + (browser.ie ? '' : '
          ')+'

          '; + //处理掉block节点前后的空格,有可能会误命中,暂时不考虑 + cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){ + if(b && !dtd.$inlineWithA[b.toLowerCase()]){ + return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,''); + } + return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'') + }); + me.setContent(cont); + sourceEditor.dispose(); + sourceEditor = null; + //还原getContent方法 + me.getContent = oldGetContent; + var first = me.body.firstChild; + //trace:1106 都删除空了,下边会报错,所以补充一个p占位 + if(!first){ + me.body.innerHTML = '

          '+(browser.ie?'':'
          ')+'

          '; + } + //要在ifm为显示时ff才能取到selection,否则报错 + //这里不能比较位置了 + me.undoManger && me.undoManger.save(true); + if(browser.gecko){ + me.body.contentEditable = true; + } + try{ + me.selection.getRange().moveToAddress(bakAddress).select(); + }catch(e){} + + } + this.fireEvent('sourcemodechanged', sourceMode); + }, + queryCommandState: function (){ + return sourceMode|0; + }, + notNeedUndo : 1 + }; + var oldQueryCommandState = me.queryCommandState; + + + me.queryCommandState = function (cmdName){ + cmdName = cmdName.toLowerCase(); + if (sourceMode) { + //源码模式下可以开启的命令 + return cmdName in { + 'source' : 1, + 'fullscreen' : 1 + } ? oldQueryCommandState.apply(this, arguments) : -1 + } + return oldQueryCommandState.apply(this, arguments); + }; + + }; + + })(); +///import core +///import plugins/undo.js +///commands 设置回车标签p或br +///commandsName EnterKey +///commandsTitle 设置回车标签p或br + /** + * @description 处理回车 + * @author zhanyi + */ + UM.plugins['enterkey'] = function() { + var hTag, + me = this, + tag = me.options.enterTag; + me.addListener('keyup', function(type, evt) { + + var keyCode = evt.keyCode || evt.which; + if (keyCode == 13) { + var range = me.selection.getRange(), + start = range.startContainer, + doSave; + + //修正在h1-h6里边回车后不能嵌套p的问题 + if (!browser.ie) { + + if (/h\d/i.test(hTag)) { + if (browser.gecko) { + var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true); + if (!h) { + me.document.execCommand('formatBlock', false, '

          '); + doSave = 1; + } + } else { + //chrome remove div + if (start.nodeType == 1) { + var tmp = me.document.createTextNode(''),div; + range.insertNode(tmp); + div = domUtils.findParentByTagName(tmp, 'div', true); + if (div) { + var p = me.document.createElement('p'); + while (div.firstChild) { + p.appendChild(div.firstChild); + } + div.parentNode.insertBefore(p, div); + domUtils.remove(div); + range.setStartBefore(tmp).setCursor(); + doSave = 1; + } + domUtils.remove(tmp); + + } + } + + if (me.undoManger && doSave) { + me.undoManger.save(); + } + } + //没有站位符,会出现多行的问题 + browser.opera && range.select(); + }else{ + me.fireEvent('saveScene',true,true) + } + } + }); + + me.addListener('keydown', function(type, evt) { + var keyCode = evt.keyCode || evt.which; + if (keyCode == 13) {//回车 + if(me.fireEvent('beforeenterkeydown')){ + domUtils.preventDefault(evt); + return; + } + me.fireEvent('saveScene',true,true); + hTag = ''; + + + var range = me.selection.getRange(); + + if (!range.collapsed) { + //跨td不能删 + var start = range.startContainer, + end = range.endContainer, + startTd = domUtils.findParentByTagName(start, 'td', true), + endTd = domUtils.findParentByTagName(end, 'td', true); + if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) { + evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false); + return; + } + } + if (tag == 'p') { + + + if (!browser.ie) { + + start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true); + + //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command + //trace:2431 + if (!start && !browser.opera) { + + me.document.execCommand('formatBlock', false, '

          '); + + if (browser.gecko) { + range = me.selection.getRange(); + start = domUtils.findParentByTagName(range.startContainer, 'p', true); + start && domUtils.removeDirtyAttr(start); + } + + + } else { + hTag = start.tagName; + start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start); + } + + } + + } + + } + }); + + browser.ie && me.addListener('setDisabled',function(){ + $(me.body).find('p').each(function(i,p){ + if(domUtils.isEmptyBlock(p)){ + p.innerHTML = ' ' + } + }) + }) + }; + +///import core +///commands 预览 +///commandsName Preview +///commandsTitle 预览 + /** + * 预览 + * @function + * @name UM.execCommand + * @param {String} cmdName preview预览编辑器内容 + */ + UM.commands['preview'] = { + execCommand : function(){ + var w = window.open('', '_blank', ''), + d = w.document, + c = this.getContent(null,null,true), + path = this.getOpt('UMEDITOR_HOME_URL'), + formula = c.indexOf('mathquill-embedded-latex')!=-1 ? + '' + + '' + + '':''; + d.open(); + d.write('' + formula + '

          '+c+'
          '); + d.close(); + }, + notNeedUndo : 1 + }; + +///import core +///commands 加粗,斜体,上标,下标 +///commandsName Bold,Italic,Subscript,Superscript +///commandsTitle 加粗,加斜,下标,上标 + /** + * b u i等基础功能实现 + * @function + * @name UM.execCommands + * @param {String} cmdName bold加粗。italic斜体。subscript上标。superscript下标。 + */ + UM.plugins['basestyle'] = function(){ + var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'], + me = this; + //添加快捷键 + me.addshortcutkey({ + "Bold" : "ctrl+66",//^B + "Italic" : "ctrl+73", //^I + "Underline" : "ctrl+shift+85",//^U + "strikeThrough" : 'ctrl+shift+83' //^s + }); + //过滤最后的产出数据 + me.addOutputRule(function(root){ + $.each(root.getNodesByTagName('b i u strike s'),function(i,node){ + switch (node.tagName){ + case 'b': + node.tagName = 'strong'; + break; + case 'i': + node.tagName = 'em'; + break; + case 'u': + node.tagName = 'span'; + node.setStyle('text-decoration','underline'); + break; + case 's': + case 'strike': + node.tagName = 'span'; + node.setStyle('text-decoration','line-through') + } + }); + }); + $.each(basestyles,function(i,cmd){ + me.commands[cmd] = { + execCommand : function( cmdName ) { + var rng = this.selection.getRange(); + if(rng.collapsed && this.queryCommandState(cmdName) != 1){ + var node = this.document.createElement({ + 'bold':'strong', + 'underline':'u', + 'superscript':'sup', + 'subscript':'sub', + 'italic':'em', + 'strikethrough':'strike' + }[cmdName]); + rng.insertNode(node).setStart(node,0).setCursor(false); + return true; + }else{ + return this.document.execCommand(cmdName) + } + + }, + queryCommandState : function(cmdName) { + if(browser.gecko){ + return this.document.queryCommandState(cmdName) + } + var path = this.selection.getStartElementPath(),result = false; + $.each(path,function(i,n){ + switch (cmdName){ + case 'bold': + if(n.nodeName == 'STRONG' || n.nodeName == 'B'){ + result = 1; + return false; + } + break; + case 'underline': + if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){ + result = 1; + return false; + } + break; + case 'superscript': + if(n.nodeName == 'SUP'){ + result = 1; + return false; + } + break; + case 'subscript': + if(n.nodeName == 'SUB'){ + result = 1; + return false; + } + break; + case 'italic': + if(n.nodeName == 'EM' || n.nodeName == 'I'){ + result = 1; + return false; + } + break; + case 'strikethrough': + if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){ + result = 1; + return false; + } + break; + } + }); + return result + } + }; + }) + }; + + +///import core +///import plugins/inserthtml.js +///commands 视频 +///commandsName InsertVideo +///commandsTitle 插入视频 +///commandsDialog dialogs\video + UM.plugins['video'] = function (){ + var me =this, + div; + + /** + * 创建插入视频字符窜 + * @param url 视频地址 + * @param poster 封面图url + * @param width 视频宽度 + * @param height 视频高度 + * @param id 组件id + * @param align 视频对齐 + * @param toEmbed 是否以flash代替显示 + */ + function creatInsertStr(url, poster, width, height, id, align, toEmbed){ + var attr = { + src: url, + poster: poster, + style: { + height: height ? ('height: ' + height + 'px;') : '', + width: width ? ('width: ' + width + 'px;') : '' + } + }; + return ''; + } + + function switchImgAndEmbed(root,img2embed){ + utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){ + if(node.getAttr('class') == 'edui-faked-video'){ + + var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed); + node.parentNode.replaceChild(UM.uNode.createElement(html),node) + } + }) + } + + me.addOutputRule(function(root){ + switchImgAndEmbed(root,true) + }); + me.addInputRule(function(root){ + switchImgAndEmbed(root) + }); + + me.commands["insertvideo"] = { + execCommand: function (cmd, videoObjs){ + videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs]; + var html = [],id = 'tmpVedio'; + for(var i=0,vi,len = videoObjs.length;i + var node = range.startContainer, + tmp, + collapsed = range.collapsed; + while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){ + tmp = node.parentNode; + range.setStartBefore(node); + //trace:937 + //更新结束边界 + if(range.startContainer === range.endContainer){ + range.endOffset--; + } + domUtils.remove(node); + node = tmp; + } + + if(!collapsed){ + node = range.endContainer; + while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){ + tmp = node.parentNode; + range.setEndBefore(node); + domUtils.remove(node); + + node = tmp; + } + + + } + } + + + + range = this.selection.getRange(); + if(!range.collapsed) { + doRemove( range ); + range.select(); + } + + } + + }; + + }; + /* + * 处理特殊键的兼容性问题 + */ + UM.plugins['keystrokes'] = function() { + var me = this; + var collapsed = true; + me.addListener('keydown', function(type, evt) { + var keyCode = evt.keyCode || evt.which, + rng = me.selection.getRange(); + + //处理全选的情况 + if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90 + || keyCode >= 48 && keyCode <= 57 || + keyCode >= 96 && keyCode <= 111 || { + 13:1, + 8:1, + 46:1 + }[keyCode]) + ){ + + var tmpNode = rng.startContainer; + if(domUtils.isFillChar(tmpNode)){ + rng.setStartBefore(tmpNode) + } + tmpNode = rng.endContainer; + if(domUtils.isFillChar(tmpNode)){ + rng.setEndAfter(tmpNode) + } + rng.txtToElmBoundary(); + //结束边界可能放到了br的前边,要把br包含进来 + // x[xxx]
          + if(rng.endContainer && rng.endContainer.nodeType == 1){ + tmpNode = rng.endContainer.childNodes[rng.endOffset]; + if(tmpNode && domUtils.isBr(tmpNode)){ + rng.setEndAfter(tmpNode); + } + } + if(rng.startOffset == 0){ + tmpNode = rng.startContainer; + if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){ + tmpNode = rng.endContainer; + if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){ + me.fireEvent('saveScene'); + me.body.innerHTML = '

          '+(browser.ie ? '' : '
          ')+'

          '; + rng.setStart(me.body.firstChild,0).setCursor(false,true); + me._selectionChange(); + return; + } + } + } + } + + //处理backspace + if (keyCode == 8) { + rng = me.selection.getRange(); + collapsed = rng.collapsed; + if(me.fireEvent('delkeydown',evt)){ + return; + } + var start,end; + //避免按两次删除才能生效的问题 + if(rng.collapsed && rng.inFillChar()){ + start = rng.startContainer; + + if(domUtils.isFillChar(start)){ + rng.setStartBefore(start).shrinkBoundary(true).collapse(true); + domUtils.remove(start) + }else{ + start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),''); + rng.startOffset--; + rng.collapse(true).select(true) + } + } + //解决选中control元素不能删除的问题 + if (start = rng.getClosedNode()) { + me.fireEvent('saveScene'); + rng.setStartBefore(start); + domUtils.remove(start); + rng.setCursor(); + me.fireEvent('saveScene'); + domUtils.preventDefault(evt); + return; + } + //阻止在table上的删除 + if (!browser.ie) { + start = domUtils.findParentByTagName(rng.startContainer, 'table', true); + end = domUtils.findParentByTagName(rng.endContainer, 'table', true); + if (start && !end || !start && end || start !== end) { + evt.preventDefault(); + return; + } + } + start = rng.startContainer; + if(rng.collapsed && start.nodeType == 1){ + var currentNode = start.childNodes[rng.startOffset-1]; + if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){ + me.fireEvent('saveScene'); + rng.setStartBefore(currentNode).collapse(true); + domUtils.remove(currentNode); + rng.select(); + me.fireEvent('saveScene'); + } + } + + //trace:3613 + if(browser.chrome){ + if(rng.collapsed){ + + while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){ + rng.setStartBefore(rng.startContainer) + } + var pre = rng.startContainer.childNodes[rng.startOffset-1]; + if(pre && pre.nodeName == 'BR'){ + rng.setStartBefore(pre); + me.fireEvent('saveScene'); + $(pre).remove(); + rng.setCursor(); + me.fireEvent('saveScene'); + } + + } + } + } + //trace:1634 + //ff的del键在容器空的时候,也会删除 + if(browser.gecko && keyCode == 46){ + var range = me.selection.getRange(); + if(range.collapsed){ + start = range.startContainer; + if(domUtils.isEmptyBlock(start)){ + var parent = start.parentNode; + while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){ + start = parent; + parent = parent.parentNode; + } + if(start === parent.lastChild) + evt.preventDefault(); + return; + } + } + } + }); + me.addListener('keyup', function(type, evt) { + var keyCode = evt.keyCode || evt.which, + rng,me = this; + if(keyCode == 8){ + if(me.fireEvent('delkeyup')){ + return; + } + rng = me.selection.getRange(); + if(rng.collapsed){ + var tmpNode, + autoClearTagName = ['h1','h2','h3','h4','h5','h6']; + if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){ + if(domUtils.isEmptyBlock(tmpNode)){ + var pre = tmpNode.previousSibling; + if(pre && pre.nodeName != 'TABLE'){ + domUtils.remove(tmpNode); + rng.setStartAtLast(pre).setCursor(false,true); + return; + }else{ + var next = tmpNode.nextSibling; + if(next && next.nodeName != 'TABLE'){ + domUtils.remove(tmpNode); + rng.setStartAtFirst(next).setCursor(false,true); + return; + } + } + } + } + //处理当删除到body时,要重新给p标签展位 + if(domUtils.isBody(rng.startContainer)){ + var tmpNode = domUtils.createElement(me.document,'p',{ + 'innerHTML' : browser.ie ? domUtils.fillChar : '
          ' + }); + rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true); + } + } + + + //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了 + if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){ + if(browser.ie){ + var span = rng.document.createElement('span'); + rng.insertNode(span).setStartBefore(span).collapse(true); + rng.select(); + domUtils.remove(span) + }else{ + rng.select() + } + + } + } + + }) + }; + /** + * 自动保存草稿 + */ + UM.plugins['autosave'] = function() { + + + var me = this, + //无限循环保护 + lastSaveTime = new Date(), + //最小保存间隔时间 + MIN_TIME = 20, + //auto save key + saveKey = null; + + + //默认间隔时间 + me.setOpt('saveInterval', 500); + + //存储媒介封装 + var LocalStorage = UM.LocalStorage = ( function () { + + var storage = window.localStorage || getUserData() || null, + LOCAL_FILE = "localStorage"; + + return { + + saveLocalData: function ( key, data ) { + + if ( storage && data) { + storage.setItem( key, data ); + return true; + } + + return false; + + }, + + getLocalData: function ( key ) { + + if ( storage ) { + return storage.getItem( key ); + } + + return null; + + }, + + removeItem: function ( key ) { + + storage && storage.removeItem( key ); + + } + + }; + + function getUserData () { + + var container = document.createElement( "div" ); + container.style.display = "none"; + + if( !container.addBehavior ) { + return null; + } + + container.addBehavior("#default#userdata"); + + return { + + getItem: function ( key ) { + + var result = null; + + try { + document.body.appendChild( container ); + container.load( LOCAL_FILE ); + result = container.getAttribute( key ); + document.body.removeChild( container ); + } catch ( e ) { + } + + return result; + + }, + + setItem: function ( key, value ) { + + document.body.appendChild( container ); + container.setAttribute( key, value ); + container.save( LOCAL_FILE ); + document.body.removeChild( container ); + + }, +// 暂时没有用到 +// clear: function () { +// +// var expiresTime = new Date(); +// expiresTime.setFullYear( expiresTime.getFullYear() - 1 ); +// document.body.appendChild( container ); +// container.expires = expiresTime.toUTCString(); +// container.save( LOCAL_FILE ); +// document.body.removeChild( container ); +// +// }, + + removeItem: function ( key ) { + + document.body.appendChild( container ); + container.removeAttribute( key ); + container.save( LOCAL_FILE ); + document.body.removeChild( container ); + + } + + }; + + } + + } )(); + + function save ( editor ) { + + var saveData = null; + + if ( new Date() - lastSaveTime < MIN_TIME ) { + return; + } + + if ( !editor.hasContents() ) { + //这里不能调用命令来删除, 会造成事件死循环 + saveKey && LocalStorage.removeItem( saveKey ); + return; + } + + lastSaveTime = new Date(); + + editor._saveFlag = null; + saveData = me.body.innerHTML; + + if ( editor.fireEvent( "beforeautosave", { + content: saveData + } ) === false ) { + return; + } + + LocalStorage.saveLocalData( saveKey, saveData ); + + editor.fireEvent( "afterautosave", { + content: saveData + } ); + + } + + me.addListener('ready', function(){ + var _suffix = "-drafts-data", + key = null; + + if ( me.key ) { + key = me.key + _suffix; + } else { + key = ( me.container.parentNode.id || 'ue-common' ) + _suffix; + } + + //页面地址+编辑器ID 保持唯一 + saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key; + }); + + me.addListener('contentchange', function(){ + + return; + + if ( !saveKey ) { + return; + } + + if ( me._saveFlag ) { + window.clearTimeout( me._saveFlag ); + } + + if ( me.options.saveInterval > 0 ) { + + me._saveFlag = window.setTimeout( function () { + + save( me ); + + }, me.options.saveInterval ); + + } else { + + save(me); + + } + + }) + + + me.commands['clearlocaldata'] = { + execCommand:function (cmd, name) { + if ( saveKey && LocalStorage.getLocalData( saveKey ) ) { + LocalStorage.removeItem( saveKey ) + } + }, + notNeedUndo: true, + ignoreContentChange:true + }; + + me.commands['getlocaldata'] = { + execCommand:function (cmd, name) { + return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : ''; + }, + notNeedUndo: true, + ignoreContentChange:true + }; + + me.commands['drafts'] = { + execCommand:function (cmd, name) { + if ( saveKey ) { + me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '

          '+(browser.ie ? ' ' : '
          ')+'

          '; + me.focus(true); + } + }, + queryCommandState: function () { + return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1; + }, + notNeedUndo: true, + ignoreContentChange:true + } + + }; + + /** + * @description + * 1.拖放文件到编辑区域,自动上传并插入到选区 + * 2.插入粘贴板的图片,自动上传并插入到选区 + * @author Jinqn + * @date 2013-10-14 + */ + UM.plugins['autoupload'] = function () { + + var me = this; + + me.setOpt('pasteImageEnabled', true); + me.setOpt('dropFileEnabled', true); + var sendAndInsertImage = function (file, editor) { + //模拟数据 + var fd = new FormData(); + fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length))); + fd.append('type', 'ajax'); + var xhr = new XMLHttpRequest(); + xhr.open("post", me.options.imageUrl, true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.addEventListener('load', function (e) { + try { + var json = eval('('+e.target.response+')'), + link = json.url, + picLink = me.options.imagePath + link; + editor.execCommand('insertimage', { + src: picLink, + _src: picLink + }); + } catch (er) { + } + }); + xhr.send(fd); + }; + + function getPasteImage(e) { + return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null; + } + + function getDropImage(e) { + return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null; + } + + me.addListener('ready', function () { + if (window.FormData && window.FileReader) { + var autoUploadHandler = function (e) { + var hasImg = false, + items; + //获取粘贴板文件列表或者拖放文件列表 + items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent); + if (items) { + var len = items.length, + file; + while (len--) { + file = items[len]; + if (file.getAsFile) file = file.getAsFile(); + if (file && file.size > 0 && /image\/\w+/i.test(file.type)) { + sendAndInsertImage(file, me); + hasImg = true; + } + } + if (hasImg) return false; + } + + }; + me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler); + me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler); + + //取消拖放图片时出现的文字光标位置提示 + me.$body.on('dragover', function (e) { + if (e.originalEvent.dataTransfer.types[0] == 'Files') { + return false; + } + }); + } + }); + + }; + /** + * 公式插件 + */ + UM.plugins['formula'] = function () { + var me = this; + + function getActiveIframe() { + return me.$body.find('iframe.edui-formula-active')[0] || null; + } + + function blurActiveIframe(){ + var iframe = getActiveIframe(); + iframe && iframe.contentWindow.formula.blur(); + } + + me.addInputRule(function (root) { + $.each(root.getNodesByTagName('span'), function (i, node) { + if (node.hasClass('mathquill-embedded-latex')) { + var firstChild, latex = ''; + while(firstChild = node.firstChild()){ + latex += firstChild.data; + node.removeChild(firstChild); + } + node.tagName = 'iframe'; + node.setAttr({ + 'frameborder': '0', + 'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html', + 'data-latex': utils.unhtml(latex) + }); + } + }); + }); + me.addOutputRule(function (root) { + $.each(root.getNodesByTagName('iframe'), function (i, node) { + if (node.hasClass('mathquill-embedded-latex')) { + node.tagName = 'span'; + node.appendChild(UM.uNode.createText(node.getAttr('data-latex'))); + node.setAttr({ + 'frameborder': '', + 'src': '', + 'data-latex': '' + }); + } + }); + }); + me.addListener('click', function(){ + blurActiveIframe(); + }); + me.addListener('afterexeccommand', function(type, cmd){ + if(cmd != 'formula') { + blurActiveIframe(); + } + }); + + me.commands['formula'] = { + execCommand: function (cmd, latex) { + var iframe = getActiveIframe(); + if (iframe) { + iframe.contentWindow.formula.insertLatex(latex); + } else { + me.execCommand('inserthtml', '' + latex + ''); + browser.ie && browser.ie9below && setTimeout(function(){ + var rng = me.selection.getRange(), + startContainer = rng.startContainer; + if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){ + rng.insertNode(me.document.createTextNode(' ')); + rng.setCursor() + } + },100) + } + }, + queryCommandState: function (cmd) { + return 0; + }, + queryCommandValue: function (cmd) { + var iframe = getActiveIframe(); + return iframe && iframe.contentWindow.formula.getLatex(); + } + } + + }; + + (function ($) { + //对jquery的扩展 + $.parseTmpl = function parse(str, data) { + var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) { + return "'," + code.replace(/\\'/g, "'") + ",'"; + }).replace(/<%([\s\S]+?)%>/g,function (match, code) { + return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('"; + }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');"; + var func = new Function('obj', tmpl); + return data ? func(data) : func; + }; + $.extend2 = function (t, s) { + var a = arguments, + notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false, + len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length; + for (var i = 1; i < len; i++) { + var x = a[i]; + for (var k in x) { + if (!notCover || !t.hasOwnProperty(k)) { + t[k] = x[k]; + } + } + } + return t; + }; + + $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6; + + //所有ui的基类 + var _eventHandler = []; + var _widget = function () { + }; + var _prefix = 'edui'; + _widget.prototype = { + on: function (ev, cb) { + this.root().on(ev, $.proxy(cb, this)); + return this; + }, + off: function (ev, cb) { + this.root().off(ev, $.proxy(cb, this)); + return this; + }, + trigger: function (ev, data) { + return this.root().trigger(ev, data) === false ? false : this; + }, + root: function ($el) { + return this._$el || (this._$el = $el); + }, + destroy: function () { + + }, + data: function (key, val) { + if (val !== undefined) { + this.root().data(_prefix + key, val); + return this; + } else { + return this.root().data(_prefix + key) + } + }, + register: function (eventName, $el, fn) { + _eventHandler.push({ + 'evtname': eventName, + '$els': $.isArray($el) ? $el : [$el], + handler: $.proxy(fn, $el) + }) + } + }; + + //从jq实例上拿到绑定的widget实例 + $.fn.edui = function (obj) { + return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget'); + }; + + function _createClass(ClassObj, properties, supperClass) { + ClassObj.prototype = $.extend2( + $.extend({}, properties), + (UM.ui[supperClass] || _widget).prototype, + true + ); + ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype; + //父class的defaultOpt 合并 + if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) { + + var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt, + subDefaultOptions = ClassObj.prototype.defaultOpt; + + ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} ); + + } + return ClassObj + } + + var _guid = 1; + + function mergeToJQ(ClassObj, className) { + $[_prefix + className] = ClassObj; + $.fn[_prefix + className] = function (opt) { + var result, args = Array.prototype.slice.call(arguments, 1); + + this.each(function (i, el) { + var $this = $(el); + var obj = $this.edui(); + if (!obj) { + ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this); + $this.edui(obj) + } + if ($.type(opt) == 'string') { + if (opt == 'this') { + result = obj; + } else { + result = obj[opt].apply(obj, args); + if (result !== obj && result !== undefined) { + return false; + } + result = null; + } + + } + }); + + return result !== null ? result : this; + } + } + + UM.ui = { + define: function (className, properties, supperClass) { + var ClassObj = UM.ui[className] = _createClass(function (options, $el) { + var _obj = function () { + }; + $.extend(_obj.prototype, ClassObj.prototype, { + guid: className + _guid++, + widgetName: className + } + ); + var obj = new _obj; + if ($.type(options) == 'string') { + obj.init && obj.init({}); + obj.root().edui(obj); + obj.root().find('a').click(function (evt) { + evt.preventDefault() + }); + return obj.root()[_prefix + className].apply(obj.root(), arguments) + } else { + $el && obj.root($el); + obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options); + try{ + obj.root().find('a').click(function (evt) { + evt.preventDefault() + }); + }catch(e){ + } + + return obj.root().edui(obj); + } + + },properties, supperClass); + + mergeToJQ(ClassObj, className); + } + }; + + $(function () { + $(document).on('click mouseup mousedown dblclick mouseover', function (evt) { + $.each(_eventHandler, function (i, obj) { + if (obj.evtname == evt.type) { + $.each(obj.$els, function (i, $el) { + if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) { + obj.handler(evt); + } + }) + } + }) + }) + }) + })(jQuery); +//button 类 + UM.ui.define('button', { + tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%><% if(title) {%> data-original-title="<%=title%>" <%};%>> ' + + '<% if(icon) {%>
          <% }; %><%if(text) {%><%=text%><%}%>' + + '<%if(caret && text){%><%}%>' + + '<% if(caret) {%><% };%>div<%}else{%>a<%}%>>', + defaultOpt: { + text: '', + title: '', + icon: '', + width: '', + caret: false, + texttype: false, + click: function () { + } + }, + init: function (options) { + var me = this; + + me.root($($.parseTmpl(me.tpl, options))) + .click(function (evt) { + me.wrapclick(options.click, evt) + }); + + me.root().hover(function () { + if(!me.root().hasClass("edui-disabled")){ + me.root().toggleClass('edui-hover') + } + }) + + return me; + }, + wrapclick: function (fn, evt) { + if (!this.disabled()) { + this.root().trigger('wrapclick'); + $.proxy(fn, this, evt)() + } + return this; + }, + label: function (text) { + if (text === undefined) { + return this.root().find('.edui-button-label').text(); + } else { + this.root().find('.edui-button-label').text(text); + return this; + } + }, + disabled: function (state) { + if (state === undefined) { + return this.root().hasClass('edui-disabled') + } + this.root().toggleClass('edui-disabled', state); + if(this.root().hasClass('edui-disabled')){ + this.root().removeClass('edui-hover') + } + return this; + }, + active: function (state) { + if (state === undefined) { + return this.root().hasClass('edui-active') + } + this.root().toggleClass('edui-active', state) + + return this; + }, + mergeWith: function ($obj) { + var me = this; + me.data('$mergeObj', $obj); + $obj.edui().data('$mergeObj', me.root()); + if (!$.contains(document.body, $obj[0])) { + $obj.appendTo(me.root()); + } + me.on('click',function () { + me.wrapclick(function () { + $obj.edui().show(); + }) + }).register('click', me.root(), function (evt) { + $obj.hide() + }); + } + }); +//toolbar 类 + (function () { + UM.ui.define('toolbar', { + tpl: '
          ' + , + init: function () { + var $root = this.root($(this.tpl)); + this.data('$btnToolbar', $root.find('.edui-btn-toolbar')) + }, + appendToBtnmenu : function(data){ + var $cont = this.data('$btnToolbar'); + data = $.isArray(data) ? data : [data]; + $.each(data,function(i,$item){ + $cont.append($item) + }) + } + }); + })(); + +//menu 类 + UM.ui.define('menu',{ + show : function($obj,dir,fnname,topOffset,leftOffset){ + + fnname = fnname || 'position'; + if(this.trigger('beforeshow') === false){ + return; + }else{ + this.root().css($.extend({display:'block'},$obj ? { + top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0), + left : $obj[fnname]().left + (dir == 'right' ? $obj.outerWidth() : 0) - (leftOffset || 0) + }:{})) + this.trigger('aftershow'); + } + }, + hide : function(all){ + var $parentmenu; + if(this.trigger('beforehide') === false){ + return; + } else { + + if($parentmenu = this.root().data('parentmenu')){ + if($parentmenu.data('parentmenu')|| all) + $parentmenu.edui().hide(); + } + this.root().css('display','none'); + this.trigger('afterhide'); + } + }, + attachTo : function($obj){ + var me = this; + if(!$obj.data('$mergeObj')){ + $obj.data('$mergeObj',me.root()); + $obj.on('wrapclick',function(evt){ + me.show() + }); + me.register('click',$obj,function(evt){ + me.hide() + }); + me.data('$mergeObj',$obj) + } + } + }); +//dropmenu 类 + UM.ui.define('dropmenu', { + tmpl: '
            ' + + '<%for(var i=0,ci;ci=data[i++];){%>' + + '<%if(ci.divider){%>
          • <%}else{%>' + + '
          • class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' + + '<%= ci.label%>' + + '
          • <%}%>' + + '<%}%>' + + '
          ', + defaultOpt: { + data: [], + click: function () { + + } + }, + init: function (options) { + var me = this; + var eventName = { + click: 1, + mouseover: 1, + mouseout: 1 + }; + + this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) { + $.proxy(options.click, me, evt, $(this).data('value'), $(this))() + }).find('li').each(function (i, el) { + var $this = $(this); + if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) { + var data = options.data[i]; + $.each(eventName, function (k) { + data[k] && $this[k](function (evt) { + $.proxy(data[k], el)(evt, data, me.root) + }) + }) + } + }) + + }, + disabled: function (cb) { + $('li[class!=edui-divider]', this.root()).each(function () { + var $el = $(this); + if (cb === true) { + $el.addClass('edui-disabled') + } else if ($.isFunction(cb)) { + $el.toggleClass('edui-disabled', cb(li)) + } else { + $el.removeClass('edui-disabled') + } + + }); + }, + val: function (val) { + var currentVal; + $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () { + var $el = $(this); + if (val === undefined) { + if ($el.find('em.edui-dropmenu-checked').length) { + currentVal = $el.data('value'); + return false + } + } else { + $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val) + } + }); + if (val === undefined) { + return currentVal + } + }, + addSubmenu: function (label, menu, index) { + index = index || 0; + + var $list = $('li[class!=edui-divider]', this.root()); + var $node = $('
        10. ' + label + '
        11. ').append(menu); + + if (index >= 0 && index < $list.length) { + $node.insertBefore($list[index]); + } else if (index < 0) { + $node.insertBefore($list[0]); + } else if (index >= $list.length) { + $node.appendTo($list); + } + } + }, 'menu'); +//splitbutton 类 +///import button + UM.ui.define('splitbutton',{ + tpl :'
          data-original-title="<%=title%>"<%}%>>
          <%if(icon){%>
          <%}%><%if(text){%><%=text%><%}%>
          '+ + '
          '+ + '
          <\/div>'+ + '
          '+ + '
          ', + defaultOpt:{ + text:'', + title:'', + click:function(){} + }, + init : function(options){ + var me = this; + me.root( $($.parseTmpl(me.tpl,options))); + me.root().find('.edui-btn:first').click(function(evt){ + if(!me.disabled()){ + $.proxy(options.click,me)(); + } + }); + me.root().find('.edui-dropdown-toggle').click(function(){ + if(!me.disabled()){ + me.trigger('arrowclick') + } + }); + me.root().hover(function () { + if(!me.root().hasClass("edui-disabled")){ + me.root().toggleClass('edui-hover') + } + }); + + return me; + }, + wrapclick:function(fn,evt){ + if(!this.disabled()){ + $.proxy(fn,this,evt)() + } + return this; + }, + disabled : function(state){ + if(state === undefined){ + return this.root().hasClass('edui-disabled') + } + this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state); + return this; + }, + active:function(state){ + if(state === undefined){ + return this.root().hasClass('edui-active') + } + this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state); + return this; + }, + mergeWith:function($obj){ + var me = this; + me.data('$mergeObj',$obj); + $obj.edui().data('$mergeObj',me.root()); + if(!$.contains(document.body,$obj[0])){ + $obj.appendTo(me.root()); + } + me.root().delegate('.edui-dropdown-toggle','click',function(){ + me.wrapclick(function(){ + $obj.edui().show(); + }) + }); + me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){ + $obj.hide() + }); + } + }); + /** + * Created with JetBrains PhpStorm. + * User: hn + * Date: 13-7-10 + * Time: 下午3:07 + * To change this template use File | Settings | File Templates. + */ + UM.ui.define('colorsplitbutton',{ + + tpl : '
          data-original-title="<%=title%>"<%}%>>
          <%if(icon){%>
          <%}%>
          style="background: <%=color%>"<%}%>>
          <%if(text){%><%=text%><%}%>
          '+ + '
          '+ + '
          <\/div>'+ + '
          '+ + '
          ', + defaultOpt: { + color: '' + }, + init: function( options ){ + + var me = this; + + me.supper.init.call( me, options ); + + }, + colorLabel: function(){ + return this.root().find('.edui-splitbutton-color-label'); + } + + }, 'splitbutton'); +//popup 类 + UM.ui.define('popup', { + tpl: '
          ){%>onmousedown="return false"<%}%>'+ + '>
          <%=subtpl%>
          ' + + '
          ' + + '
          ', + defaultOpt: { + stopprop:false, + subtpl: '', + width: '', + height: '' + }, + init: function (options) { + this.root($($.parseTmpl(this.tpl, options))); + return this; + }, + mergeTpl: function (data) { + return $.parseTmpl(this.tpl, {subtpl: data}); + }, + show: function ($obj, posObj) { + if (!posObj) posObj = {}; + + var fnname = posObj.fnname || 'position'; + if (this.trigger('beforeshow') === false) { + return; + } else { + this.root().css($.extend({display: 'block'}, $obj ? { + top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0), + left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0), + position: 'absolute' + } : {})); + + this.root().find('.edui-popup-caret').css({ + top: posObj.caretTop || 0, + left: posObj.caretLeft || 0, + position: 'absolute' + }).addClass(posObj.caretDir || "up") + + } + this.trigger("aftershow"); + }, + hide: function () { + this.root().css('display', 'none'); + this.trigger('afterhide') + }, + attachTo: function ($obj, posObj) { + var me = this + if (!$obj.data('$mergeObj')) { + $obj.data('$mergeObj', me.root()); + $obj.on('wrapclick', function (evt) { + me.show($obj, posObj) + }); + me.register('click', $obj, function (evt) { + me.hide() + }); + me.data('$mergeObj', $obj) + } + }, + getBodyContainer: function () { + return this.root().find(".edui-popup-body"); + } + }); +//scale 类 + UM.ui.define('scale', { + tpl: '
          ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
          ', + defaultOpt: { + $doc: $(document), + $wrap: $(document) + }, + init: function (options) { + if(options.$doc) this.defaultOpt.$doc = options.$doc; + if(options.$wrap) this.defaultOpt.$wrap = options.$wrap; + this.root($($.parseTmpl(this.tpl, options))); + this.initStyle(); + this.startPos = this.prePos = {x: 0, y: 0}; + this.dragId = -1; + return this; + }, + initStyle: function () { + utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' + + '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}' + + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}' + + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}' + + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}' + + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}' + + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}' + + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}' + + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}' + + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}'); + }, + _eventHandler: function (e) { + var me = this, + $doc = me.defaultOpt.$doc; + switch (e.type) { + case 'mousedown': + var hand = e.target || e.srcElement, hand; + if (hand.className.indexOf('edui-scale-hand') != -1) { + me.dragId = hand.className.slice(-1); + me.startPos.x = me.prePos.x = e.clientX; + me.startPos.y = me.prePos.y = e.clientY; + $doc.bind('mousemove', $.proxy(me._eventHandler, me)); + } + break; + case 'mousemove': + if (me.dragId != -1) { + me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y}); + me.prePos.x = e.clientX; + me.prePos.y = e.clientY; + me.updateTargetElement(); + } + break; + case 'mouseup': + if (me.dragId != -1) { + me.dragId = -1; + me.updateTargetElement(); + var $target = me.data('$scaleTarget'); + if ($target.parent()) me.attachTo(me.data('$scaleTarget')); + } + $doc.unbind('mousemove', $.proxy(me._eventHandler, me)); + break; + default: + break; + } + }, + updateTargetElement: function () { + var me = this, + $root = me.root(), + $target = me.data('$scaleTarget'); + $target.css({width: $root.width(), height: $root.height()}); + me.attachTo($target); + }, + updateContainerStyle: function (dir, offset) { + var me = this, + $dom = me.root(), + tmp, + rect = [ + //[left, top, width, height] + [0, 0, -1, -1], + [0, 0, 0, -1], + [0, 0, 1, -1], + [0, 0, -1, 0], + [0, 0, 1, 0], + [0, 0, -1, 1], + [0, 0, 0, 1], + [0, 0, 1, 1] + ]; + + if (rect[dir][0] != 0) { + tmp = parseInt($dom.offset().left) + offset.x; + $dom.css('left', me._validScaledProp('left', tmp)); + } + if (rect[dir][1] != 0) { + tmp = parseInt($dom.offset().top) + offset.y; + $dom.css('top', me._validScaledProp('top', tmp)); + } + if (rect[dir][2] != 0) { + tmp = $dom.width() + rect[dir][2] * offset.x; + $dom.css('width', me._validScaledProp('width', tmp)); + } + if (rect[dir][3] != 0) { + tmp = $dom.height() + rect[dir][3] * offset.y; + $dom.css('height', me._validScaledProp('height', tmp)); + } + }, + _validScaledProp: function (prop, value) { + var $ele = this.root(), + $wrap = this.defaultOpt.$doc, + calc = function(val, a, b){ + return (val + a) > b ? b - a : value; + }; + + value = isNaN(value) ? 0 : value; + switch (prop) { + case 'left': + return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width()); + case 'top': + return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height()); + case 'width': + return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width()); + case 'height': + return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height()); + } + }, + show: function ($obj) { + var me = this; + if ($obj) me.attachTo($obj); + me.root().bind('mousedown', $.proxy(me._eventHandler, me)); + me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me)); + me.root().show(); + me.trigger("aftershow"); + }, + hide: function () { + var me = this; + me.root().unbind('mousedown', $.proxy(me._eventHandler, me)); + me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me)); + me.root().hide(); + me.trigger('afterhide') + }, + attachTo: function ($obj) { + var me = this, + imgPos = $obj.offset(), + $root = me.root(), + $wrap = me.defaultOpt.$wrap, + posObj = $wrap.offset(); + + me.data('$scaleTarget', $obj); + me.root().css({ + position: 'absolute', + width: $obj.width(), + height: $obj.height(), + left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')), + top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width')) + }); + }, + getScaleTarget: function () { + return this.data('$scaleTarget')[0]; + } + }); +//colorpicker 类 + UM.ui.define('colorpicker', { + tpl: function (opt) { + var COLORS = ( + 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' + + 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' + + 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' + + 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' + + 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' + + '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' + + 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(','); + + var html = '
          ' + + '' + + '' + + ''; + + for (var i = 0; i < COLORS.length; i++) { + if (i && i % 10 === 0) { + html += '' + (i == 60 ? '' : '') + ''; + } + html += i < 70 ? '' : ''; + } + html += '
          '+opt.lang_themeColor+'
          '+opt.lang_standardColor+'
          '; + return html; + }, + init: function (options) { + var me = this; + me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)),options))); + + me.root().on("click",function (e) { + me.trigger('pickcolor', $(e.target).data('color')); + }); + } + }, 'popup'); + /** + * Created with JetBrains PhpStorm. + * User: hn + * Date: 13-5-29 + * Time: 下午8:01 + * To change this template use File | Settings | File Templates. + */ + + (function(){ + + var widgetName = 'combobox', + itemClassName = 'edui-combobox-item', + HOVER_CLASS = 'edui-combobox-item-hover', + ICON_CLASS = 'edui-combobox-checked-icon', + labelClassName = 'edui-combobox-item-label'; + + UM.ui.define( widgetName, ( function(){ + + return { + tpl: "
            edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" + + "<%if(autoRecord) {%>" + + "<%for( var i=0, len = recordStack.length; i" + + "<%var index = recordStack[i];%>" + + "
          • <%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" + + "" + + "" + + "
          • " + + "<%}%>" + + "<%if( i ) {%>" + + "
          • " + + "<%}%>" + + "<%}%>" + + "<%for( var i=0, label; label = items[i]; i++ ) {%>" + + "
          • <%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" + + "" + + "" + + "
          • " + + "<%}%>" + + "
          ", + defaultOpt: { + //记录栈初始列表 + recordStack: [], + //可用项列表 + items: [], + //item对应的值列表 + value: [], + comboboxName: '', + selected: '', + //自动记录 + autoRecord: true, + //最多记录条数 + recordCount: 5 + }, + init: function( options ){ + + var me = this; + + $.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), { + itemClassName: itemClassName, + iconClass: ICON_CLASS, + labelClassName: labelClassName + } ); + + this._transStack( options ); + + me.root( $( $.parseTmpl( me.tpl, options ) ) ); + + this.data( 'options', options ).initEvent(); + + }, + initEvent: function(){ + + var me = this; + + me.initSelectItem(); + + this.initItemActive(); + + }, + /** + * 初始化选择项 + */ + initSelectItem: function(){ + + var me = this, + labelClass = "."+labelClassName; + + me.root().delegate('.' + itemClassName, 'click', function(){ + + var $li = $(this), + index = $li.attr('data-item-index'); + + me.trigger('comboboxselect', { + index: index, + label: $li.find(labelClass).text(), + value: me.data('options').value[ index ] + }).select( index ); + + me.hide(); + + return false; + + }); + + }, + initItemActive: function(){ + var fn = { + mouseenter: 'addClass', + mouseleave: 'removeClass' + }; + if ($.IE6) { + this.root().delegate( '.'+itemClassName, 'mouseenter mouseleave', function( evt ){ + $(this)[ fn[ evt.type ] ]( HOVER_CLASS ); + }).one('afterhide', function(){ + }); + } + }, + /** + * 选择给定索引的项 + * @param index 项索引 + * @returns {*} 如果存在对应索引的项,则返回该项;否则返回null + */ + select: function( index ){ + + var itemCount = this.data('options').itemCount, + items = this.data('options').autowidthitem; + + if ( items && !items.length ) { + items = this.data('options').items; + } + + if( itemCount == 0 ) { + return null; + } + + if( index < 0 ) { + + index = itemCount + index % itemCount; + + } else if ( index >= itemCount ) { + + index = itemCount-1; + + } + + this.trigger( 'changebefore', items[ index ] ); + + this._update( index ); + + this.trigger( 'changeafter', items[ index ] ); + + return null; + + }, + selectItemByLabel: function( label ){ + + var itemMapping = this.data('options').itemMapping, + me = this, + index = null; + + !$.isArray( label ) && ( label = [ label ] ); + + $.each( label, function( i, item ){ + + index = itemMapping[ item ]; + + if( index !== undefined ) { + + me.select( index ); + return false; + + } + + } ); + + }, + /** + * 转换记录栈 + */ + _transStack: function( options ) { + + var temp = [], + itemIndex = -1, + selected = -1; + + $.each( options.recordStack, function( index, item ){ + + itemIndex = options.itemMapping[ item ]; + + if( $.isNumeric( itemIndex ) ) { + + temp.push( itemIndex ); + + //selected的合法性检测 + if( item == options.selected ) { + selected = itemIndex; + } + + } + + } ); + + options.recordStack = temp; + options.selected = selected; + temp = null; + + }, + _optionAdaptation: function( options ) { + + if( !( 'itemStyles' in options ) ) { + + options.itemStyles = []; + + for( var i = 0, len = options.items.length; i < len; i++ ) { + options.itemStyles.push(''); + } + + } + + options.autowidthitem = options.autowidthitem || options.items; + options.itemCount = options.items.length; + + return options; + + }, + _createItemMapping: function( stackItem, items ){ + + var temp = {}, + result = { + recordStack: [], + mapping: {} + }; + + $.each( items, function( index, item ){ + temp[ item ] = index; + } ); + + result.itemMapping = temp; + + $.each( stackItem, function( index, item ){ + + if( temp[ item ] !== undefined ) { + result.recordStack.push( temp[ item ] ); + result.mapping[ item ] = temp[ item ]; + } + + } ); + + return result; + + }, + _update: function ( index ) { + + var options = this.data("options"), + newStack = [], + newChilds = null; + + $.each( options.recordStack, function( i, item ){ + + if( item != index ) { + newStack.push( item ); + } + + } ); + + //压入最新的记录 + newStack.unshift( index ); + + if( newStack.length > options.recordCount ) { + newStack.length = options.recordCount; + } + + options.recordStack = newStack; + options.selected = index; + + newChilds = $( $.parseTmpl( this.tpl, options ) ); + + //重新渲染 + this.root().html( newChilds.html() ); + + newChilds = null; + newStack = null; + + } + }; + + } )(), 'menu' ); + + })(); + + /** + * Combox 抽象基类 + * User: hn + * Date: 13-5-29 + * Time: 下午8:01 + * To change this template use File | Settings | File Templates. + */ + + (function(){ + + var widgetName = 'buttoncombobox'; + + UM.ui.define( widgetName, ( function(){ + + return { + defaultOpt: { + //按钮初始文字 + label: '', + title: '' + }, + init: function( options ) { + + var me = this; + + var btnWidget = $.eduibutton({ + caret: true, + name: options.comboboxName, + title: options.title, + text: options.label, + click: function(){ + me.show( this.root() ); + } + }); + + me.supper.init.call( me, options ); + + //监听change, 改变button显示内容 + me.on('changebefore', function( e, label ){ + btnWidget.eduibutton('label', label ); + }); + + me.data( 'button', btnWidget ); + + me.attachTo(btnWidget) + + }, + button: function(){ + return this.data( 'button' ); + } + } + + } )(), 'combobox' ); + + })(); + + /*modal 类*/ + UM.ui.define('modal', { + tpl: '
          ' + + '
          ' + + '
          ' + + '

          <%=title%>

          ' + + '
          ' + + '
          ' + + '
          ' + + '<% if(cancellabel || oklabel) {%>' + + '' + + '<%}%>
          ', + defaultOpt: { + title: "", + cancellabel: "", + oklabel: "", + width: '', + height: '', + backdrop: true, + keyboard: true + }, + init: function (options) { + var me = this; + + me.root($($.parseTmpl(me.tpl, options || {}))); + + me.data("options", options); + if (options.okFn) { + me.on('ok', $.proxy(options.okFn, me)) + } + if (options.cancelFn) { + me.on('beforehide', $.proxy(options.cancelFn, me)) + } + + me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me)) + .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me)); + + $('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){ + $(this).toggleClass('edui-hover') + }); + }, + toggle: function () { + var me = this; + return me[!me.data("isShown") ? 'show' : 'hide'](); + }, + show: function () { + + var me = this; + + me.trigger("beforeshow"); + + if (me.data("isShown")) return; + + me.data("isShown", true); + + me.escape(); + + me.backdrop(function () { + me.autoCenter(); + me.root() + .show() + .focus() + .trigger('aftershow'); + }) + }, + showTip: function ( text ) { + $( '.edui-modal-tip', this.root() ).html( text ).fadeIn(); + }, + hideTip: function ( text ) { + $( '.edui-modal-tip', this.root() ).fadeOut( function (){ + $(this).html(''); + } ); + }, + autoCenter: function () { + //ie6下不用处理了 + !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2)); + }, + hide: function () { + var me = this; + + me.trigger("beforehide"); + + if (!me.data("isShown")) return; + + me.data("isShown", false); + + me.escape(); + + me.hideModal(); + }, + escape: function () { + var me = this; + if (me.data("isShown") && me.data("options").keyboard) { + me.root().on('keyup', function (e) { + e.which == 27 && me.hide(); + }) + } + else if (!me.data("isShown")) { + me.root().off('keyup'); + } + }, + hideModal: function () { + var me = this; + me.root().hide(); + me.backdrop(function () { + me.removeBackdrop(); + me.trigger('afterhide'); + }) + }, + removeBackdrop: function () { + this.$backdrop && this.$backdrop.remove(); + this.$backdrop = null; + }, + backdrop: function (callback) { + var me = this; + if (me.data("isShown") && me.data("options").backdrop) { + me.$backdrop = $('
          ').click( + me.data("options").backdrop == 'static' ? + $.proxy(me.root()[0].focus, me.root()[0]) + : $.proxy(me.hide, me) + ) + } + me.trigger('afterbackdrop'); + callback && callback(); + + }, + attachTo: function ($obj) { + var me = this + if (!$obj.data('$mergeObj')) { + + $obj.data('$mergeObj', me.root()); + $obj.on('click', function () { + me.toggle($obj) + }); + me.data('$mergeObj', $obj) + } + }, + ok: function () { + var me = this; + me.trigger('beforeok'); + if (me.trigger("ok", me) === false) { + return; + } + me.hide(); + }, + getBodyContainer: function () { + return this.root().find('.edui-modal-body') + } + }); + + + /*tooltip 类*/ + UM.ui.define('tooltip', { + tpl: '
          ' + + '
          ' + + '
          ' + + '
          ', + init: function (options) { + var me = this; + me.root($($.parseTmpl(me.tpl, options || {}))); + }, + content: function (e) { + var me = this, + title = $(e.currentTarget).attr("data-original-title"); + + me.root().find('.edui-tooltip-inner')['text'](title); + }, + position: function (e) { + var me = this, + $obj = $(e.currentTarget); + + me.root().css($.extend({display: 'block'}, $obj ? { + top: $obj.outerHeight(), + left: (($obj.outerWidth() - me.root().outerWidth()) / 2) + } : {})) + }, + show: function (e) { + if ($(e.currentTarget).hasClass('edui-disabled')) return; + + var me = this; + me.content(e); + me.root().appendTo($(e.currentTarget)); + me.position(e); + me.root().css('display', 'block'); + }, + hide: function () { + var me = this; + me.root().css('display', 'none') + }, + attachTo: function ($obj) { + var me = this; + + function tmp($obj) { + var me = this; + + if (!$.contains(document.body, me.root()[0])) { + me.root().appendTo($obj); + } + + me.data('tooltip', me.root()); + + $obj.each(function () { + if ($(this).attr("data-original-title")) { + $(this).on('mouseenter', $.proxy(me.show, me)) + .on('mouseleave click', $.proxy(me.hide, me)) + + } + }); + + } + + if ($.type($obj) === "undefined") { + $("[data-original-title]").each(function (i, el) { + tmp.call(me, $(el)); + }) + + } else { + if (!$obj.data('tooltip')) { + tmp.call(me, $obj); + } + } + } + }); + + /*tab 类*/ + UM.ui.define('tab', { + init: function (options) { + var me = this, + slr = options.selector; + + if ($.type(slr)) { + me.root($(slr, options.context)); + me.data("context", options.context); + + $(slr, me.data("context")).on('click', function (e) { + me.show(e); + }); + } + }, + show: function (e) { + + var me = this, + $cur = $(e.target), + $ul = $cur.closest('ul'), + selector, + previous, + $target, + e; + + selector = $cur.attr('data-context'); + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); + + var $tmp = $cur.parent('li'); + + if (!$tmp.length || $tmp.hasClass('edui-active')) return; + + previous = $ul.find('.edui-active:last a')[0]; + + e = $.Event('beforeshow', { + target: $cur[0], + relatedTarget: previous + }); + + me.trigger(e); + + if (e.isDefaultPrevented()) return; + + $target = $(selector, me.data("context")); + + me.activate($cur.parent('li'), $ul); + me.activate($target, $target.parent(), function () { + me.trigger({ + type: 'aftershow', relatedTarget: previous + }) + }); + }, + activate: function (element, container, callback) { + if (element === undefined) { + return $(".edui-tab-item.edui-active",this.root()).index(); + } + + var $active = container.find('> .edui-active'); + + $active.removeClass('edui-active'); + + element.addClass('edui-active'); + + callback && callback(); + } + }); + + +//button 类 + UM.ui.define('separator', { + tpl: '
          ', + init: function (options) { + var me = this; + me.root($($.parseTmpl(me.tpl, options))); + return me; + } + }); + /** + * @file adapter.js + * @desc adapt ui to editor + * @import core/Editor.js, core/utils.js + */ + + (function () { + var _editorUI = {}, + _editors = {}, + _readyFn = [], + _activeWidget = null, + _widgetData = {}, + _widgetCallBack = {}, + _cacheUI = {}, + _maxZIndex = null; + + utils.extend(UM, { + defaultWidth : 500, + defaultHeight : 500, + registerUI: function (name, fn) { + utils.each(name.split(/\s+/), function (uiname) { + _editorUI[uiname] = fn; + }) + }, + + setEditor : function(editor){ + !_editors[editor.id] && (_editors[editor.id] = editor); + }, + registerWidget : function(name,pro,cb){ + _widgetData[name] = $.extend2(pro,{ + $root : '', + _preventDefault:false, + root:function($el){ + return this.$root || (this.$root = $el); + }, + preventDefault:function(){ + this._preventDefault = true; + }, + clear:false + }); + if(cb){ + _widgetCallBack[name] = cb; + } + }, + getWidgetData : function(name){ + return _widgetData[name] + }, + setWidgetBody : function(name,$widget,editor){ + if(!editor._widgetData){ + + utils.extend(editor,{ + _widgetData : {}, + getWidgetData : function(name){ + return this._widgetData[name]; + }, + getWidgetCallback : function(widgetName){ + var me = this; + return function(){ + return _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0))) + } + } + }) + + } + var pro = _widgetData[name]; + if(!pro){ + return null; + } + pro = editor._widgetData[name]; + if(!pro){ + pro = _widgetData[name]; + pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro); + } + + pro.root($widget.edui().getBodyContainer()); + + pro.initContent(editor,$widget); + if(!pro._preventDefault){ + pro.initEvent(editor,$widget); + } + + pro.width && $widget.width(pro.width); + + + }, + setActiveWidget : function($widget){ + _activeWidget = $widget; + }, + getEditor: function (id, options) { + var editor = _editors[id] || (_editors[id] = this.createEditor(id, options)); + _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex'); + return editor; + }, + setTopEditor: function(editor){ + $.each(_editors, function(i, o){ + if(editor == o) { + editor.$container && editor.$container.css('zIndex', _maxZIndex + 1); + } else { + o.$container && o.$container.css('zIndex', o.getOpt('zIndex')); + } + }); + }, + clearCache : function(id){ + if ( _editors[id]) { + delete _editors[id] + } + }, + delEditor: function (id) { + var editor; + if (editor = _editors[id]) { + editor.destroy(); + } + }, + ready: function( fn ){ + _readyFn.push( fn ); + }, + createEditor: function (id, opt) { + var editor = new UM.Editor(opt); + var T = this; + + editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T)); + function renderUI(){ + + + var $container = this.createUI('#' + id, editor); + editor.key=id; + editor.ready(function(){ + $.each( _readyFn, function( index, fn ){ + $.proxy( fn, editor )(); + } ); + }); + var options = editor.options; + if(options.initialFrameWidth){ + options.minFrameWidth = options.initialFrameWidth + }else{ + options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth; + } + + $container.css({ + width: options.initialFrameWidth, + zIndex:editor.getOpt('zIndex') + }); + + //ie6下缓存图片 + UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true); + + editor.render(id); + + + //添加tooltip; + $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1); + + $container.find('a').click(function(evt){ + evt.preventDefault() + }); + + editor.fireEvent("afteruiready"); + } + + return editor; + + }, + createUI: function (id, editor) { + var $editorCont = $(id), + $container = $('
          ').insertBefore($editorCont); + editor.$container = $container; + editor.container = $container[0]; + + editor.$body = $editorCont; + + //修正在ie9+以上的版本中,自动长高收起时的,残影问题 + if(browser.ie && browser.ie9above){ + var $span = $(''); + $span.insertAfter($container); + } + //初始化注册的ui组件 + $.each(_editorUI,function(n,v){ + var widget = v.call(editor,n); + if(widget){ + _cacheUI[n] = widget; + } + + }); + + $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor)); + + $container.find('.edui-toolbar').append($('
          ')); + + + return $container; + }, + createToolbar: function (options, editor) { + var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui(); + //创建下来菜单列表 + + if (options.toolbar && options.toolbar.length) { + var btns = []; + $.each(options.toolbar,function(i,uiNames){ + $.each(uiNames.split(/\s+/),function(index,name){ + if(name == '|'){ + $.eduiseparator && btns.push($.eduiseparator()); + }else{ + var ui = _cacheUI[name]; + if(name=="fullscreen"){ + ui&&btns.unshift(ui); + }else{ + ui && btns.push(ui); + } + } + + }); + btns.length && toolbar.appendToBtnmenu(btns); + }); + } else { + $toolbar.find('.edui-btn-toolbar').remove() + } + return $toolbar; + } + + }) + + + })(); + + + + UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' + + 'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts image', + function(name) { + var me = this; + var $btn = $.eduibutton({ + icon : name, + click : function(){ + me.execCommand(name); + }, + title: this.getLang('labelMap')[name] || '' + }); + + this.addListener('selectionchange',function(){ + var state = this.queryCommandState(name); + $btn.edui().disabled(state == -1).active(state == 1) + }); + return $btn; + } + ); + + + /** + * 全屏组件 + */ + + (function(){ + + //状态缓存 + var STATUS_CACHE = {}, + //状态值列表 + STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ], + CONTENT_AREA_STATUS = {}, + //页面状态 + DOCUMENT_STATUS = {}, + DOCUMENT_ELEMENT_STATUS = {}, + + FULLSCREENS = {}; + + + UM.registerUI('fullscreen', function( name ){ + + var me = this, + $button = $.eduibutton({ + 'icon': 'fullscreen', + 'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name), + 'click': function(){ + //切换 + me.execCommand( name ); + UM.setTopEditor(me); + } + }); + + me.addListener( "selectionchange", function () { + + var state = this.queryCommandState( name ); + $button.edui().disabled( state == -1 ).active( state == 1 ); + + } ); + + //切换至全屏 + me.addListener('ready', function(){ + + me.options.fullscreen && Fullscreen.getInstance( me ).toggle(); + + }); + + return $button; + + }); + + UM.commands[ 'fullscreen' ] = { + + execCommand: function (cmdName) { + + Fullscreen.getInstance( this ).toggle(); + + }, + queryCommandState: function (cmdName) { + + return this._edui_fullscreen_status; + }, + notNeedUndo: 1 + + }; + + function Fullscreen( editor ) { + + var me = this; + + if( !editor ) { + throw new Error('invalid params, notfound editor'); + } + + me.editor = editor; + + //记录初始化的全屏组件 + FULLSCREENS[ editor.uid ] = this; + + editor.addListener('destroy', function(){ + delete FULLSCREENS[ editor.uid ]; + me.editor = null; + }); + + } + + Fullscreen.prototype = { + + /** + * 全屏状态切换 + */ + toggle: function(){ + + var editor = this.editor, + //当前编辑器的缩放状态 + _edui_fullscreen_status = this.isFullState(); + editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status ); + + //更新状态 + this.update( !_edui_fullscreen_status ); + + !_edui_fullscreen_status ? this.enlarge() : this.revert(); + + editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status ); + if(editor.body.contentEditable === 'true'){ + editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status ); + } + + editor.fireEvent( 'selectionchange' ); + + }, + /** + * 执行放大 + */ + enlarge: function(){ + + this.saveSataus(); + + this.setDocumentStatus(); + + this.resize(); + + }, + /** + * 全屏还原 + */ + revert: function(){ + + //还原CSS表达式 + var options = this.editor.options, + height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width')); + + $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"'); + + //还原容器状态 + this.revertContainerStatus(); + + this.revertContentAreaStatus(); + + this.revertDocumentStatus(); + + }, + /** + * 更新状态 + * @param isFull 当前状态是否是全屏状态 + */ + update: function( isFull ) { + this.editor._edui_fullscreen_status = isFull; + }, + /** + * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态, 则不做调整 + */ + resize: function(){ + + var $win = null, + height = 0, + width = 0, + borderWidth = 0, + paddingWidth = 0, + editor = this.editor, + me = this, + bound = null, + editorBody = null; + + if( !this.isFullState() ) { + return; + } + + $win = $( window ); + width = $win.width(); + height = $win.height(); + editorBody = this.getEditorHolder(); + //文本编辑区border宽度 + borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0; + //容器border宽度 + borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0; + //容器padding + paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0; + + //干掉css表达式 + $.IE6 && editorBody.style.setExpression( 'height', null ); + + bound = this.getBound(); + + $( editor.container ).css( { + width: width + 'px', + height: height + 'px', + position: !$.IE6 ? 'fixed' : 'absolute', + top: bound.top, + left: bound.left, + margin: 0, + padding: 0, + overflowX: 'hidden', + overflowY: 'hidden' + } ); + + $( editorBody ).css({ + width: width - 2*borderWidth - paddingWidth + 'px', + height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px', + overflowX: 'hidden', + overflowY: 'auto' + }); + + }, + /** + * 保存状态 + */ + saveSataus: function(){ + + var styles = this.editor.container.style, + tmp = null, + cache = {}; + + for( var i= 0, len = STATUS_LIST.length; i offset) { + setFloating(); + }else{ + unsetFloating(); + } + } + var defer_updateFloating = utils.defer(function(){ + updateFloating(); + },browser.ie ? 200 : 100,true); + + me.addListener('destroy',function(){ + $(window).off('scroll resize',updateFloating); + me.removeListener('keydown', defer_updateFloating); + }); + + if(checkHasUI(me)){ + toolbarBox = $('.edui-toolbar',me.container)[0]; + me.addListener("afteruiready",function(){ + setTimeout(function(){ + orgTop = $(toolbarBox).offset().top; + },100); + }); + bakCssText = toolbarBox.style.cssText; + placeHolder.style.height = toolbarBox.offsetHeight + 'px'; + if(LteIE6){ + fixIE6FixedPos(); + } + + $(window).on('scroll resize',updateFloating); + me.addListener('keydown', defer_updateFloating); + me.addListener('resize', function(){ + unsetFloating(); + placeHolder.style.height = toolbarBox.offsetHeight + 'px'; + updateFloating(); + }); + + me.addListener('beforefullscreenchange', function (t, enabled){ + if (enabled) { + unsetFloating(); + isFullScreening = enabled; + } + }); + me.addListener('fullscreenchanged', function (t, enabled){ + if (!enabled) { + updateFloating(); + } + isFullScreening = enabled; + }); + me.addListener('sourcemodechanged', function (t, enabled){ + setTimeout(function (){ + updateFloating(); + },0); + }); + me.addListener("clearDoc",function(){ + setTimeout(function(){ + updateFloating(); + },0); + + }) + } + }) + + + }); + UM.registerUI('source',function(name){ + var me = this; + me.addListener('fullscreenchanged',function(){ + me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height()) + + }); + var $btn = $.eduibutton({ + icon : name, + click : function(){ + me.execCommand(name); + UM.setTopEditor(me); + }, + title: this.getLang('labelMap')[name] || '' + }); + + this.addListener('selectionchange',function(){ + var state = this.queryCommandState(name); + $btn.edui().disabled(state == -1).active(state == 1) + }); + return $btn; + }); + + UM.registerUI('paragraph fontfamily fontsize', function( name ) { + + var me = this, + label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name), + options = { + label: label, + title: label, + comboboxName: name, + items: me.options[ name ] || [], + itemStyles: [], + value: [], + autowidthitem: [] + }, + $combox = null, + comboboxWidget = null; + if(options.items.length == 0){ + return null; + } + switch ( name ) { + + case 'paragraph': + options = transForParagraph( options ); + break; + + case 'fontfamily': + options = transForFontfamily( options ); + break; + + case 'fontsize': + options = transForFontsize( options ); + break; + + } + + //实例化 + $combox = $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1); + comboboxWidget = $combox.edui(); + + comboboxWidget.on('comboboxselect', function( evt, res ){ + me.execCommand( name, res.value ); + }).on("beforeshow", function(){ + if( $combox.parent().length === 0 ) { + $combox.appendTo( me.$container.find('.edui-dialog-container') ); + } + UM.setTopEditor(me); + }); + + + //状态反射 + this.addListener('selectionchange',function( evt ){ + + var state = this.queryCommandState( name ), + value = this.queryCommandValue( name ); + + //设置按钮状态 + comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 ); + if(value){ + //设置label + value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/); + + comboboxWidget.selectItemByLabel( value ); + } + + + }); + + return comboboxWidget.button().addClass('edui-combobox'); + + /** + * 宽度自适应工具函数 + * @param word 单词内容 + * @param hasSuffix 是否含有后缀 + */ + function wordCountAdaptive ( word, hasSuffix ) { + + var $tmpNode = $('' ).html( word ).css( { + display: 'inline', + position: 'absolute', + top: -10000000, + left: -100000 + } ).appendTo( document.body), + width = $tmpNode.width(); + + $tmpNode.remove(); + $tmpNode = null; + + if( width < 50 ) { + + return word; + + } else { + + word = word.slice( 0, hasSuffix ? -4 : -1 ); + + if( !word.length ) { + return '...'; + } + + return wordCountAdaptive( word + '...', true ); + + } + + } + + + //段落参数转换 + function transForParagraph ( options ) { + + var tempItems = []; + + for( var key in options.items ) { + + options.value.push( key ); + tempItems.push( key ); + options.autowidthitem.push( wordCountAdaptive( key ) ); + + } + + options.items = tempItems; + options.autoRecord = false; + + return options; + + } + + //字体参数转换 + function transForFontfamily ( options ) { + + var temp = null, + tempItems = []; + + for( var i = 0, len = options.items.length; i < len; i++ ) { + + temp = options.items[ i ].val; + tempItems.push( temp.split(/\s*,\s*/)[0] ); + options.itemStyles.push('font-family: ' + temp); + options.value.push( temp ); + options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) ); + + } + + options.items = tempItems; + + return options; + + } + + //字体大小参数转换 + function transForFontsize ( options ) { + + var temp = null, + tempItems = []; + + options.itemStyles = []; + options.value = []; + + for( var i = 0, len = options.items.length; i < len; i++ ) { + + temp = options.items[ i ]; + tempItems.push( temp ); + options.itemStyles.push('font-size: ' + temp +'px'); + + } + + options.value = options.items; + options.items = tempItems; + options.autoRecord = false; + + return options; + + } + + }); + + + UM.registerUI('forecolor backcolor', function( name ) { + function getCurrentColor() { + return domUtils.getComputedStyle( $colorLabel[0], 'background-color' ); + } + + var me = this, + $colorPickerWidget = null, + $colorLabel = null, + $btn = null; + + //querycommand + this.addListener('selectionchange', function(){ + + var state = this.queryCommandState( name ); + $btn.edui().disabled( state == -1 ).active( state == 1 ); + + }); + + $btn = $.eduicolorsplitbutton({ + icon: name, + caret: true, + name: name, + title: me.getLang("labelMap")[name], + click: function() { + me.execCommand( name, getCurrentColor() ); + } + }); + + $colorLabel = $btn.edui().colorLabel(); + + $colorPickerWidget = $.eduicolorpicker({ + name: name, + lang_clearColor: me.getLang('clearColor') || '', + lang_themeColor: me.getLang('themeColor') || '', + lang_standardColor: me.getLang('standardColor') || '' + }) + .on('pickcolor', function( evt, color ){ + window.setTimeout( function(){ + $colorLabel.css("backgroundColor", color); + me.execCommand( name, color ); + }, 0 ); + }) + .on('show',function(){ + UM.setActiveWidget( colorPickerWidget.root() ); + }).css('zIndex',me.getOpt('zIndex') + 1); + + $btn.edui().on('arrowclick',function(){ + if(!$colorPickerWidget.parent().length){ + me.$container.find('.edui-dialog-container').append($colorPickerWidget); + } + $colorPickerWidget.edui().show($btn,{ + caretDir:"down", + offsetTop:-5, + offsetLeft:8, + caretLeft:11, + caretTop:-8 + }); + UM.setTopEditor(me); + }).register('click', $btn, function () { + $colorPickerWidget.edui().hide() + }); + + return $btn; + + }); + +})(jQuery) diff --git a/web/assets/common/plugins/umeditor/umeditor.min.js b/web/assets/common/plugins/umeditor/umeditor.min.js new file mode 100644 index 0000000..8bba185 --- /dev/null +++ b/web/assets/common/plugins/umeditor/umeditor.min.js @@ -0,0 +1,41 @@ +/*! + * UEditor Mini版本 + * version: 1.2.2 + * build: Wed Mar 19 2014 17:14:14 GMT+0800 (中国标准时间) + */ +(function($){UMEDITOR_CONFIG=window.UMEDITOR_CONFIG||{};window.UM={plugins:{},commands:{},I18N:{},version:"1.2.2"};var dom=UM.dom={};var browser=UM.browser=function(){var agent=navigator.userAgent.toLowerCase(),opera=window.opera,browser={ie:/(msie\s|trident.*rv:)([\w.]+)/.test(agent),opera:(!!opera&&opera.version),webkit:(agent.indexOf(" applewebkit/")>-1),mac:(agent.indexOf("macintosh")>-1),quirks:(document.compatMode=="BackCompat")};browser.gecko=(navigator.product=="Gecko"&&!browser.webkit&&!browser.opera&&!browser.ie);var version=0;if(browser.ie){var v1=agent.match(/(?:msie\s([\w.]+))/);var v2=agent.match(/(?:trident.*rv:([\w.]+))/);if(v1&&v2&&v1[1]&&v2[1]){version=Math.max(v1[1]*1,v2[1]*1)}else{if(v1&&v1[1]){version=v1[1]*1}else{if(v2&&v2[1]){version=v2[1]*1}else{version=0}}}browser.ie11Compat=document.documentMode==11;browser.ie9Compat=document.documentMode==9;browser.ie8=!!document.documentMode;browser.ie8Compat=document.documentMode==8;browser.ie7Compat=((version==7&&!document.documentMode)||document.documentMode==7);browser.ie6Compat=(version<7||browser.quirks);browser.ie9above=version>8;browser.ie9below=version<9}if(browser.gecko){var geckoRelease=agent.match(/rv:([\d\.]+)/);if(geckoRelease){geckoRelease=geckoRelease[1].split(".");version=geckoRelease[0]*10000+(geckoRelease[1]||0)*100+(geckoRelease[2]||0)*1}}if(/chrome\/(\d+\.\d)/i.test(agent)){browser.chrome=+RegExp["\x241"]}if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent)&&!/chrome/i.test(agent)){browser.safari=+(RegExp["\x241"]||RegExp["\x242"])}if(browser.opera){version=parseFloat(opera.version())}if(browser.webkit){version=parseFloat(agent.match(/ applewebkit\/(\d+)/)[1])}browser.version=version;browser.isCompatible=!browser.mobile&&((browser.ie&&version>=6)||(browser.gecko&&version>=10801)||(browser.opera&&version>=9.5)||(browser.air&&version>=1)||(browser.webkit&&version>=522)||false);return browser}();var ie=browser.ie,webkit=browser.webkit,gecko=browser.gecko,opera=browser.opera;var utils=UM.utils={each:function(obj,iterator,context){if(obj==null){return}if(obj.length===+obj.length){for(var i=0,l=obj.length;i=start&&v===item){index=i;return false}});return index},removeItem:function(array,item){for(var i=0,l=array.length;i'](?:(amp|lt|quot|gt|#39|nbsp);)?/g,function(a,b){if(b){return a}else{return{"<":"<","&":"&",'"':""",">":">","'":"'"}[a]}}):""},html:function(str){return str?str.replace(/&((g|l|quo)t|amp|#39);/g,function(m){return{"<":"<","&":"&",""":'"',">":">","'":"'"}[m]}):""},cssStyleToDomStyle:function(){var test=document.createElement("div").style,cache={"float":test.cssFloat!=undefined?"cssFloat":test.styleFloat!=undefined?"styleFloat":"float"};return function(cssName){return cache[cssName]||(cache[cssName]=cssName.toLowerCase().replace(/-./g,function(match){return match.charAt(1).toUpperCase()}))}}(),loadFile:function(){var tmpList=[];function getItem(doc,obj){try{for(var i=0,ci;ci=tmpList[i++];){if(ci.doc===doc&&ci.url==(obj.src||obj.href)){return ci}}}catch(e){return null}}return function(doc,obj,fn){var item=getItem(doc,obj);if(item){if(item.ready){fn&&fn()}else{item.funs.push(fn)}return}tmpList.push({doc:doc,url:obj.src||obj.href,funs:[fn]});if(!doc.body){var html=[];for(var p in obj){if(p=="tag"){continue}html.push(p+'="'+obj[p]+'"')}doc.write("<"+obj.tag+" "+html.join(" ")+" >");return}if(obj.id&&doc.getElementById(obj.id)){return}var element=doc.createElement(obj.tag); + delete obj.tag;for(var p in obj){element.setAttribute(p,obj[p])}element.onload=element.onreadystatechange=function(){if(!this.readyState||/loaded|complete/.test(this.readyState)){item=getItem(doc,obj);if(item.funs.length>0){item.ready=1;for(var fi;fi=item.funs.pop();){fi()}}element.onload=element.onreadystatechange=null}};element.onerror=function(){throw Error("The load "+(obj.href||obj.src)+" fails,check the url settings of file umeditor.config.js ")};doc.getElementsByTagName("head")[0].appendChild(element)}}(),isEmptyObject:function(obj){if(obj==null){return true}if(this.isArray(obj)||this.isString(obj)){return obj.length===0}for(var key in obj){if(obj.hasOwnProperty(key)){return false}}return true},fixColor:function(name,value){if(/color/i.test(name)&&/rgba?/.test(value)){var array=value.split(",");if(array.length>3){return""}value="#";for(var i=0,color;color=array[i++];){color=parseInt(color.replace(/[^\d]/gi,""),10).toString(16);value+=color.length==1?"0"+color:color}value=value.toUpperCase()}return value},clone:function(source,target){var tmp;target=target||{};for(var i in source){if(source.hasOwnProperty(i)){tmp=source[i];if(typeof tmp=="object"){target[i]=utils.isArray(tmp)?[]:{};utils.clone(source[i],target[i])}else{target[i]=tmp}}}return target},transUnitToPx:function(val){if(!/(pt|cm)/.test(val)){return val}var unit;val.replace(/([\d.]+)(\w+)/,function(str,v,u){val=v;unit=u});switch(unit){case"cm":val=parseFloat(val)*25;break;case"pt":val=Math.round(parseFloat(val)*96/72)}return val+(val?"px":"")},cssRule:browser.ie&&browser.version!=11?function(key,style,doc){var indexList,index;doc=doc||document;if(doc.indexList){indexList=doc.indexList}else{indexList=doc.indexList={}}var sheetStyle;if(!indexList[key]){if(style===undefined){return""}sheetStyle=doc.createStyleSheet("",index=doc.styleSheets.length);indexList[key]=index}else{sheetStyle=doc.styleSheets[indexList[key]]}if(style===undefined){return sheetStyle.cssText}sheetStyle.cssText=style||""}:function(key,style,doc){doc=doc||document;var head=doc.getElementsByTagName("head")[0],node;if(!(node=doc.getElementById(key))){if(style===undefined){return""}node=doc.createElement("style");node.id=key;head.appendChild(node)}if(style===undefined){return node.innerHTML}if(style!==""){node.innerHTML=style}else{head.removeChild(node)}}};utils.each(["String","Function","Array","Number","RegExp","Object"],function(v){UM.utils["is"+v]=function(obj){return Object.prototype.toString.apply(obj)=="[object "+v+"]"}});var EventBase=UM.EventBase=function(){};EventBase.prototype={addListener:function(types,listener){types=utils.trim(types).split(" ");for(var i=0,ti;ti=types[i++];){getListener(this,ti,true).push(listener)}},removeListener:function(types,listener){types=utils.trim(types).split(" ");for(var i=0,ti;ti=types[i++];){utils.removeItem(getListener(this,ti)||[],listener)}},fireEvent:function(){var types=arguments[0];types=utils.trim(types).split(" ");for(var i=0,ti;ti=types[i++];){var listeners=getListener(this,ti),r,t,k;if(listeners){k=listeners.length;while(k--){if(!listeners[k]){continue}t=listeners[k].apply(this,arguments);if(t===true){return t}if(t!==undefined){r=t}}}if(t=this["on"+ti.toLowerCase()]){r=t.apply(this,arguments)}}return r}};function getListener(obj,type,force){var allListeners;type=type.toLowerCase();return((allListeners=(obj.__allListeners||force&&(obj.__allListeners={})))&&(allListeners[type]||force&&(allListeners[type]=[])))}var dtd=dom.dtd=(function(){function _(s){for(var k in s){s[k.toUpperCase()]=s[k]}return s}var X=utils.extend2;var A=_({isindex:1,fieldset:1}),B=_({input:1,button:1,select:1,textarea:1,label:1}),C=X(_({a:1}),B),D=X({iframe:1},C),E=_({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),F=_({ins:1,del:1,script:1,style:1}),G=X(_({b:1,acronym:1,bdo:1,"var":1,"#":1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}),F),H=X(_({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}),G),I=X(_({p:1}),H),J=X(_({iframe:1}),H,B),K=_({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,"#":1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,"var":1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),L=X(_({a:0}),J),M=_({tr:1}),N=_({"#":1}),O=X(_({param:1}),K),P=X(_({form:1}),A,D,E,I),Q=_({li:1,ol:1,ul:1}),R=_({style:1,script:1}),S=_({base:1,link:1,meta:1,title:1}),T=X(S,R),U=_({head:1,body:1}),V=_({html:1});var block=_({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),empty=_({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1}); + return _({$nonBodyContent:X(V,U,S),$block:block,$inline:L,$inlineWithA:X(_({a:1}),L),$body:X(_({script:1,style:1}),block),$cdata:_({script:1,style:1}),$empty:empty,$nonChild:_({iframe:1,textarea:1}),$listItem:_({dd:1,dt:1,li:1}),$list:_({ul:1,ol:1,dl:1}),$isNotEmpty:_({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,video:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),$removeEmpty:_({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,"var":1}),$removeEmptyBlock:_({"p":1,"div":1}),$tableContent:_({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),$notTransContent:_({pre:1,script:1,style:1,textarea:1}),html:U,head:T,style:N,script:N,body:P,base:{},link:{},meta:{},title:N,col:{},tr:_({td:1,th:1}),img:{},embed:{},colgroup:_({thead:1,col:1,tbody:1,tr:1,tfoot:1}),noscript:P,td:P,br:{},th:P,center:P,kbd:L,button:X(I,E),basefont:{},h5:L,h4:L,samp:L,h6:L,ol:Q,h1:L,h3:L,option:N,h2:L,form:X(A,D,E,I),select:_({optgroup:1,option:1}),font:L,ins:L,menu:Q,abbr:L,label:L,table:_({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),code:L,tfoot:M,cite:L,li:P,input:{},iframe:P,strong:L,textarea:N,noframes:P,big:L,small:L,span:_({"#":1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),hr:L,dt:L,sub:L,optgroup:_({option:1}),param:{},bdo:L,"var":L,div:P,object:O,sup:L,dd:P,strike:L,area:{},dir:Q,map:X(_({area:1,form:1,p:1}),A,F,E),applet:O,dl:_({dt:1,dd:1}),del:L,isindex:{},fieldset:X(_({legend:1}),K),thead:M,ul:Q,acronym:L,b:L,a:X(_({a:1}),J),blockquote:X(_({td:1,tr:1,tbody:1,li:1}),P),caption:L,i:L,u:L,tbody:M,s:L,address:X(D,I),tt:L,legend:L,q:L,pre:X(G,C),p:X(_({"a":1}),L),em:L,dfn:L})})();function getDomNode(node,start,ltr,startFromChild,fn,guard){var tmpNode=startFromChild&&node[start],parent;!tmpNode&&(tmpNode=node[ltr]);while(!tmpNode&&(parent=(parent||node).parentNode)){if(parent.tagName=="BODY"||guard&&!guard(parent)){return null}tmpNode=parent[ltr]}if(tmpNode&&fn&&!fn(tmpNode)){return getDomNode(tmpNode,start,ltr,false,fn)}return tmpNode}var attrFix=ie&&browser.version<9?{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder"}:{tabindex:"tabIndex",readonly:"readOnly"},styleBlock=utils.listToMap(["-webkit-box","-moz-box","block","list-item","table","table-row-group","table-header-group","table-footer-group","table-row","table-column-group","table-column","table-cell","table-caption"]);var domUtils=dom.domUtils={NODE_ELEMENT:1,NODE_DOCUMENT:9,NODE_TEXT:3,NODE_COMMENT:8,NODE_DOCUMENT_FRAGMENT:11,POSITION_IDENTICAL:0,POSITION_DISCONNECTED:1,POSITION_FOLLOWING:2,POSITION_PRECEDING:4,POSITION_IS_CONTAINED:8,POSITION_CONTAINS:16,fillChar:ie&&browser.version=="6"?"\ufeff":"\u200B",keys:{8:1,46:1,16:1,17:1,18:1,37:1,38:1,39:1,40:1,13:1},breakParent:function(node,parent){var tmpNode,parentClone=node,clone=node,leftNodes,rightNodes;do{parentClone=parentClone.parentNode;if(leftNodes){tmpNode=parentClone.cloneNode(false);tmpNode.appendChild(leftNodes);leftNodes=tmpNode;tmpNode=parentClone.cloneNode(false);tmpNode.appendChild(rightNodes);rightNodes=tmpNode}else{leftNodes=parentClone.cloneNode(false);rightNodes=leftNodes.cloneNode(false)}while(tmpNode=clone.previousSibling){leftNodes.insertBefore(tmpNode,leftNodes.firstChild)}while(tmpNode=clone.nextSibling){rightNodes.appendChild(tmpNode)}clone=parentClone}while(parent!==parentClone);tmpNode=parent.parentNode;tmpNode.insertBefore(leftNodes,parent);tmpNode.insertBefore(rightNodes,parent);tmpNode.insertBefore(node,rightNodes);domUtils.remove(parent);return node},trimWhiteTextNode:function(node){function remove(dir){var child;while((child=node[dir])&&child.nodeType==3&&domUtils.isWhitespace(child)){node.removeChild(child)}}remove("firstChild");remove("lastChild")},getPosition:function(nodeA,nodeB){if(nodeA===nodeB){return 0}var node,parentsA=[nodeA],parentsB=[nodeB];node=nodeA;while(node=node.parentNode){if(node===nodeB){return 10}parentsA.push(node)}node=nodeB;while(node=node.parentNode){if(node===nodeA){return 20}parentsB.push(node)}parentsA.reverse();parentsB.reverse();if(parentsA[0]!==parentsB[0]){return 1}var i=-1;while(i++,parentsA[i]===parentsB[i]){}nodeA=parentsA[i];nodeB=parentsB[i];while(nodeA=nodeA.nextSibling){if(nodeA===nodeB){return 4}}return 2},getNodeIndex:function(node,ignoreTextNode){var preNode=node,i=0;while(preNode=preNode.previousSibling){if(ignoreTextNode&&preNode.nodeType==3){if(preNode.nodeType!=preNode.nextSibling.nodeType){i++}continue}i++}return i},inDoc:function(node,doc){return domUtils.getPosition(node,doc)==10},findParent:function(node,filterFn,includeSelf){if(node&&!domUtils.isBody(node)){node=includeSelf?node:node.parentNode;while(node){if(!filterFn||filterFn(node)||domUtils.isBody(node)){return filterFn&&!filterFn(node)&&domUtils.isBody(node)?null:node + }node=node.parentNode}}return null},findParentByTagName:function(node,tagNames,includeSelf,excludeFn){tagNames=utils.listToMap(utils.isArray(tagNames)?tagNames:[tagNames]);return domUtils.findParent(node,function(node){return tagNames[node.tagName]&&!(excludeFn&&excludeFn(node))},includeSelf)},findParents:function(node,includeSelf,filterFn,closerFirst){var parents=includeSelf&&(filterFn&&filterFn(node)||!filterFn)?[node]:[];while(node=domUtils.findParent(node,filterFn)){parents.push(node)}return closerFirst?parents:parents.reverse()},insertAfter:function(node,newNode){return node.parentNode.insertBefore(newNode,node.nextSibling)},remove:function(node,keepChildren){var parent=node.parentNode,child;if(parent){if(keepChildren&&node.hasChildNodes()){while(child=node.firstChild){parent.insertBefore(child,node)}}parent.removeChild(node)}return node},getNextDomNode:function(node,startFromChild,filterFn,guard){return getDomNode(node,"firstChild","nextSibling",startFromChild,filterFn,guard)},getPreDomNode:function(node,startFromChild,filterFn,guard){return getDomNode(node,"lastChild","previousSibling",startFromChild,filterFn,guard)},isBookmarkNode:function(node){return node.nodeType==1&&node.id&&/^_baidu_bookmark_/i.test(node.id)},getWindow:function(node){var doc=node.ownerDocument||node;return doc.defaultView||doc.parentWindow},getCommonAncestor:function(nodeA,nodeB){if(nodeA===nodeB){return nodeA}var parentsA=[nodeA],parentsB=[nodeB],parent=nodeA,i=-1;while(parent=parent.parentNode){if(parent===nodeB){return parent}parentsA.push(parent)}parent=nodeB;while(parent=parent.parentNode){if(parent===nodeA){return parent}parentsB.push(parent)}parentsA.reverse();parentsB.reverse();while(i++,parentsA[i]===parentsB[i]){}return i==0?null:parentsA[i-1]},clearEmptySibling:function(node,ignoreNext,ignorePre){function clear(next,dir){var tmpNode;while(next&&!domUtils.isBookmarkNode(next)&&(domUtils.isEmptyInlineElement(next)||!new RegExp("[^\t\n\r"+domUtils.fillChar+"]").test(next.nodeValue))){tmpNode=next[dir];domUtils.remove(next);next=tmpNode}}!ignoreNext&&clear(node.nextSibling,"nextSibling");!ignorePre&&clear(node.previousSibling,"previousSibling")},split:function(node,offset){var doc=node.ownerDocument;if(browser.ie&&offset==node.nodeValue.length){var next=doc.createTextNode("");return domUtils.insertAfter(node,next)}var retval=node.splitText(offset);if(browser.ie8){var tmpNode=doc.createTextNode("");domUtils.insertAfter(retval,tmpNode);domUtils.remove(tmpNode)}return retval},isWhitespace:function(node){return !new RegExp("[^ \t\n\r"+domUtils.fillChar+"]").test(node.nodeValue)},getXY:function(element){var x=0,y=0;while(element.offsetParent){y+=element.offsetTop;x+=element.offsetLeft;element=element.offsetParent}return{"x":x,"y":y}},isEmptyInlineElement:function(node){if(node.nodeType!=1||!dtd.$removeEmpty[node.tagName]){return 0}node=node.firstChild;while(node){if(domUtils.isBookmarkNode(node)){return 0}if(node.nodeType==1&&!domUtils.isEmptyInlineElement(node)||node.nodeType==3&&!domUtils.isWhitespace(node)){return 0}node=node.nextSibling}return 1},isBlockElm:function(node){return node.nodeType==1&&(dtd.$block[node.tagName]||styleBlock[domUtils.getComputedStyle(node,"display")])&&!dtd.$nonChild[node.tagName]},getElementsByTagName:function(node,name,filter){if(filter&&utils.isString(filter)){var className=filter;filter=function(node){var result=false;$.each(utils.trim(className).replace(/[ ]{2,}/g," ").split(" "),function(i,v){if($(node).hasClass(v)){result=true;return false}});return result}}name=utils.trim(name).replace(/[ ]{2,}/g," ").split(" ");var arr=[];for(var n=0,ni;ni=name[n++];){var list=node.getElementsByTagName(ni);for(var i=0,ci;ci=list[i++];){if(!filter||filter(ci)){arr.push(ci)}}}return arr},unSelectable:ie&&browser.ie9below||browser.opera?function(node){node.onselectstart=function(){return false};node.onclick=node.onkeyup=node.onkeydown=function(){return false};node.unselectable="on";node.setAttribute("unselectable","on");for(var i=0,ci;ci=node.all[i++];){switch(ci.tagName.toLowerCase()){case"iframe":case"textarea":case"input":case"select":break;default:ci.unselectable="on";node.setAttribute("unselectable","on")}}}:function(node){node.style.MozUserSelect=node.style.webkitUserSelect=node.style.msUserSelect=node.style.KhtmlUserSelect="none"},removeAttributes:function(node,attrNames){attrNames=utils.isArray(attrNames)?attrNames:utils.trim(attrNames).replace(/[ ]{2,}/g," ").split(" ");for(var i=0,ci;ci=attrNames[i++];){ci=attrFix[ci]||ci;switch(ci){case"className":node[ci]="";break;case"style":node.style.cssText="";!browser.ie&&node.removeAttributeNode(node.getAttributeNode("style"))}node.removeAttribute(ci)}},createElement:function(doc,tag,attrs){return domUtils.setAttributes(doc.createElement(tag),attrs)},setAttributes:function(node,attrs){for(var attr in attrs){if(attrs.hasOwnProperty(attr)){var value=attrs[attr];switch(attr){case"class":node.className=value;break;case"style":node.style.cssText=node.style.cssText+";"+value; + break;case"innerHTML":node[attr]=value;break;case"value":node.value=value;break;default:node.setAttribute(attrFix[attr]||attr,value)}}}return node},getComputedStyle:function(element,styleName){return utils.transUnitToPx(utils.fixColor(styleName,$(element).css(styleName)))},preventDefault:function(evt){evt.preventDefault?evt.preventDefault():(evt.returnValue=false)},removeStyle:function(element,name){if(browser.ie){if(name=="color"){name="(^|;)"+name}element.style.cssText=element.style.cssText.replace(new RegExp(name+"[^:]*:[^;]+;?","ig"),"")}else{if(element.style.removeProperty){element.style.removeProperty(name)}else{element.style.removeAttribute(utils.cssStyleToDomStyle(name))}}if(!element.style.cssText){domUtils.removeAttributes(element,["style"])}},getStyle:function(element,name){var value=element.style[utils.cssStyleToDomStyle(name)];return utils.fixColor(name,value)},setStyle:function(element,name,value){element.style[utils.cssStyleToDomStyle(name)]=value;if(!utils.trim(element.style.cssText)){this.removeAttributes(element,"style")}},removeDirtyAttr:function(node){for(var i=0,ci,nodes=node.getElementsByTagName("*");ci=nodes[i++];){ci.removeAttribute("_moz_dirty")}node.removeAttribute("_moz_dirty")},getChildCount:function(node,fn){var count=0,first=node.firstChild;fn=fn||function(){return 1};while(first){if(fn(first)){count++}first=first.nextSibling}return count},isEmptyNode:function(node){return !node.firstChild||domUtils.getChildCount(node,function(node){return !domUtils.isBr(node)&&!domUtils.isBookmarkNode(node)&&!domUtils.isWhitespace(node)})==0},isBr:function(node){return node.nodeType==1&&node.tagName=="BR"},isFillChar:function(node,isInStart){return node.nodeType==3&&!node.nodeValue.replace(new RegExp((isInStart?"^":"")+domUtils.fillChar),"").length},isEmptyBlock:function(node,reg){if(!node||node.nodeType!=1){return 0}reg=reg||new RegExp("[ \t\r\n"+domUtils.fillChar+"]","g");if(node[browser.ie?"innerText":"textContent"].replace(reg,"").length>0){return 0}for(var n in dtd.$isNotEmpty){if(node.getElementsByTagName(n).length){return 0}}return 1},isCustomeNode:function(node){return node.nodeType==1&&node.getAttribute("_ue_custom_node_")},fillNode:function(doc,node){var tmpNode=browser.ie?doc.createTextNode(domUtils.fillChar):doc.createElement("br");node.innerHTML="";node.appendChild(tmpNode)},isBoundaryNode:function(node,dir){var tmp;while(!domUtils.isBody(node)){tmp=node;node=node.parentNode;if(tmp!==node[dir]){return false}}return true},isFillChar:function(node,isInStart){return node.nodeType==3&&!node.nodeValue.replace(new RegExp((isInStart?"^":"")+domUtils.fillChar),"").length},isBody:function(node){return $(node).hasClass("edui-body-container")}};var fillCharReg=new RegExp(domUtils.fillChar,"g");(function(){var guid=0,fillChar=domUtils.fillChar,fillData;function updateCollapse(range){range.collapsed=range.startContainer&&range.endContainer&&range.startContainer===range.endContainer&&range.startOffset==range.endOffset}function selectOneNode(rng){return !rng.collapsed&&rng.startContainer.nodeType==1&&rng.startContainer===rng.endContainer&&rng.endOffset-rng.startOffset==1}function setEndPoint(toStart,node,offset,range){if(node.nodeType==1&&(dtd.$empty[node.tagName]||dtd.$nonChild[node.tagName])){offset=domUtils.getNodeIndex(node)+(toStart?0:1);node=node.parentNode}if(toStart){range.startContainer=node;range.startOffset=offset;if(!range.endContainer){range.collapse(true)}}else{range.endContainer=node;range.endOffset=offset;if(!range.startContainer){range.collapse(false)}}updateCollapse(range);return range}var Range=dom.Range=function(document,body){var me=this;me.startContainer=me.startOffset=me.endContainer=me.endOffset=null;me.document=document;me.collapsed=true;me.body=body};function removeFillData(doc,excludeNode){try{if(fillData&&domUtils.inDoc(fillData,doc)){if(!fillData.nodeValue.replace(fillCharReg,"").length){var tmpNode=fillData.parentNode;domUtils.remove(fillData);while(tmpNode&&domUtils.isEmptyInlineElement(tmpNode)&&(browser.safari?!(domUtils.getPosition(tmpNode,excludeNode)&domUtils.POSITION_CONTAINS):!tmpNode.contains(excludeNode))){fillData=tmpNode.parentNode;domUtils.remove(tmpNode);tmpNode=fillData}}else{fillData.nodeValue=fillData.nodeValue.replace(fillCharReg,"")}}}catch(e){}}function mergeSibling(node,dir){var tmpNode;node=node[dir];while(node&&domUtils.isFillChar(node)){tmpNode=node[dir];domUtils.remove(node);node=tmpNode}}function execContentsAction(range,action){var start=range.startContainer,end=range.endContainer,startOffset=range.startOffset,endOffset=range.endOffset,doc=range.document,frag=doc.createDocumentFragment(),tmpStart,tmpEnd;if(start.nodeType==1){start=start.childNodes[startOffset]||(tmpStart=start.appendChild(doc.createTextNode("")))}if(end.nodeType==1){end=end.childNodes[endOffset]||(tmpEnd=end.appendChild(doc.createTextNode("")))}if(start===end&&start.nodeType==3){frag.appendChild(doc.createTextNode(start.substringData(startOffset,endOffset-startOffset)));if(action){start.deleteData(startOffset,endOffset-startOffset); + range.collapse(true)}return frag}var current,currentLevel,clone=frag,startParents=domUtils.findParents(start,true),endParents=domUtils.findParents(end,true);for(var i=0;startParents[i]==endParents[i];){i++}for(var j=i,si;si=startParents[j];j++){current=si.nextSibling;if(si==start){if(!tmpStart){if(range.startContainer.nodeType==3){clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));if(action){start.deleteData(startOffset,start.nodeValue.length-startOffset)}}else{clone.appendChild(!action?start.cloneNode(true):start)}}}else{currentLevel=si.cloneNode(false);clone.appendChild(currentLevel)}while(current){if(current===end||current===endParents[j]){break}si=current.nextSibling;clone.appendChild(!action?current.cloneNode(true):current);current=si}clone=currentLevel}clone=frag;if(!startParents[i]){clone.appendChild(startParents[i-1].cloneNode(false));clone=clone.firstChild}for(var j=i,ei;ei=endParents[j];j++){current=ei.previousSibling;if(ei==end){if(!tmpEnd&&range.endContainer.nodeType==3){clone.appendChild(doc.createTextNode(end.substringData(0,endOffset)));if(action){end.deleteData(0,endOffset)}}}else{currentLevel=ei.cloneNode(false);clone.appendChild(currentLevel)}if(j!=i||!startParents[i]){while(current){if(current===start){break}ei=current.previousSibling;clone.insertBefore(!action?current.cloneNode(true):current,clone.firstChild);current=ei}}clone=currentLevel}if(action){range.setStartBefore(!endParents[i]?endParents[i-1]:!startParents[i]?startParents[i-1]:endParents[i]).collapse(true)}tmpStart&&domUtils.remove(tmpStart);tmpEnd&&domUtils.remove(tmpEnd);return frag}Range.prototype={deleteContents:function(){var txt;if(!this.collapsed){execContentsAction(this,1)}if(browser.webkit){txt=this.startContainer;if(txt.nodeType==3&&!txt.nodeValue.length){this.setStartBefore(txt).collapse(true);domUtils.remove(txt)}}return this},inFillChar:function(){var start=this.startContainer;if(this.collapsed&&start.nodeType==3&&start.nodeValue.replace(new RegExp("^"+domUtils.fillChar),"").length+1==start.nodeValue.length){return true}return false},setStart:function(node,offset){return setEndPoint(true,node,offset,this)},setEnd:function(node,offset){return setEndPoint(false,node,offset,this)},setStartAfter:function(node){return this.setStart(node.parentNode,domUtils.getNodeIndex(node)+1)},setStartBefore:function(node){return this.setStart(node.parentNode,domUtils.getNodeIndex(node))},setEndAfter:function(node){return this.setEnd(node.parentNode,domUtils.getNodeIndex(node)+1)},setEndBefore:function(node){return this.setEnd(node.parentNode,domUtils.getNodeIndex(node))},setStartAtFirst:function(node){return this.setStart(node,0)},setStartAtLast:function(node){return this.setStart(node,node.nodeType==3?node.nodeValue.length:node.childNodes.length)},setEndAtFirst:function(node){return this.setEnd(node,0)},setEndAtLast:function(node){return this.setEnd(node,node.nodeType==3?node.nodeValue.length:node.childNodes.length)},selectNode:function(node){return this.setStartBefore(node).setEndAfter(node)},selectNodeContents:function(node){return this.setStart(node,0).setEndAtLast(node)},cloneRange:function(){var me=this;return new Range(me.document).setStart(me.startContainer,me.startOffset).setEnd(me.endContainer,me.endOffset)},collapse:function(toStart){var me=this;if(toStart){me.endContainer=me.startContainer;me.endOffset=me.startOffset}else{me.startContainer=me.endContainer;me.startOffset=me.endOffset}me.collapsed=true;return me},shrinkBoundary:function(ignoreEnd){var me=this,child,collapsed=me.collapsed;function check(node){return node.nodeType==1&&!domUtils.isBookmarkNode(node)&&!dtd.$empty[node.tagName]&&!dtd.$nonChild[node.tagName]}while(me.startContainer.nodeType==1&&(child=me.startContainer.childNodes[me.startOffset])&&check(child)){me.setStart(child,0)}if(collapsed){return me.collapse(true)}if(!ignoreEnd){while(me.endContainer.nodeType==1&&me.endOffset>0&&(child=me.endContainer.childNodes[me.endOffset-1])&&check(child)){me.setEnd(child,child.childNodes.length)}}return me},trimBoundary:function(ignoreEnd){this.txtToElmBoundary();var start=this.startContainer,offset=this.startOffset,collapsed=this.collapsed,end=this.endContainer;if(start.nodeType==3){if(offset==0){this.setStartBefore(start)}else{if(offset>=start.nodeValue.length){this.setStartAfter(start)}else{var textNode=domUtils.split(start,offset);if(start===end){this.setEnd(textNode,this.endOffset-offset)}else{if(start.parentNode===end){this.endOffset+=1}}this.setStartBefore(textNode)}}if(collapsed){return this.collapse(true)}}if(!ignoreEnd){offset=this.endOffset;end=this.endContainer;if(end.nodeType==3){if(offset==0){this.setEndBefore(end)}else{offset=container.nodeValue.length){r["set"+c.replace(/(\w)/,function(a){return a.toUpperCase()})+"After"](container)}}}}if(ignoreCollapsed||!this.collapsed){adjust(this,"start");adjust(this,"end")}return this},insertNode:function(node){var first=node,length=1;if(node.nodeType==11){first=node.firstChild;length=node.childNodes.length}this.trimBoundary(true);var start=this.startContainer,offset=this.startOffset;var nextNode=start.childNodes[offset];if(nextNode){start.insertBefore(node,nextNode)}else{start.appendChild(node)}if(first.parentNode===this.endContainer){this.endOffset=this.endOffset+length}return this.setStartBefore(first)},setCursor:function(toEnd,noFillData){return this.collapse(!toEnd).select(noFillData)},createBookmark:function(serialize,same){var endNode,startNode=this.document.createElement("span");startNode.style.cssText="display:none;line-height:0px;";startNode.appendChild(this.document.createTextNode("\u200D"));startNode.id="_baidu_bookmark_start_"+(same?"":guid++);if(!this.collapsed){endNode=startNode.cloneNode(true);endNode.id="_baidu_bookmark_end_"+(same?"":guid++)}this.insertNode(startNode);if(endNode){this.collapse().insertNode(endNode).setEndBefore(endNode)}this.setStartAfter(startNode);return{start:serialize?startNode.id:startNode,end:endNode?serialize?endNode.id:endNode:null,id:serialize}},moveToBookmark:function(bookmark){var start=bookmark.id?this.document.getElementById(bookmark.start):bookmark.start,end=bookmark.end&&bookmark.id?this.document.getElementById(bookmark.end):bookmark.end;this.setStartBefore(start);domUtils.remove(start);if(end){this.setEndBefore(end);domUtils.remove(end)}else{this.collapse(true)}return this},adjustmentBoundary:function(){if(!this.collapsed){while(!domUtils.isBody(this.startContainer)&&this.startOffset==this.startContainer[this.startContainer.nodeType==3?"nodeValue":"childNodes"].length&&this.startContainer[this.startContainer.nodeType==3?"nodeValue":"childNodes"].length){this.setStartAfter(this.startContainer)}while(!domUtils.isBody(this.endContainer)&&!this.endOffset&&this.endContainer[this.endContainer.nodeType==3?"nodeValue":"childNodes"].length){this.setEndBefore(this.endContainer)}}return this},getClosedNode:function(){var node;if(!this.collapsed){var range=this.cloneRange().adjustmentBoundary().shrinkBoundary();if(selectOneNode(range)){var child=range.startContainer.childNodes[range.startOffset];if(child&&child.nodeType==1&&(dtd.$empty[child.tagName]||dtd.$nonChild[child.tagName])){node=child}}}return node},select:browser.ie?function(noFillData,textRange){var nativeRange;if(!this.collapsed){this.shrinkBoundary()}var node=this.getClosedNode();if(node&&!textRange){try{nativeRange=this.document.body.createControlRange();nativeRange.addElement(node);nativeRange.select()}catch(e){}return this}var bookmark=this.createBookmark(),start=bookmark.start,end;nativeRange=this.document.body.createTextRange();nativeRange.moveToElementText(start);nativeRange.moveStart("character",1);if(!this.collapsed){var nativeRangeEnd=this.document.body.createTextRange();end=bookmark.end;nativeRangeEnd.moveToElementText(end);nativeRange.setEndPoint("EndToEnd",nativeRangeEnd)}else{if(!noFillData&&this.startContainer.nodeType!=3){var tmpText=this.document.createTextNode(fillChar),tmp=this.document.createElement("span");tmp.appendChild(this.document.createTextNode(fillChar));start.parentNode.insertBefore(tmp,start);start.parentNode.insertBefore(tmpText,start);removeFillData(this.document,tmpText);fillData=tmpText;mergeSibling(tmp,"previousSibling");mergeSibling(start,"nextSibling");nativeRange.moveStart("character",-1);nativeRange.collapse(true)}}this.moveToBookmark(bookmark);tmp&&domUtils.remove(tmp);try{nativeRange.select()}catch(e){}return this}:function(notInsertFillData){function checkOffset(rng){function check(node,offset,dir){if(node.nodeType==3&&node.nodeValue.length ');this.cloneRange().insertNode($span.get(0));var winScrollTop=$(window).scrollTop(),winHeight=$(window).height(),spanTop=$span.offset().top;if(spanTopwinScrollTop+winHeight){if(spanTop>winScrollTop+winHeight){window.scrollTo(0,spanTop-winHeight+$span.height())}else{window.scrollTo(0,winScrollTop-spanTop)}}$span.remove()},getOffset:function(){var bk=this.createBookmark();var offset=$(bk.start).css("display","inline-block").offset();this.moveToBookmark(bk);return offset}}})();(function(){function getBoundaryInformation(range,start){var getIndex=domUtils.getNodeIndex;range=range.duplicate();range.collapse(start);var parent=range.parentElement();if(!parent.hasChildNodes()){return{container:parent,offset:0}}var siblings=parent.children,child,testRange=range.duplicate(),startIndex=0,endIndex=siblings.length-1,index=-1,distance;while(startIndex<=endIndex){index=Math.floor((startIndex+endIndex)/2);child=siblings[index];testRange.moveToElementText(child);var position=testRange.compareEndPoints("StartToStart",range);if(position>0){endIndex=index-1}else{if(position<0){startIndex=index+1}else{return{container:parent,offset:getIndex(child)}}}}if(index==-1){testRange.moveToElementText(parent);testRange.setEndPoint("StartToStart",range);distance=testRange.text.replace(/(\r\n|\r)/g,"\n").length;siblings=parent.childNodes;if(!distance){child=siblings[siblings.length-1];return{container:child,offset:child.nodeValue.length}}var i=siblings.length;while(distance>0){distance-=siblings[--i].nodeValue.length}return{container:siblings[i],offset:-distance}}testRange.collapse(position>0);testRange.setEndPoint(position>0?"StartToStart":"EndToStart",range);distance=testRange.text.replace(/(\r\n|\r)/g,"\n").length;if(!distance){return dtd.$empty[child.tagName]||dtd.$nonChild[child.tagName]?{container:parent,offset:getIndex(child)+(position>0?0:1)}:{container:child,offset:position>0?0:child.childNodes.length}}while(distance>0){try{var pre=child;child=child[position>0?"previousSibling":"nextSibling"];distance-=child.nodeValue.length}catch(e){return{container:parent,offset:getIndex(pre)}}}return{container:child,offset:position>0?-distance:child.nodeValue.length+distance}}function transformIERangeToRange(ieRange,range){if(ieRange.item){range.selectNode(ieRange.item(0))}else{var bi=getBoundaryInformation(ieRange,true);range.setStart(bi.container,bi.offset);if(ieRange.compareEndPoints("StartToEnd",ieRange)!=0){bi=getBoundaryInformation(ieRange,false);range.setEnd(bi.container,bi.offset)}}return range}function _getIERange(sel,txtRange){var ieRange;try{ieRange=sel.getNative(txtRange).createRange() +}catch(e){return null}var el=ieRange.item?ieRange.item(0):ieRange.parentElement();if((el.ownerDocument||el)===sel.document){return ieRange}return null}var Selection=dom.Selection=function(doc,body){var me=this;me.document=doc;me.body=body;if(browser.ie9below){$(body).on("beforedeactivate",function(){me._bakIERange=me.getIERange()}).on("activate",function(){try{var ieNativRng=_getIERange(me);if((!ieNativRng||!me.rangeInBody(ieNativRng))&&me._bakIERange){me._bakIERange.select()}}catch(ex){}me._bakIERange=null})}};Selection.prototype={hasNativeRange:function(){var rng;if(!browser.ie||browser.ie9above){var nativeSel=this.getNative();if(!nativeSel.rangeCount){return false}rng=nativeSel.getRangeAt(0)}else{rng=_getIERange(this)}return this.rangeInBody(rng)},getNative:function(txtRange){var doc=this.document;try{return !doc?null:browser.ie9below||txtRange?doc.selection:domUtils.getWindow(doc).getSelection()}catch(e){return null}},getIERange:function(txtRange){var ieRange=_getIERange(this,txtRange);if(!ieRange||!this.rangeInBody(ieRange,txtRange)){if(this._bakIERange){return this._bakIERange}}return ieRange},rangeInBody:function(rng,txtRange){var node=browser.ie9below||txtRange?rng.item?rng.item():rng.parentElement():rng.startContainer;return node===this.body||domUtils.inDoc(node,this.body)},cache:function(){this.clear();this._cachedRange=this.getRange();this._cachedStartElement=this.getStart();this._cachedStartElementPath=this.getStartElementPath()},getStartElementPath:function(){if(this._cachedStartElementPath){return this._cachedStartElementPath}var start=this.getStart();if(start){return domUtils.findParents(start,true,null,true)}return[]},clear:function(){this._cachedStartElementPath=this._cachedRange=this._cachedStartElement=null},isFocus:function(){return this.hasNativeRange()},getRange:function(){var me=this;function optimze(range){var child=me.body.firstChild,collapsed=range.collapsed;while(child&&child.firstChild){range.setStart(child,0);child=child.firstChild}if(!range.startContainer){range.setStart(me.body,0)}if(collapsed){range.collapse(true)}}if(me._cachedRange!=null){return this._cachedRange}var range=new dom.Range(me.document,me.body);if(browser.ie9below){var nativeRange=me.getIERange();if(nativeRange&&this.rangeInBody(nativeRange)){try{transformIERangeToRange(nativeRange,range)}catch(e){optimze(range)}}else{optimze(range)}}else{var sel=me.getNative();if(sel&&sel.rangeCount&&me.rangeInBody(sel.getRangeAt(0))){var firstRange=sel.getRangeAt(0);var lastRange=sel.getRangeAt(sel.rangeCount-1);range.setStart(firstRange.startContainer,firstRange.startOffset).setEnd(lastRange.endContainer,lastRange.endOffset);if(range.collapsed&&domUtils.isBody(range.startContainer)&&!range.startOffset){optimze(range)}}else{if(this._bakRange&&(this._bakRange.startContainer===this.body||domUtils.inDoc(this._bakRange.startContainer,this.body))){return this._bakRange}optimze(range)}}return this._bakRange=range},getStart:function(){if(this._cachedStartElement){return this._cachedStartElement}var range=browser.ie9below?this.getIERange():this.getRange(),tmpRange,start,tmp,parent;if(browser.ie9below){if(!range){return this.document.body.firstChild}if(range.item){return range.item(0)}tmpRange=range.duplicate();tmpRange.text.length>0&&tmpRange.moveStart("character",1);tmpRange.collapse(1);start=tmpRange.parentElement();parent=tmp=range.parentElement();while(tmp=tmp.parentNode){if(tmp==start){start=parent;break}}}else{start=range.startContainer;if(start.nodeType==1&&start.hasChildNodes()){start=start.childNodes[Math.min(start.childNodes.length-1,range.startOffset)]}if(start.nodeType==3){return start.parentNode}}return start},getText:function(){var nativeSel,nativeRange;if(this.isFocus()&&(nativeSel=this.getNative())){nativeRange=browser.ie9below?nativeSel.createRange():nativeSel.getRangeAt(0);return browser.ie9below?nativeRange.text:nativeRange.toString()}return""}}})();(function(){var uid=0,_selectionChangeTimer;function setValue(form,editor){var textarea;if(editor.textarea){if(utils.isString(editor.textarea)){for(var i=0,ti,tis=domUtils.getElementsByTagName(form,"textarea");ti=tis[i++];){if(ti.id=="umeditor_textarea_"+editor.options.textarea){textarea=ti;break}}}else{textarea=editor.textarea}}if(!textarea){form.appendChild(textarea=domUtils.createElement(document,"textarea",{"name":editor.options.textarea,"id":"umeditor_textarea_"+editor.options.textarea,"style":"display:none"}));editor.textarea=textarea}textarea.value=editor.hasContents()?(editor.options.allHtmlEnabled?editor.getAllHtml():editor.getContent(null,null,true)):""}function loadPlugins(me){for(var pi in UM.plugins){if(me.options.excludePlugins.indexOf(pi)==-1){UM.plugins[pi].call(me);me.plugins[pi]=1}}me.langIsReady=true;me.fireEvent("langReady")}function checkCurLang(I18N){for(var lang in I18N){return lang}}var Editor=UM.Editor=function(options){var me=this;me.uid=uid++;EventBase.call(me);me.commands={};me.options=utils.extend(utils.clone(options||{}),UMEDITOR_CONFIG,true); + me.shortcutkeys={};me.inputRules=[];me.outputRules=[];me.setOpt({isShow:true,initialContent:"",initialStyle:"",autoClearinitialContent:false,textarea:"editorValue",focus:false,focusInEnd:true,autoClearEmptyNode:true,fullscreen:false,readonly:false,zIndex:999,enterTag:"p",lang:"zh-cn",langPath:me.options.UMEDITOR_HOME_URL+"lang/",theme:"default",themePath:me.options.UMEDITOR_HOME_URL+"themes/",allHtmlEnabled:false,autoSyncData:true,autoHeightEnabled:true,excludePlugins:""});me.plugins={};if(!utils.isEmptyObject(UM.I18N)){me.options.lang=checkCurLang(UM.I18N);loadPlugins(me)}else{utils.loadFile(document,{src:me.options.langPath+me.options.lang+"/"+me.options.lang+".js",tag:"script",type:"text/javascript",defer:"defer"},function(){loadPlugins(me)})}};Editor.prototype={ready:function(fn){var me=this;if(fn){me.isReady?fn.apply(me):me.addListener("ready",fn)}},setOpt:function(key,val){var obj={};if(utils.isString(key)){obj[key]=val}else{obj=key}utils.extend(this.options,obj,true)},getOpt:function(key){return this.options[key]||""},destroy:function(){var me=this;me.fireEvent("destroy");var container=me.container;if(container===document.body){container=me.container}var textarea=me.textarea;if(!textarea){textarea=document.createElement("textarea");container.parentNode.insertBefore(textarea,container)}else{textarea.style.display=""}textarea.style.width=me.body.offsetWidth+"px";textarea.style.height=me.body.offsetHeight+"px";textarea.value=me.getContent();textarea.id=me.key;if(container.contains(textarea)){$(textarea).insertBefore(container)}container.innerHTML="";domUtils.remove(container);UM.clearCache(me.id);for(var p in me){if(me.hasOwnProperty(p)){delete this[p]}}},initialCont:function(holder){if(holder){holder.getAttribute("name")&&(this.options.textarea=holder.getAttribute("name"));if(holder&&/script|textarea/ig.test(holder.tagName)){var newDiv=document.createElement("div");holder.parentNode.insertBefore(newDiv,holder);this.options.initialContent=UM.htmlparser(holder.value||holder.innerHTML||this.options.initialContent).toHtml();holder.className&&(newDiv.className=holder.className);holder.style.cssText&&(newDiv.style.cssText=holder.style.cssText);if(/textarea/i.test(holder.tagName)){this.textarea=holder;this.textarea.style.display="none"}else{holder.parentNode.removeChild(holder);holder.id&&(newDiv.id=holder.id)}holder=newDiv;holder.innerHTML=""}return holder}else{return null}},render:function(container){var me=this,options=me.options,getStyleValue=function(attr){return parseInt($(container).css(attr))};if(utils.isString(container)){container=document.getElementById(container)}if(container){this.id=container.getAttribute("id");UM.setEditor(this);utils.cssRule("edui-style-body",me.options.initialStyle,document);container=this.initialCont(container);container.className+=" edui-body-container";if(options.initialFrameWidth){options.minFrameWidth=options.initialFrameWidth}else{options.minFrameWidth=options.initialFrameWidth=$(container).width()||UM.defaultWidth}if(options.initialFrameHeight){options.minFrameHeight=options.initialFrameHeight}else{options.initialFrameHeight=options.minFrameHeight=$(container).height()||UM.defaultHeight}container.style.width=/%$/.test(options.initialFrameWidth)?"100%":options.initialFrameWidth-getStyleValue("padding-left")-getStyleValue("padding-right")+"px";var height=/%$/.test(options.initialFrameHeight)?"100%":(options.initialFrameHeight-getStyleValue("padding-top")-getStyleValue("padding-bottom"));if(this.options.autoHeightEnabled){container.style.minHeight=height+"px";container.style.height="";if(browser.ie&&browser.version<=6){container.style.height=height;container.style.setExpression("height","this.scrollHeight <= "+height+' ? "'+height+'px" : "auto"')}}else{$(container).height(height)}container.style.zIndex=options.zIndex;this._setup(container)}},_setup:function(cont){var me=this,options=me.options;cont.contentEditable=true;document.body.spellcheck=false;me.document=document;me.window=document.defaultView||document.parentWindow;me.body=cont;me.$body=$(cont);me.selection=new dom.Selection(document,me.body);me._isEnabled=false;var geckoSel;if(browser.gecko&&(geckoSel=this.selection.getNative())){geckoSel.removeAllRanges()}this._initEvents();for(var form=cont.parentNode;form&&!domUtils.isBody(form);form=form.parentNode){if(form.tagName=="FORM"){me.form=form;if(me.options.autoSyncData){$(cont).on("blur",function(){setValue(form,me)})}else{$(form).on("submit",function(){setValue(this,me)})}break}}if(options.initialContent){if(options.autoClearinitialContent){var oldExecCommand=me.execCommand;me.execCommand=function(){me.fireEvent("firstBeforeExecCommand");return oldExecCommand.apply(me,arguments)};this._setDefaultContent(options.initialContent)}else{this.setContent(options.initialContent,false,true)}}if(domUtils.isEmptyNode(me.body)){me.body.innerHTML="

          "+(browser.ie?"":"
          ")+"

          "}if(options.focus){setTimeout(function(){me.focus(me.options.focusInEnd);!me.options.autoClearinitialContent&&me._selectionChange() + },0)}if(!me.container){me.container=cont.parentNode}me._bindshortcutKeys();me.isReady=1;me.fireEvent("ready");options.onready&&options.onready.call(me);if(!browser.ie||browser.ie9above){$(me.body).on("blur focus",function(e){var nSel=me.selection.getNative();if(e.type=="blur"){if(nSel.rangeCount>0){me._bakRange=nSel.getRangeAt(0)}}else{try{me._bakRange&&nSel.addRange(me._bakRange)}catch(e){}me._bakRange=null}})}!options.isShow&&me.setHide();options.readonly&&me.setDisabled()},sync:function(formId){var me=this,form=formId?document.getElementById(formId):domUtils.findParent(me.body.parentNode,function(node){return node.tagName=="FORM"},true);form&&setValue(form,me)},setHeight:function(height,notSetHeight){!notSetHeight&&(this.options.initialFrameHeight=height);if(this.options.autoHeightEnabled){$(this.body).css({"min-height":height+"px"});if(browser.ie&&browser.version<=6&&this.container){this.container.style.height=height;this.container.style.setExpression("height","this.scrollHeight <= "+height+' ? "'+height+'px" : "auto"')}}else{$(this.body).height(height)}this.fireEvent("resize")},setWidth:function(width){this.$container&&this.$container.width(width);$(this.body).width(width-$(this.body).css("padding-left").replace("px","")*1-$(this.body).css("padding-right").replace("px","")*1);this.fireEvent("resize")},addshortcutkey:function(cmd,keys){var obj={};if(keys){obj[cmd]=keys}else{obj=cmd}utils.extend(this.shortcutkeys,obj)},_bindshortcutKeys:function(){var me=this,shortcutkeys=this.shortcutkeys;me.addListener("keydown",function(type,e){var keyCode=e.keyCode||e.which;for(var i in shortcutkeys){var tmp=shortcutkeys[i].split(",");for(var t=0,ti;ti=tmp[t++];){ti=ti.split(":");var key=ti[0],param=ti[1];if(/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase())||/^(\d+)$/.test(key)){if(((RegExp.$1=="ctrl"?(e.ctrlKey||e.metaKey):0)&&(RegExp.$2!=""?e[RegExp.$2.slice(1)+"Key"]:1)&&keyCode==RegExp.$3)||keyCode==RegExp.$1){if(me.queryCommandState(i,param)!=-1){me.execCommand(i,param)}domUtils.preventDefault(e)}}}}})},getContent:function(cmd,fn,notSetCursor,ignoreBlank,formatter){var me=this;if(cmd&&utils.isFunction(cmd)){fn=cmd;cmd=""}if(fn?!fn():!this.hasContents()){return""}me.fireEvent("beforegetcontent");var root=UM.htmlparser(me.body.innerHTML,ignoreBlank);me.filterOutputRule(root);me.fireEvent("aftergetcontent",root);return root.toHtml(formatter)},getAllHtml:function(){var me=this,headHtml=[],html="";me.fireEvent("getAllHtml",headHtml);if(browser.ie&&browser.version>8){var headHtmlForIE9="";utils.each(me.document.styleSheets,function(si){headHtmlForIE9+=(si.href?'':"")});utils.each(me.document.getElementsByTagName("script"),function(si){headHtmlForIE9+=si.outerHTML})}return""+(me.options.charset?'':"")+(headHtmlForIE9||me.document.getElementsByTagName("head")[0].innerHTML)+headHtml.join("\n")+""+""+me.getContent(null,null,true)+""},getPlainTxt:function(){var reg=new RegExp(domUtils.fillChar,"g"),html=this.body.innerHTML.replace(/[\n\r]/g,"");html=html.replace(/<(p|div)[^>]*>(| )<\/\1>/gi,"\n").replace(//gi,"\n").replace(/<[^>/]+>/g,"").replace(/(\n)?<\/([^>]+)>/g,function(a,b,c){return dtd.$block[c]?"\n":b?b:""});return html.replace(reg,"").replace(/\u00a0/g," ").replace(/ /g," ")},getContentTxt:function(){var reg=new RegExp(domUtils.fillChar,"g");return this.body[browser.ie?"innerText":"textContent"].replace(reg,"").replace(/\u00a0/g," ")},setContent:function(html,isAppendTo,notFireSelectionchange){var me=this;me.fireEvent("beforesetcontent",html);var root=UM.htmlparser(html);me.filterInputRule(root);html=root.toHtml();me.body.innerHTML=(isAppendTo?me.body.innerHTML:"")+html;function isCdataDiv(node){return node.tagName=="DIV"&&node.getAttribute("cdata_tag")}if(me.options.enterTag=="p"){var child=this.body.firstChild,tmpNode;if(!child||child.nodeType==1&&(dtd.$cdata[child.tagName]||isCdataDiv(child)||domUtils.isCustomeNode(child))&&child===this.body.lastChild){this.body.innerHTML="

          "+(browser.ie?" ":"
          ")+"

          "+this.body.innerHTML}else{var p=me.document.createElement("p");while(child){while(child&&(child.nodeType==3||child.nodeType==1&&dtd.p[child.tagName]&&!dtd.$cdata[child.tagName])){tmpNode=child.nextSibling;p.appendChild(child);child=tmpNode}if(p.firstChild){if(!child){me.body.appendChild(p);break}else{child.parentNode.insertBefore(p,child);p=me.document.createElement("p")}}child=child.nextSibling}}}me.fireEvent("aftersetcontent");me.fireEvent("contentchange");!notFireSelectionchange&&me._selectionChange();me._bakRange=me._bakIERange=me._bakNativeRange=null;var geckoSel;if(browser.gecko&&(geckoSel=this.selection.getNative())){geckoSel.removeAllRanges()}if(me.options.autoSyncData){me.form&&setValue(me.form,me)}},focus:function(toEnd){try{var me=this,rng=me.selection.getRange(); + if(toEnd){rng.setStartAtLast(me.body.lastChild).setCursor(false,true)}else{rng.select(true)}this.fireEvent("focus")}catch(e){}},blur:function(){var sel=this.selection.getNative();sel.empty?sel.empty():sel.removeAllRanges();this.fireEvent("blur")},isFocus:function(){if(this.fireEvent("isfocus")===true){return true}return this.selection.isFocus()},_initEvents:function(){var me=this,cont=me.body,_proxyDomEvent=function(){me._proxyDomEvent.apply(me,arguments)};$(cont).on("click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart",_proxyDomEvent).on("focus blur",_proxyDomEvent).on("mouseup keydown",function(evt){if(evt.type=="keydown"&&(evt.ctrlKey||evt.metaKey||evt.shiftKey||evt.altKey)){return}if(evt.button==2){return}me._selectionChange(250,evt)})},_proxyDomEvent:function(evt){return this.fireEvent(evt.type.replace(/^on/,""),evt)},_selectionChange:function(delay,evt){var me=this;var hackForMouseUp=false;var mouseX,mouseY;if(browser.ie&&browser.version<9&&evt&&evt.type=="mouseup"){var range=this.selection.getRange();if(!range.collapsed){hackForMouseUp=true;mouseX=evt.clientX;mouseY=evt.clientY}}clearTimeout(_selectionChangeTimer);_selectionChangeTimer=setTimeout(function(){if(!me.selection.getNative()){return}var ieRange;if(hackForMouseUp&&me.selection.getNative().type=="None"){ieRange=me.document.body.createTextRange();try{ieRange.moveToPoint(mouseX,mouseY)}catch(ex){ieRange=null}}var bakGetIERange;if(ieRange){bakGetIERange=me.selection.getIERange;me.selection.getIERange=function(){return ieRange}}me.selection.cache();if(bakGetIERange){me.selection.getIERange=bakGetIERange}if(me.selection._cachedRange&&me.selection._cachedStartElement){me.fireEvent("beforeselectionchange");me.fireEvent("selectionchange",!!evt);me.fireEvent("afterselectionchange");me.selection.clear()}},delay||50)},_callCmdFn:function(fnName,args){args=Array.prototype.slice.call(args,0);var cmdName=args.shift().toLowerCase(),cmd,cmdFn;cmd=this.commands[cmdName]||UM.commands[cmdName];cmdFn=cmd&&cmd[fnName];if((!cmd||!cmdFn)&&fnName=="queryCommandState"){return 0}else{if(cmdFn){return cmdFn.apply(this,[cmdName].concat(args))}}},execCommand:function(cmdName){if(!this.isFocus()){var bakRange=this.selection._bakRange;if(bakRange){bakRange.select()}else{this.focus(true)}}cmdName=cmdName.toLowerCase();var me=this,result,cmd=me.commands[cmdName]||UM.commands[cmdName];if(!cmd||!cmd.execCommand){return null}if(!cmd.notNeedUndo&&!me.__hasEnterExecCommand){me.__hasEnterExecCommand=true;if(me.queryCommandState.apply(me,arguments)!=-1){me.fireEvent("saveScene");me.fireEvent("beforeexeccommand",cmdName);result=this._callCmdFn("execCommand",arguments);(!cmd.ignoreContentChange&&!me._ignoreContentChange)&&me.fireEvent("contentchange");me.fireEvent("afterexeccommand",cmdName);me.fireEvent("saveScene")}me.__hasEnterExecCommand=false}else{result=this._callCmdFn("execCommand",arguments);(!me.__hasEnterExecCommand&&!cmd.ignoreContentChange&&!me._ignoreContentChange)&&me.fireEvent("contentchange")}(!me.__hasEnterExecCommand&&!cmd.ignoreContentChange&&!me._ignoreContentChange)&&me._selectionChange();return result},queryCommandState:function(cmdName){try{return this._callCmdFn("queryCommandState",arguments)}catch(e){return 0}},queryCommandValue:function(cmdName){try{return this._callCmdFn("queryCommandValue",arguments)}catch(e){return null}},hasContents:function(tags){if(tags){for(var i=0,ci;ci=tags[i++];){if(this.body.getElementsByTagName(ci).length>0){return true}}}if(!domUtils.isEmptyBlock(this.body)){return true}tags=["div"];for(i=0;ci=tags[i++];){var nodes=domUtils.getElementsByTagName(this.body,ci);for(var n=0,cn;cn=nodes[n++];){if(domUtils.isCustomeNode(cn)){return true}}}return false},reset:function(){this.fireEvent("reset")},isEnabled:function(){return this._isEnabled!=true},setEnabled:function(){var me=this,range;me.body.contentEditable=true;if(me.lastBk){range=me.selection.getRange();try{range.moveToBookmark(me.lastBk);delete me.lastBk}catch(e){range.setStartAtFirst(me.body).collapse(true)}range.select(true)}if(me.bkqueryCommandState){me.queryCommandState=me.bkqueryCommandState;delete me.bkqueryCommandState}if(me._bkproxyDomEvent){me._proxyDomEvent=me._bkproxyDomEvent;delete me._bkproxyDomEvent}me.fireEvent("setEnabled")},enable:function(){return this.setEnabled()},setDisabled:function(except,keepDomEvent){var me=this;me.body.contentEditable=false;me._except=except?utils.isArray(except)?except:[except]:[];if(!me.lastBk){me.lastBk=me.selection.getRange().createBookmark(true)}if(!me.bkqueryCommandState){me.bkqueryCommandState=me.queryCommandState;me.queryCommandState=function(type){if(utils.indexOf(me._except,type)!=-1){return me.bkqueryCommandState.apply(me,arguments)}return -1}}if(!keepDomEvent&&!me._bkproxyDomEvent){me._bkproxyDomEvent=me._proxyDomEvent;me._proxyDomEvent=function(){return false}}me.fireEvent("selectionchange");me.fireEvent("setDisabled",me._except)},disable:function(except){return this.setDisabled(except)},_setDefaultContent:function(){function clear(){var me=this; + if(me.document.getElementById("initContent")){me.body.innerHTML="

          "+(ie?"":"
          ")+"

          ";me.removeListener("firstBeforeExecCommand focus",clear);setTimeout(function(){me.focus();me._selectionChange()},0)}}return function(cont){var me=this;me.body.innerHTML='

          '+cont+"

          ";me.addListener("firstBeforeExecCommand focus",clear)}}(),setShow:function(){var me=this,range=me.selection.getRange();if(me.container.style.display=="none"){try{range.moveToBookmark(me.lastBk);delete me.lastBk}catch(e){range.setStartAtFirst(me.body).collapse(true)}setTimeout(function(){range.select(true)},100);me.container.style.display=""}},show:function(){return this.setShow()},setHide:function(){var me=this;if(!me.lastBk){me.lastBk=me.selection.getRange().createBookmark(true)}me.container.style.display="none"},hide:function(){return this.setHide()},getLang:function(path){var lang=UM.I18N[this.options.lang];if(!lang){throw Error("not import language file")}path=(path||"").split(".");for(var i=0,ci;ci=path[i++];){lang=lang[ci];if(!lang){break}}return lang},getContentLength:function(ingoneHtml,tagNames){var count=this.getContent(false,false,true).length;if(ingoneHtml){tagNames=(tagNames||[]).concat(["hr","img","iframe"]);count=this.getContentTxt().replace(/[\t\r\n]+/g,"").length;for(var i=0,ci;ci=tagNames[i++];){count+=this.body.getElementsByTagName(ci).length}}return count},addInputRule:function(rule,ignoreUndo){rule.ignoreUndo=ignoreUndo;this.inputRules.push(rule)},filterInputRule:function(root,isUndoLoad){for(var i=0,ci;ci=this.inputRules[i++];){if(isUndoLoad&&ci.ignoreUndo){continue}ci.call(this,root)}},addOutputRule:function(rule,ignoreUndo){rule.ignoreUndo=ignoreUndo;this.outputRules.push(rule)},filterOutputRule:function(root,isUndoLoad){for(var i=0,ci;ci=this.outputRules[i++];){if(isUndoLoad&&ci.ignoreUndo){continue}ci.call(this,root)}}};utils.inherits(Editor,EventBase)})();var filterWord=UM.filterWord=function(){function isWordDocument(str){return/(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test(str)}function transUnit(v){v=v.replace(/[\d.]+\w+/g,function(m){return utils.transUnitToPx(m)});return v}function filterPasteWord(str){return str.replace(/[\t\r\n]+/g," ").replace(//ig,"").replace(/]*>[\s\S]*?.<\/v:shape>/gi,function(str){if(browser.opera){return""}try{if(/Bitmap/i.test(str)){return""}var width=str.match(/width:([ \d.]*p[tx])/i)[1],height=str.match(/height:([ \d.]*p[tx])/i)[1],src=str.match(/src=\s*"([^"]*)"/i)[1];return''}catch(e){return""}}).replace(/<\/?div[^>]*>/g,"").replace(/v:\w+=(["']?)[^'"]+\1/g,"").replace(/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi,"").replace(/

          ]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"

          $1

          ").replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig,function(str,name,marks,val){return name=="class"&&val=="MsoListParagraph"?str:""}).replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi,function(a,b,c){return c.replace(/[\t\r\n ]+/g," ")}).replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi,function(str,tag,tmp,style){var n=[],s=style.replace(/^\s+|\s+$/,"").replace(/'/g,"'").replace(/"/gi,"'").split(/;\s*/g);for(var i=0,v;v=s[i];i++){var name,value,parts=v.split(":");if(parts.length==2){name=parts[0].toLowerCase();value=parts[1].toLowerCase();if(/^(background)\w*/.test(name)&&value.replace(/(initial|\s)/g,"").length==0||/^(margin)\w*/.test(name)&&/^0\w+$/.test(value)){continue}switch(name){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":if(!/]/.test(html)){return UM.htmlparser(html).children[0]}else{return new uNode({type:"element",children:[],tagName:html})}};uNode.createText=function(data,noTrans){return new UM.uNode({type:"text","data":noTrans?data:utils.unhtml(data||"")})};function nodeToHtml(node,arr,formatter,current){switch(node.type){case"root":for(var i=0,ci;ci=node.children[i++];){if(formatter&&ci.type=="element"&&!dtd.$inlineWithA[ci.tagName]&&i>1){insertLine(arr,current,true);insertIndent(arr,current)}nodeToHtml(ci,arr,formatter,current)}break;case"text":isText(node,arr);break;case"element":isElement(node,arr,formatter,current);break;case"comment":isComment(node,arr,formatter)}return arr}function isText(node,arr){if(node.parentNode.tagName=="pre"){arr.push(node.data)}else{arr.push(notTransTagName[node.parentNode.tagName]?utils.html(node.data):node.data.replace(/[ ]{2}/g,"  "))}}function isElement(node,arr,formatter,current){var attrhtml="";if(node.attrs){attrhtml=[];var attrs=node.attrs;for(var a in attrs){attrhtml.push(a+(attrs[a]!==undefined?'="'+(notTransAttrs[a]?utils.html(attrs[a]).replace(/["]/g,function(a){return"""}):utils.unhtml(attrs[a]))+'"':""))}attrhtml=attrhtml.join(" ")}arr.push("<"+node.tagName+(attrhtml?" "+attrhtml:"")+(dtd.$empty[node.tagName]?"/":"")+">");if(formatter&&!dtd.$inlineWithA[node.tagName]&&node.tagName!="pre"){if(node.children&&node.children.length){current=insertLine(arr,current,true);insertIndent(arr,current)}}if(node.children&&node.children.length){for(var i=0,ci;ci=node.children[i++];){if(formatter&&ci.type=="element"&&!dtd.$inlineWithA[ci.tagName]&&i>1){insertLine(arr,current);insertIndent(arr,current)}nodeToHtml(ci,arr,formatter,current)}}if(!dtd.$empty[node.tagName]){if(formatter&&!dtd.$inlineWithA[node.tagName]&&node.tagName!="pre"){if(node.children&&node.children.length){current=insertLine(arr,current);insertIndent(arr,current)}}arr.push("")}}function isComment(node,arr){arr.push("")}function getNodeById(root,id){var node;if(root.type=="element"&&root.getAttr("id")==id){return root}if(root.children&&root.children.length){for(var i=0,ci;ci=root.children[i++];){if(node=getNodeById(ci,id)){return node}}}}function getNodesByTagName(node,tagName,arr){if(node.type=="element"&&node.tagName==tagName){arr.push(node)}if(node.children&&node.children.length){for(var i=0,ci;ci=node.children[i++];){getNodesByTagName(ci,tagName,arr)}}}function nodeTraversal(root,fn){if(root.children&&root.children.length){for(var i=0,ci;ci=root.children[i];){nodeTraversal(ci,fn);if(ci.parentNode){if(ci.children&&ci.children.length){fn(ci)}if(ci.parentNode){i++}}}}else{fn(root)}}uNode.prototype={toHtml:function(formatter){var arr=[];nodeToHtml(this,arr,formatter,0);return arr.join("")},innerHTML:function(htmlstr){if(this.type!="element"||dtd.$empty[this.tagName]){return this}if(utils.isString(htmlstr)){if(this.children){for(var i=0,ci;ci=this.children[i++];){ci.parentNode=null}}this.children=[];var tmpRoot=UM.htmlparser(htmlstr);for(var i=0,ci;ci=tmpRoot.children[i++];){this.children.push(ci);ci.parentNode=this}return this}else{var tmpRoot=new UM.uNode({type:"root",children:this.children});return tmpRoot.toHtml()}},innerText:function(textStr,noTrans){if(this.type!="element"||dtd.$empty[this.tagName]){return this}if(textStr){if(this.children){for(var i=0,ci;ci=this.children[i++];){ci.parentNode=null}}this.children=[];this.appendChild(uNode.createText(textStr,noTrans));return this}else{return this.toHtml().replace(/<[^>]+>/g,"")}},getData:function(){if(this.type=="element"){return""}return this.data},firstChild:function(){return this.children?this.children[0]:null},lastChild:function(){return this.children?this.children[this.children.length-1]:null},previousSibling:function(){var parent=this.parentNode;for(var i=0,ci;ci=parent.children[i];i++){if(ci===this){return i==0?null:parent.children[i-1]}}},nextSibling:function(){var parent=this.parentNode;for(var i=0,ci;ci=parent.children[i++];){if(ci===this){return parent.children[i]}}},replaceChild:function(target,source){if(this.children){if(target.parentNode){target.parentNode.removeChild(target)}for(var i=0,ci;ci=this.children[i];i++){if(ci===source){this.children.splice(i,1,target); + source.parentNode=null;target.parentNode=this;return target}}}},appendChild:function(node){if(this.type=="root"||(this.type=="element"&&!dtd.$empty[this.tagName])){if(!this.children){this.children=[]}if(node.parentNode){node.parentNode.removeChild(node)}for(var i=0,ci;ci=this.children[i];i++){if(ci===node){this.children.splice(i,1);break}}this.children.push(node);node.parentNode=this;return node}},insertBefore:function(target,source){if(this.children){if(target.parentNode){target.parentNode.removeChild(target)}for(var i=0,ci;ci=this.children[i];i++){if(ci===source){this.children.splice(i,0,target);target.parentNode=this;return target}}}},insertAfter:function(target,source){if(this.children){if(target.parentNode){target.parentNode.removeChild(target)}for(var i=0,ci;ci=this.children[i];i++){if(ci===source){this.children.splice(i+1,0,target);target.parentNode=this;return target}}}},removeChild:function(node,keepChildren){if(this.children){for(var i=0,ci;ci=this.children[i];i++){if(ci===node){this.children.splice(i,1);ci.parentNode=null;if(keepChildren&&ci.children&&ci.children.length){for(var j=0,cj;cj=ci.children[j];j++){this.children.splice(i+j,0,cj);cj.parentNode=this}}return ci}}}},getAttr:function(attrName){return this.attrs&&this.attrs[attrName.toLowerCase()]},setAttr:function(attrName,attrVal){if(!attrName){delete this.attrs;return}if(!this.attrs){this.attrs={}}if(utils.isObject(attrName)){for(var a in attrName){if(!attrName[a]){delete this.attrs[a]}else{this.attrs[a.toLowerCase()]=attrName[a]}}}else{if(!attrVal){delete this.attrs[attrName]}else{this.attrs[attrName.toLowerCase()]=attrVal}}},hasAttr:function(attrName){var attrVal=this.getAttr(attrName);return(attrVal!==null)&&(attrVal!==undefined)},getIndex:function(){var parent=this.parentNode;for(var i=0,ci;ci=parent.children[i];i++){if(ci===this){return i}}return -1},getNodeById:function(id){var node;if(this.children&&this.children.length){for(var i=0,ci;ci=this.children[i++];){if(node=getNodeById(ci,id)){return node}}}},getNodesByTagName:function(tagNames){tagNames=utils.trim(tagNames).replace(/[ ]{2,}/g," ").split(" ");var arr=[],me=this;utils.each(tagNames,function(tagName){if(me.children&&me.children.length){for(var i=0,ci;ci=me.children[i++];){getNodesByTagName(ci,tagName,arr)}}});return arr},getStyle:function(name){var cssStyle=this.getAttr("style");if(!cssStyle){return""}var reg=new RegExp("(^|;)\\s*"+name+":([^;]+)","i");var match=cssStyle.match(reg);if(match&&match[0]){return match[2]}return""},setStyle:function(name,val){function exec(name,val){var reg=new RegExp("(^|;)\\s*"+name+":([^;]+;?)","gi");cssStyle=cssStyle.replace(reg,"$1");if(val){cssStyle=name+":"+utils.unhtml(val)+";"+cssStyle}}var cssStyle=this.getAttr("style");if(!cssStyle){cssStyle=""}if(utils.isObject(name)){for(var a in name){exec(a,name[a])}}else{exec(name,val)}this.setAttr("style",utils.trim(cssStyle))},hasClass:function(className){if(this.hasAttr("class")){var classNames=this.getAttr("class").split(/\s+/),hasClass=false;$.each(classNames,function(key,item){if(item===className){hasClass=true}});return hasClass}else{return false}},addClass:function(className){var classes=null,hasClass=false;if(this.hasAttr("class")){classes=this.getAttr("class");classes=classes.split(/\s+/);classes.forEach(function(item){if(item===className){hasClass=true;return}});!hasClass&&classes.push(className);this.setAttr("class",classes.join(" "))}else{this.setAttr("class",className)}},removeClass:function(className){if(this.hasAttr("class")){var cl=this.getAttr("class");cl=cl.replace(new RegExp("\\b"+className+"\\b","g"),"");this.setAttr("class",utils.trim(cl).replace(/[ ]{2,}/g," "))}},traversal:function(fn){if(this.children&&this.children.length){nodeTraversal(this,fn)}return this}}})();var htmlparser=UM.htmlparser=function(htmlstr,ignoreBlank){var re_tag=/<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,re_attr=/([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;var allowEmptyTags={b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,sub:1,img:1,video:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1};htmlstr=htmlstr.replace(new RegExp(domUtils.fillChar,"g"),"");if(!ignoreBlank){htmlstr=htmlstr.replace(new RegExp("[\\r\\t\\n"+(ignoreBlank?"":" ")+"]*]*)>[\\r\\t\\n"+(ignoreBlank?"":" ")+"]*","g"),function(a,b){if(b&&allowEmptyTags[b.toLowerCase()]){return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,"")}return a.replace(new RegExp("^[\\r\\n"+(ignoreBlank?"":" ")+"]+"),"").replace(new RegExp("[\\r\\n"+(ignoreBlank?"":" ")+"]+$"),"")})}var notTransAttrs={"href":1,"src":1};var uNode=UM.uNode,needParentNode={"td":"tr","tr":["tbody","thead","tfoot"],"tbody":"table","th":"tr","thead":"table","tfoot":"table","caption":"table","li":["ul","ol"],"dt":"dl","dd":"dl","option":"select"},needChild={"ol":"li","ul":"li"};function text(parent,data){if(needChild[parent.tagName]){var tmpNode=uNode.createElement(needChild[parent.tagName]); + parent.appendChild(tmpNode);tmpNode.appendChild(uNode.createText(data));parent=tmpNode}else{parent.appendChild(uNode.createText(data))}}function element(parent,tagName,htmlattr){var needParentTag;if(needParentTag=needParentNode[tagName]){var tmpParent=parent,hasParent;while(tmpParent.type!="root"){if(utils.isArray(needParentTag)?utils.indexOf(needParentTag,tmpParent.tagName)!=-1:needParentTag==tmpParent.tagName){parent=tmpParent;hasParent=true;break}tmpParent=tmpParent.parentNode}if(!hasParent){parent=element(parent,utils.isArray(needParentTag)?needParentTag[0]:needParentTag)}}var elm=new uNode({parentNode:parent,type:"element",tagName:tagName.toLowerCase(),children:dtd.$empty[tagName]?null:[]});if(htmlattr){var attrs={},match;while(match=re_attr.exec(htmlattr)){attrs[match[1].toLowerCase()]=notTransAttrs[match[1].toLowerCase()]?(match[2]||match[3]||match[4]):utils.unhtml(match[2]||match[3]||match[4])}elm.attrs=attrs}parent.children.push(elm);return dtd.$empty[tagName]?parent:elm}function comment(parent,data){parent.children.push(new uNode({type:"comment",data:data,parentNode:parent}))}var match,currentIndex=0,nextIndex=0;var root=new uNode({type:"root",children:[]});var currentParent=root;while(match=re_tag.exec(htmlstr)){currentIndex=match.index;try{if(currentIndex>nextIndex){text(currentParent,htmlstr.slice(nextIndex,currentIndex))}if(match[3]){if(dtd.$cdata[currentParent.tagName]){text(currentParent,match[0])}else{currentParent=element(currentParent,match[3].toLowerCase(),match[4])}}else{if(match[1]){if(currentParent.type!="root"){if(dtd.$cdata[currentParent.tagName]&&!dtd.$cdata[match[1]]){text(currentParent,match[0])}else{var tmpParent=currentParent;while(currentParent.type=="element"&¤tParent.tagName!=match[1].toLowerCase()){currentParent=currentParent.parentNode;if(currentParent.type=="root"){currentParent=tmpParent;throw"break"}}currentParent=currentParent.parentNode}}}else{if(match[2]){comment(currentParent,match[2])}}}}catch(e){}nextIndex=re_tag.lastIndex}if(nextIndex"+(browser.ie?"":"
          ")+"

          ";range.setStart(me.body.firstChild,0).collapse(true)}}}!range.collapsed&&range.deleteContents();if(range.startContainer.nodeType==1){var child=range.startContainer.childNodes[range.startOffset],pre; + if(child&&domUtils.isBlockElm(child)&&(pre=child.previousSibling)&&domUtils.isBlockElm(pre)){range.setEnd(pre,pre.childNodes.length).collapse();while(child.firstChild){pre.appendChild(child.firstChild)}domUtils.remove(child)}}}var child,parent,pre,tmp,hadBreak=0,nextNode;if(range.inFillChar()){child=range.startContainer;if(domUtils.isFillChar(child)){range.setStartBefore(child).collapse(true);domUtils.remove(child)}else{if(domUtils.isFillChar(child,true)){child.nodeValue=child.nodeValue.replace(fillCharReg,"");range.startOffset--;range.collapsed&&range.collapse(true)}}}while(child=div.firstChild){if(hadBreak){var p=me.document.createElement("p");while(child&&(child.nodeType==3||!dtd.$block[child.tagName])){nextNode=child.nextSibling;p.appendChild(child);child=nextNode}if(p.firstChild){child=p}}range.insertNode(child);nextNode=child.nextSibling;if(!hadBreak&&child.nodeType==domUtils.NODE_ELEMENT&&domUtils.isBlockElm(child)){parent=domUtils.findParent(child,function(node){return domUtils.isBlockElm(node)});if(parent&&parent.tagName.toLowerCase()!="body"&&!(dtd[parent.tagName][child.nodeName]&&child.parentNode===parent)){if(!dtd[parent.tagName][child.nodeName]){pre=parent}else{tmp=child.parentNode;while(tmp!==parent){pre=tmp;tmp=tmp.parentNode}}domUtils.breakParent(child,pre||tmp);var pre=child.previousSibling;domUtils.trimWhiteTextNode(pre);if(!pre.childNodes.length){domUtils.remove(pre)}if(!browser.ie&&(next=child.nextSibling)&&domUtils.isBlockElm(next)&&next.lastChild&&!domUtils.isBr(next.lastChild)){next.appendChild(me.document.createElement("br"))}hadBreak=1}}var next=child.nextSibling;if(!div.firstChild&&next&&domUtils.isBlockElm(next)){range.setStart(next,0).collapse(true);break}range.setEndAfter(child).collapse()}child=range.startContainer;if(nextNode&&domUtils.isBr(nextNode)){domUtils.remove(nextNode)}if(domUtils.isBlockElm(child)&&domUtils.isEmptyNode(child)){if(nextNode=child.nextSibling){domUtils.remove(child);if(nextNode.nodeType==1&&dtd.$block[nextNode.tagName]){range.setStart(nextNode,0).collapse(true).shrinkBoundary()}}else{try{child.innerHTML=browser.ie?domUtils.fillChar:"
          "}catch(e){range.setStartBefore(child);domUtils.remove(child)}}}try{if(browser.ie9below&&range.startContainer.nodeType==1&&!range.startContainer.childNodes[range.startOffset]){var start=range.startContainer,pre=start.childNodes[range.startOffset-1];if(pre&&pre.nodeType==1&&dtd.$empty[pre.tagName]){var txt=this.document.createTextNode(domUtils.fillChar);range.insertNode(txt).setStart(txt,0).collapse(true)}}setTimeout(function(){range.select(true)})}catch(e){}setTimeout(function(){range=me.selection.getRange();range.scrollIntoView();me.fireEvent("afterinserthtml")},200)}};UM.commands["insertimage"]={execCommand:function(cmd,opt){opt=utils.isArray(opt)?opt:[opt];if(!opt.length){return}var me=this;var html=[],str="",ci;ci=opt[0];if(opt.length==1){str=''+ci.alt+'";if(ci["floatStyle"]=="center"){str='

          '+str+"

          "}html.push(str)}else{for(var i=0;ci=opt[i++];){str="

          ";html.push(str)}}me.execCommand("insertHtml",html.join(""),true)}};UM.plugins["justify"]=function(){var me=this;$.each("justifyleft justifyright justifycenter justifyfull".split(" "),function(i,cmdName){me.commands[cmdName]={execCommand:function(cmdName){return this.document.execCommand(cmdName)},queryCommandValue:function(cmdName){var val=this.document.queryCommandValue(cmdName);return val===true||val==="true"?cmdName.replace(/justify/,""):""},queryCommandState:function(cmdName){return this.document.queryCommandState(cmdName)?1:0}}})};UM.plugins["font"]=function(){var me=this,fonts={"forecolor":"forecolor","backcolor":"backcolor","fontsize":"fontsize","fontfamily":"fontname"},cmdNameToStyle={"forecolor":"color","backcolor":"background-color","fontsize":"font-size","fontfamily":"font-family"},cmdNameToAttr={"forecolor":"color","fontsize":"size","fontfamily":"face"};me.setOpt({"fontfamily":[{name:"songti",val:"宋体,SimSun"},{name:"yahei",val:"微软雅黑,Microsoft YaHei"},{name:"kaiti",val:"楷体,楷体_GB2312, SimKai"},{name:"heiti",val:"黑体, SimHei"},{name:"lishu",val:"隶书, SimLi"},{name:"andaleMono",val:"andale mono"},{name:"arial",val:"arial, helvetica,sans-serif"},{name:"arialBlack",val:"arial black,avant garde"},{name:"comicSansMs",val:"comic sans ms"},{name:"impact",val:"impact,chicago"},{name:"timesNewRoman",val:"times new roman"},{name:"sans-serif",val:"sans-serif"}],"fontsize":[10,12,16,18,24,32,48]}); + me.addOutputRule(function(root){utils.each(root.getNodesByTagName("font"),function(node){if(node.tagName=="font"){var cssStyle=[];for(var p in node.attrs){switch(p){case"size":var val=node.attrs[p];$.each({"10":"1","12":"2","16":"3","18":"4","24":"5","32":"6","48":"7"},function(k,v){if(v==val){val=k;return false}});cssStyle.push("font-size:"+val+"px");break;case"color":cssStyle.push("color:"+node.attrs[p]);break;case"face":cssStyle.push("font-family:"+node.attrs[p]);break;case"style":cssStyle.push(node.attrs[p])}}node.attrs={"style":cssStyle.join(";")}}node.tagName="span";if(node.parentNode.tagName=="span"&&node.parentNode.children.length==1){$.each(node.attrs,function(k,v){node.parentNode.attrs[k]=k=="style"?node.parentNode.attrs[k]+v:v});node.parentNode.removeChild(node,true)}})});for(var p in fonts){(function(cmd){me.commands[cmd]={execCommand:function(cmdName,value){if(value=="transparent"){return}var rng=this.selection.getRange();if(rng.collapsed){var span=$("").css(cmdNameToStyle[cmdName],value)[0];rng.insertNode(span).setStart(span,0).setCursor()}else{if(cmdName=="fontsize"){value={"10":"1","12":"2","16":"3","18":"4","24":"5","32":"6","48":"7"}[(value+"").replace(/px/,"")]}this.document.execCommand(fonts[cmdName],false,value);if(browser.gecko){$.each(this.$body.find("a"),function(i,a){var parent=a.parentNode;if(parent.lastChild===parent.firstChild&&/FONT|SPAN/.test(parent.tagName)){var cloneNode=parent.cloneNode(false);cloneNode.innerHTML=a.innerHTML;$(a).html("").append(cloneNode).insertBefore(parent);$(parent).remove()}})}if(!browser.ie){var nativeRange=this.selection.getNative().getRangeAt(0);var common=nativeRange.commonAncestorContainer;var rng=this.selection.getRange(),bk=rng.createBookmark(true);$(common).find("a").each(function(i,n){var parent=n.parentNode;if(parent.nodeName=="FONT"){var font=parent.cloneNode(false);font.innerHTML=n.innerHTML;$(n).html("").append(font)}});rng.moveToBookmark(bk).select()}return true}},queryCommandValue:function(cmdName){var start=me.selection.getStart();var val=$(start).css(cmdNameToStyle[cmdName]);if(val===undefined){val=$(start).attr(cmdNameToAttr[cmdName])}return val?utils.fixColor(cmdName,val).replace(/px/,""):""},queryCommandState:function(cmdName){return this.queryCommandValue(cmdName)}}})(p)}};UM.plugins["link"]=function(){var me=this;me.setOpt("autourldetectinie",false);if(browser.ie&&this.options.autourldetectinie===false){this.addListener("keyup",function(cmd,evt){var me=this,keyCode=evt.keyCode;if(keyCode==13||keyCode==32){var rng=me.selection.getRange();var start=rng.startContainer;if(keyCode==13){if(start.nodeName=="P"){var pre=start.previousSibling;if(pre&&pre.nodeType==1){var pre=pre.lastChild;if(pre&&pre.nodeName=="A"&&!pre.getAttribute("_href")){domUtils.remove(pre,true)}}}}else{if(keyCode==32){if(start.nodeType==3&&/^\s$/.test(start.nodeValue)){start=start.previousSibling;if(start&&start.nodeName=="A"&&!start.getAttribute("_href")){domUtils.remove(start,true)}}}}}})}this.addOutputRule(function(root){$.each(root.getNodesByTagName("a"),function(i,a){var _href=utils.html(a.getAttr("_href"));if(!/^(ftp|https?|\/|file)/.test(_href)){_href="http://"+_href}a.setAttr("href",_href);a.setAttr("_href");if(a.getAttr("title")==""){a.setAttr("title")}})});this.addInputRule(function(root){$.each(root.getNodesByTagName("a"),function(i,a){a.setAttr("_href",utils.html(a.getAttr("href")))})});me.commands["link"]={execCommand:function(cmdName,opt){var me=this;var rng=me.selection.getRange();if(rng.collapsed){var start=rng.startContainer;if(start=domUtils.findParentByTagName(start,"a",true)){$(start).attr(opt);rng.selectNode(start).select()}else{rng.insertNode($(""+opt.href+"").attr(opt)[0]).select()}}else{me.document.execCommand("createlink",false,"_umeditor_link");utils.each(domUtils.getElementsByTagName(me.body,"a",function(n){return n.getAttribute("href")=="_umeditor_link"}),function(l){if($(l).text()=="_umeditor_link"){$(l).text(opt.href)}domUtils.setAttributes(l,opt);rng.selectNode(l).select()})}},queryCommandState:function(){return this.queryCommandValue("link")?1:0},queryCommandValue:function(){var path=this.selection.getStartElementPath();var result;$.each(path,function(i,n){if(n.nodeName=="A"){result=n;return false}});return result}};me.commands["unlink"]={execCommand:function(){this.document.execCommand("unlink")}}};UM.commands["print"]={execCommand:function(){var me=this,id="editor_print_"+ +new Date();$('').attr("id",id).css({width:"0px",height:"0px","overflow":"hidden","float":"left","position":"absolute",top:"-10000px",left:"-10000px"}).appendTo(me.$container.find(".edui-dialog-container"));var w=window.open("",id,""),d=w.document;d.open();d.write("
          "+this.getContent(null,null,true)+"

          c$q7CxhrU{pOV$rn)CDowrtBM7&~={I-a? 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%0D{~cR)!)dS_6~=Ck`da!cf6Jo-y6 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 z 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

        NF&@ge{F+!1m$4?Up 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%0Di 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