WP Mail SMTP by WPForms - Version 1.8.0

Version Description

  • 2019-12-12 =
  • Added: New recommended mailer: Pepipost.
  • Added: "Suggest a Mailer" link in a list of mailers to send us your ideas about new ones.
  • Fixed: Sendgrid: Content ID for attachments missing.
  • Changed: Timeout to HTTP requests (pepipost, sendgrid, mailgun), same as max_execution_time, to prevent fails when sending emails with big attachments.
Download this release

Release Info

Developer slaFFik
Plugin Icon 128x128 WP Mail SMTP by WPForms
Version 1.8.0
Comparing to
See all releases

Code changes from version 1.7.1 to 1.8.0

Files changed (60) hide show
  1. assets/css/smtp-admin.min.css +1 -1
  2. assets/images/providers/pepipost-smtp.png +0 -0
  3. assets/images/providers/pepipost.png +0 -0
  4. assets/js/smtp-about.js +190 -190
  5. assets/languages/wp-mail-smtp.pot +67 -37
  6. readme.txt +395 -377
  7. src/Admin/Area.php +889 -889
  8. src/Admin/Pages/About.php +702 -702
  9. src/Admin/Pages/SettingsTab.php +546 -537
  10. src/Admin/PluginsInstallSkin.php +55 -55
  11. src/Options.php +38 -13
  12. src/Processor.php +232 -232
  13. src/Providers/Loader.php +205 -204
  14. src/Providers/MailerAbstract.php +24 -4
  15. src/Providers/Pepipost/Options.php +2 -2
  16. src/Providers/PepipostAPI/Mailer.php +440 -0
  17. src/Providers/PepipostAPI/Options.php +119 -0
  18. src/Providers/Sendgrid/Mailer.php +1 -0
  19. src/Providers/Sendinblue/Options.php +119 -119
  20. vendor/guzzlehttp/guzzle/phpstan-baseline.neon +1412 -0
  21. vendor/guzzlehttp/guzzle/src/Client.php +531 -422
  22. vendor/guzzlehttp/guzzle/src/ClientInterface.php +87 -84
  23. vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +316 -314
  24. vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php +84 -84
  25. vendor/guzzlehttp/guzzle/src/Exception/RequestException.php +192 -217
  26. vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php +585 -580
  27. vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +219 -205
  28. vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php +195 -190
  29. vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php +544 -544
  30. vendor/guzzlehttp/guzzle/src/HandlerStack.php +277 -273
  31. vendor/guzzlehttp/guzzle/src/MessageFormatter.php +185 -180
  32. vendor/guzzlehttp/guzzle/src/Pool.php +132 -123
  33. vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php +111 -106
  34. vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php +249 -237
  35. vendor/guzzlehttp/guzzle/src/RequestOptions.php +263 -255
  36. vendor/guzzlehttp/guzzle/src/RetryMiddleware.php +128 -115
  37. vendor/guzzlehttp/guzzle/src/TransferStats.php +126 -126
  38. vendor/guzzlehttp/guzzle/src/functions.php +346 -346
  39. vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php +3 -1
  40. vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php +3 -2
  41. vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php +2 -2
  42. vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php +1 -1
  43. vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php +1 -1
  44. vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php +4 -125
  45. vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php +5 -4
  46. vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php +19 -0
  47. vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php +3 -2
  48. vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php +3 -2
  49. vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php +41 -11
  50. vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php +44 -14
  51. vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php +2 -1
  52. vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php +2 -1
  53. vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php +2 -1
  54. vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php +2 -1
  55. vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php +42 -11
  56. vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php +7 -2
  57. vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php +2 -1
  58. vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php +2 -1
  59. vendor/monolog/monolog/src/Monolog/Utils.php +134 -0
  60. wp_mail_smtp.php +167 -166
assets/css/smtp-admin.min.css CHANGED
@@ -1,3 +1,3 @@
1
- #wpcontent{padding-left:0 !important;position:relative}@media (max-width: 600px){#wpcontent{padding-top:46px}}@media (max-width: 600px){#wpbody{padding-top:0}}body.toplevel_page_wp-mail-smtp div.jconfirm *,body.toplevel_page_wp-mail-smtp div.jconfirm *::before,body.toplevel_page_wp-mail-smtp div.jconfirm *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box{border-radius:0;box-shadow:0 2px 6px rgba(0,0,0,0.2)}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box div.jconfirm-closeIcon{color:rgba(0,0,0,0.4);top:8px;right:8px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box div.jconfirm-closeIcon:hover{color:rgba(0,0,0,0.8)}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c{margin:0 0 26px 0 !important;padding:0 !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{margin:0 !important;color:#c4c4c4 !important;-webkit-transition:none !important;transition:none !important;-webkit-transform:none !important;-ms-transform:none !important;transform:none !important;font-size:45px !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c i:empty{display:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c svg{height:35px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-title{display:block;color:#333}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c+span.jconfirm-title{margin-top:20px !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content-pane{margin-bottom:0;display:block}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content-pane .jconfirm-content{overflow:inherit}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content{font-size:16px;color:#555;line-height:1.4}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content.lite-upgrade p{font-size:18px;padding:0 20px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p{margin:0 0 16px;font-size:18px;line-height:1.5}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p:last-of-type{margin:0}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p.large{font-size:18px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p.small{font-size:14px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content .already-purchased{font-size:12px;color:#ccc;text-decoration:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content .already-purchased:hover{text-decoration:underline}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-buttons .already-purchased{display:block;font-size:12px;color:#aaa;text-decoration:none;padding-top:20px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-buttons .already-purchased:hover{color:#999;text-decoration:underline}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note{text-align:center;margin:30px 0 0}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note p{background-color:#faffac;margin:0 -30px;padding:24px 60px 20px;font-size:15px;color:#4d4d4d;position:relative}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note p:after{content:'\f058';display:inline-block;font:normal normal normal 14px/1 FontAwesome;background-color:#fff;font-size:26px;border-radius:50%;padding:5px 6px;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;position:absolute;top:-20px;right:50%;margin-right:-18px;color:#3abc01}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note span{font-weight:700;color:#3abc01}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note a{color:#aaa;display:block;margin-top:12px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box input[type=text]{display:block;width:99%;border:1px solid #d6d6d6;padding:10px;box-shadow:none;margin:20px auto 0 auto}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-confirm{background-color:#FF982D;color:#fff;outline:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-confirm:hover{background-color:#f97f00;border-color:#f97f00}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button,body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-default{color:#666}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-block{display:block;text-align:center;width:100%;margin:0 0 10px 0 !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-normal-case{text-transform:none !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .error{display:none;color:red}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .wpforms-error{border:1px solid #ebccd1 !important}#wp-mail-smtp-header{border-top:3px solid #FF982D;padding:20px}#wp-mail-smtp-header img{display:block;margin:0;max-width:242px}@media (max-width: 782px){#wp-mail-smtp-header img{max-width:200px}}#wp-mail-smtp{margin:0}#wp-mail-smtp .wp-mail-smtp-hide{display:none}#wp-mail-smtp .wp-mail-smtp-page-title{background-color:#fff;font-size:14px;margin:0 0 20px 0;padding:0 20px}#wp-mail-smtp .wp-mail-smtp-page-title a.tab{border-bottom:2px solid #fff;box-shadow:none;color:#666;display:inline-block;margin-right:30px;padding:20px 0 18px 0;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-title a.tab.active{border-bottom:2px solid #FF982D}#wp-mail-smtp .wp-mail-smtp-page-title a.tab:hover{border-color:#999}#wp-mail-smtp .wp-mail-smtp-page-title a.action{padding:5px;border:0;min-height:auto;color:#fff;margin:-10px 0 0 20px}#wp-mail-smtp .wp-mail-smtp-page-title .page-title{background-color:#fff;display:inline-block;font-size:23px;margin:0;padding:15px 20px 15px 0}#wp-mail-smtp .wp-mail-smtp-page-content{padding:0 20px}#wp-mail-smtp .wp-mail-smtp-page-content *,#wp-mail-smtp .wp-mail-smtp-page-content *::before,#wp-mail-smtp .wp-mail-smtp-page-content *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-clear:before{content:" ";display:table}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-clear:after{clear:both;content:" ";display:table}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row{border-bottom:1px solid #e4e4e4;padding:30px 0;font-size:14px;line-height:1.3}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row:first-of-type{padding-top:10px !important}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row{padding:20px 0}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.inactive{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row .wp-mail-smtp-setting-mid-row-sep{background:#e4e4e4;height:1px;border:0;margin:15px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading{padding:20px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading.no-desc h2,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading.no-desc h4{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading .wp-mail-smtp-setting-field{margin:0;max-width:1005px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]{float:left;margin:1px 0 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]+label{margin:0 0 0 8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox .desc{margin:0 0 0 30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]+label+.desc{margin:8px 0 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-text .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-password .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-number .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-email .wp-mail-smtp-setting-label{padding-top:8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-select .wp-mail-smtp-setting-label{padding-top:8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-radio .wp-mail-smtp-setting-field input[type=radio]{margin:-3px 10px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-radio .wp-mail-smtp-setting-field label{margin-right:30px;display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field label{vertical-align:middle;display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field label:hover .wp-mail-smtp-setting-toggle-switch{background-color:#999}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch{background-color:#46B450}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch:before{-webkit-transform:translateX(19px);-ms-transform:translateX(19px);transform:translateX(19px)}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch{background-color:#69c471;cursor:default}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label{display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label+.wp-mail-smtp-setting-toggle-unchecked-label{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-unchecked-label{color:#aaa}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-unchecked-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-checked-label{text-transform:uppercase;font-weight:400;color:#777;font-size:13px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-checked-label{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-switch{position:relative;cursor:pointer;background-color:#ccc;border-radius:15px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;vertical-align:middle;position:relative;display:inline-block;margin:0 5px 0 0;width:40px;height:20px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-switch:before{position:absolute;content:"";height:14px;width:14px;left:3px;top:3px;background-color:#fff;border-radius:50%;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer{padding-bottom:20px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers{width:680px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer{display:inline-block;width:150px;margin-right:15px;margin-bottom:15px;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer .wp-mail-smtp-mailer-recommended{position:absolute;right:-1px;width:99px;top:2px;z-index:1}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer:last-child{margin-right:0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image{background:#fff;text-align:center;border:2px solid #E5E5E5;border-radius:4px;cursor:pointer;height:76px;position:relative;margin-bottom:10px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image.is-recommended{background-image:url(../images/recommended.svg);background-repeat:no-repeat;background-size:60%;background-position:top right -2px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image img{max-width:90%;max-height:40px;display:block;position:relative;top:50%;left:50%;transform:translate(-50%, -50%);opacity:0.6;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer.wp-mail-smtp-mailer-smtp .wp-mail-smtp-mailer-image img{max-height:30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer.active .wp-mail-smtp-mailer-image{border-color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer.active .wp-mail-smtp-mailer-image img{opacity:1}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer:hover .wp-mail-smtp-mailer-image{border-color:#ccc}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailer:hover .wp-mail-smtp-mailer-image img{opacity:1}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h2,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h4{color:#444;font-size:20px;font-weight:700;margin:0 0 6px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h2{margin-bottom:15px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h3{color:#444;font-size:24px;font-weight:600;margin:0 0 20px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p{margin:12px 0 0;font-size:14px;line-height:1.5em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p:first-of-type{margin:8px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.desc{font-style:italic;color:#777}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.buttonned{margin-top:30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice{margin:5px 0 15px;box-sizing:border-box;background:#fff;border-left:4px solid transparent;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1)}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice.inline-error{border-color:#dc3232;margin-bottom:5px;padding:10px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice.inline-edu-notice{border-color:#809EB0;line-height:1.5em;padding:10px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice a.wp-mail-smtp-mailer-notice-dismiss{float:right;color:#999DA1;margin:0 0 10px 10px;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice a.wp-mail-smtp-mailer-notice-dismiss:hover{color:#666a6e}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row ul{margin:8px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row ul.list li{margin-left:20px;list-style-type:disc}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list th{padding:5px 5px 5px 0;text-align:left}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.email{padding-right:2em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.status{width:100px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a{border-bottom:1px solid;display:inline-block;margin-right:5px;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]{color:#a00}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:hover,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:active,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:focus{color:#400}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]{background-color:#fff;border:1px solid #ddd;border-radius:3px;box-shadow:none;color:#333;display:inline-block;vertical-align:middle;padding:7px 12px;margin:0 10px 0 0;width:400px;min-height:35px}@media (max-width: 959px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]{width:300px}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password][readonly]{background-color:#f9f9f9}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password].small-text{width:75px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]:focus{border-color:#bbb}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]:disabled{opacity:0.6}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label{display:block;float:left;width:205px;padding:0 20px 0 0}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label{float:none;width:100%;padding-bottom:15px}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label label{display:block;font-weight:600;font-size:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-field{display:block;margin:0 0 0 205px;max-width:800px}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-field{margin:0}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-submit{margin:0;padding:25px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-submit .help-text{margin-left:10px;vertical-align:middle}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-mailer-options .wp-mail-smtp-mailer-option .wp-mail-smtp-setting-row.section-heading{padding:20px 0 !important}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-mailer-options .wp-mail-smtp-mailer-option blockquote{background:#E5E5E5;border-radius:4px;color:#666;font-size:14px;margin:20px 0;padding:15px;width:1005px}#wp-mail-smtp .wp-mail-smtp-page-content.wp-mail-smtp-page-general p{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline{background:#fff;border-left:4px solid #fff;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);margin:5px 0 15px;padding:1px 12px}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-success{border-left-color:#46b450}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-warning{border-left-color:#ffb900}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-error{border-left-color:#dc3232}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-info{border-left-color:#00a0d2}#wp-mail-smtp .wp-mail-smtp-page-content .notice p,#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline p{margin:0.5em 0;padding:2px}#wp-mail-smtp .wp-mail-smtp-page-content pre{white-space:pre-line}#wp-mail-smtp .wp-mail-smtp-page-content.active{display:block}#wp-mail-smtp .wp-mail-smtp-page-content .connected-as{margin-left:30px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug{background-color:#fff;padding:25px 20px 1px 25px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug h2{color:#444;margin:1.4em 0 0.8em;font-size:16px;font-weight:700}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug p{font-size:14px;color:#555;margin-bottom:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol{font-size:14px;color:#555;margin:0 0 1.1em 1.8em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li{margin:0 0 8px 0;line-height:1.5}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li:last-of-type,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li:last-of-type{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li ul,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li ul{list-style-type:disc}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug a{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug a:hover{color:#f97f00}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .dashicons-star-filled{color:#FF982D;width:16px;height:16px;font-size:16px;vertical-align:text-top}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .price-off{color:green;font-weight:bold}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle{text-decoration:none;color:#444}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle:hover{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle .dashicons{font-size:15px;height:15px;width:15px;padding-top:3px;border:0;outline:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log{border-left:3px solid #ffb900;padding:0 0 0 20px;margin:0 0 10px 0;font-size:12px;display:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log pre{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-note{display:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner{background-color:#fff;padding:25px 20px;border:1px solid #dadada;margin:10px 0 0 0;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .wp-mail-smtp-pro-banner-dismiss{position:absolute;right:10px;top:10px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .wp-mail-smtp-pro-banner-dismiss button{background:none;border:none;color:#a9a9a9;cursor:pointer;margin:0;padding:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner h2{color:#444;margin-top:0;font-size:16px;font-weight:700}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner p{font-size:14px;color:#555;margin-bottom:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner p:last-of-type{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits{margin:0 0 16px 0;overflow:auto;max-width:1000px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul{margin:0;padding:0;width:50%;float:left}@media (max-width: 600px){#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul{width:100%;float:none}}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li{margin:0;padding:0 0 2px 16px;color:#555;font-size:14px;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li:before{content:'+';position:absolute;top:-1px;left:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li.arrow-right:before{content:'→'}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:hover,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:active,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:focus{color:#f97f00}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .stars{text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .stars .dashicons{width:16px;height:16px;font-size:16px;vertical-align:text-top}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .price-off{color:green;font-weight:bold}#wp-mail-smtp .wp-mail-smtp-admin-columns>div[class*="-column-"]{float:left}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-20{width:20%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-33{width:33.33333%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-40{width:40%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-50{width:50%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-60{width:60%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-80{width:80%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-last{float:right !important}#wp-mail-smtp .wp-mail-smtp-admin-columns:after{content:"";display:table;clear:both}#wp-mail-smtp .wp-mail-smtp-page-upsell{display:flex;align-items:center;justify-content:center;height:auto;flex-direction:column}#wp-mail-smtp .wp-mail-smtp-page-upsell>*{width:800px}#wp-mail-smtp .wp-mail-smtp-page-upsell *,#wp-mail-smtp .wp-mail-smtp-page-upsell *::before,#wp-mail-smtp .wp-mail-smtp-page-upsell *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-mail-smtp .wp-mail-smtp-page-upsell h2{font-size:24px;color:#444444;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell h3{font-size:16px;font-weight:normal;color:#72777C;line-height:1.5em;margin-top:0;margin-bottom:25px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features{margin-bottom:40px;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-items:stretch;align-content:flex-start}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature{display:flex;flex-direction:row;width:360px;align-items:flex-start;align-content:flex-start;margin-right:60px;margin-top:40px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(1){margin-top:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(2){margin-top:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(even){margin-right:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-image{width:65px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-image img{display:block;margin:5px auto 0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content{margin-left:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content h4{font-size:15px;margin:0 0 0.5em}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content p{color:#72777C;margin:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images{margin-bottom:20px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images img{width:380px;height:auto;margin-right:20px;border:5px solid #fff;border-radius:5px;box-shadow:0 0 10px 5px rgba(0,0,0,0.15)}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images img:last-child{margin-right:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-button{text-align:center}.wp-mail-smtp-btn{border:0;border-radius:3px;cursor:pointer;display:inline-block;margin:0;text-decoration:none;text-align:center;vertical-align:middle;white-space:nowrap;text-shadow:none;box-shadow:none;outline:none}.wp-mail-smtp-btn .dashicons{font-size:16px;width:16px;height:16px}.wp-mail-smtp-btn:disabled{opacity:0.5;cursor:not-allowed}.wp-mail-smtp-btn.wp-mail-smtp-btn-md{font-size:13px;font-weight:600;padding:8px 12px;min-height:35px}.wp-mail-smtp-btn.wp-mail-smtp-btn-lg{font-size:16px;font-weight:600;padding:16px 28px}.wp-mail-smtp-btn.wp-mail-smtp-btn-orange{background-color:#FF982D;border-color:#FF982D;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:focus{background-color:#f97f00;border-color:#f97f00}.wp-mail-smtp-btn.wp-mail-smtp-btn-red{background-color:#DC3232;border-color:#DC3232;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-red:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-red:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-red:focus{background-color:darkred;border-color:darkred}.wp-mail-smtp-btn.wp-mail-smtp-btn-grey{background-color:#f5f5f5;border:1px solid #ccc;color:#666}.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:focus{background-color:#d7d7d7;border-color:#ccc;color:#444}.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey{background-color:#f5f5f5;border:1px solid #ccc;color:#666}.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:focus{background-color:#eee;color:#444}.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish{background-color:#738e9e;border:1px solid #738e9e;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:focus{background-color:#395360;border-color:#395360;color:#fff}
2
 
3
  /*# sourceMappingURL=smtp-admin.min.css.map */
1
+ #wpcontent{padding-left:0 !important;position:relative}@media (max-width: 600px){#wpcontent{padding-top:46px}}@media (max-width: 600px){#wpbody{padding-top:0}}body.toplevel_page_wp-mail-smtp div.jconfirm *,body.toplevel_page_wp-mail-smtp div.jconfirm *::before,body.toplevel_page_wp-mail-smtp div.jconfirm *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box{border-radius:0;box-shadow:0 2px 6px rgba(0,0,0,0.2)}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box div.jconfirm-closeIcon{color:rgba(0,0,0,0.4);top:8px;right:8px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box-container div.jconfirm-box div.jconfirm-closeIcon:hover{color:rgba(0,0,0,0.8)}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c{margin:0 0 26px 0 !important;padding:0 !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c{margin:0 !important;color:#c4c4c4 !important;-webkit-transition:none !important;transition:none !important;-webkit-transform:none !important;-ms-transform:none !important;transform:none !important;font-size:45px !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c i:empty{display:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c svg{height:35px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-title{display:block;color:#333}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-title-c .jconfirm-icon-c+span.jconfirm-title{margin-top:20px !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content-pane{margin-bottom:0;display:block}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content-pane .jconfirm-content{overflow:inherit}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content{font-size:16px;color:#555;line-height:1.4}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content.lite-upgrade p{font-size:18px;padding:0 20px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p{margin:0 0 16px;font-size:18px;line-height:1.5}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p:last-of-type{margin:0}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p.large{font-size:18px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content p.small{font-size:14px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content .already-purchased{font-size:12px;color:#ccc;text-decoration:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-content .already-purchased:hover{text-decoration:underline}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-buttons .already-purchased{display:block;font-size:12px;color:#aaa;text-decoration:none;padding-top:20px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box div.jconfirm-buttons .already-purchased:hover{color:#999;text-decoration:underline}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note{text-align:center;margin:30px 0 0}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note p{background-color:#faffac;margin:0 -30px;padding:24px 60px 20px;font-size:15px;color:#4d4d4d;position:relative}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note p:after{content:'\f058';display:inline-block;font:normal normal normal 14px/1 FontAwesome;background-color:#fff;font-size:26px;border-radius:50%;padding:5px 6px;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;position:absolute;top:-20px;right:50%;margin-right:-18px;color:#3abc01}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note span{font-weight:700;color:#3abc01}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .discount-note a{color:#aaa;display:block;margin-top:12px}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box input[type=text]{display:block;width:99%;border:1px solid #d6d6d6;padding:10px;box-shadow:none;margin:20px auto 0 auto}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-confirm{background-color:#FF982D;color:#fff;outline:none}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-confirm:hover{background-color:#f97f00;border-color:#f97f00}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button,body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-default{color:#666}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-block{display:block;text-align:center;width:100%;margin:0 0 10px 0 !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box button.btn-normal-case{text-transform:none !important}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .error{display:none;color:red}body.toplevel_page_wp-mail-smtp div.jconfirm div.jconfirm-box .wpforms-error{border:1px solid #ebccd1 !important}#wp-mail-smtp-header{border-top:3px solid #FF982D;padding:20px}#wp-mail-smtp-header img{display:block;margin:0;max-width:242px}@media (max-width: 782px){#wp-mail-smtp-header img{max-width:200px}}#wp-mail-smtp{margin:0}#wp-mail-smtp .wp-mail-smtp-hide{display:none}#wp-mail-smtp .wp-mail-smtp-page-title{background-color:#fff;font-size:14px;margin:0 0 20px 0;padding:0 20px}#wp-mail-smtp .wp-mail-smtp-page-title a.tab{border-bottom:2px solid #fff;box-shadow:none;color:#666;display:inline-block;margin-right:30px;padding:20px 0 18px 0;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-title a.tab.active{border-bottom:2px solid #FF982D}#wp-mail-smtp .wp-mail-smtp-page-title a.tab:hover{border-color:#999}#wp-mail-smtp .wp-mail-smtp-page-title a.action{padding:5px;border:0;min-height:auto;color:#fff;margin:-10px 0 0 20px}#wp-mail-smtp .wp-mail-smtp-page-title .page-title{background-color:#fff;display:inline-block;font-size:23px;margin:0;padding:15px 20px 15px 0}#wp-mail-smtp .wp-mail-smtp-page-content{padding:0 20px}#wp-mail-smtp .wp-mail-smtp-page-content *,#wp-mail-smtp .wp-mail-smtp-page-content *::before,#wp-mail-smtp .wp-mail-smtp-page-content *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-clear:before{content:" ";display:table}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-clear:after{clear:both;content:" ";display:table}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row{border-bottom:1px solid #e4e4e4;padding:30px 0;font-size:14px;line-height:1.3}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row:first-of-type{padding-top:10px !important}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row{padding:20px 0}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.inactive{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row .wp-mail-smtp-setting-mid-row-sep{background:#e4e4e4;height:1px;border:0;margin:15px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading{padding:20px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading.no-desc h2,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading.no-desc h4{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.section-heading .wp-mail-smtp-setting-field{margin:0;max-width:1005px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]{float:left;margin:1px 0 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]+label{margin:0 0 0 8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox .desc{margin:0 0 0 30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox input[type=checkbox]+label+.desc{margin:8px 0 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-text .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-password .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-number .wp-mail-smtp-setting-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-email .wp-mail-smtp-setting-label{padding-top:8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-select .wp-mail-smtp-setting-label{padding-top:8px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-radio .wp-mail-smtp-setting-field input[type=radio]{margin:-3px 10px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-radio .wp-mail-smtp-setting-field label{margin-right:30px;display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field label{vertical-align:middle;display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field label:hover .wp-mail-smtp-setting-toggle-switch{background-color:#999}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch{background-color:#46B450}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch:before{-webkit-transform:translateX(19px);-ms-transform:translateX(19px);transform:translateX(19px)}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch{background-color:#69c471;cursor:default}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label{display:inline-block}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:checked+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label+.wp-mail-smtp-setting-toggle-unchecked-label{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-checked-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field input[type=checkbox]:disabled+.wp-mail-smtp-setting-toggle-switch+.wp-mail-smtp-setting-toggle-unchecked-label{color:#aaa}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-unchecked-label,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-checked-label{text-transform:uppercase;font-weight:400;color:#777;font-size:13px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-checked-label{display:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-switch{position:relative;cursor:pointer;background-color:#ccc;border-radius:15px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;vertical-align:middle;position:relative;display:inline-block;margin:0 5px 0 0;width:40px;height:20px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-checkbox-toggle .wp-mail-smtp-setting-field .wp-mail-smtp-setting-toggle-switch:before{position:absolute;content:"";height:14px;width:14px;left:3px;top:3px;background-color:#fff;border-radius:50%;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer{padding-bottom:20px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer{display:inline-block;width:140px;margin-right:12px;margin-bottom:12px;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image{background:#fff;text-align:center;border:2px solid #E5E5E5;border-radius:4px;cursor:pointer;height:76px;position:relative;margin-bottom:10px;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image.is-recommended{background-image:url(../images/recommended.svg);background-repeat:no-repeat;background-size:60%;background-position:top right -2px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer .wp-mail-smtp-mailer-image img{max-width:90%;max-height:40px;display:block;position:relative;top:50%;left:50%;transform:translate(-50%, -50%);opacity:0.6;-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-ms-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer.wp-mail-smtp-mailer-smtp .wp-mail-smtp-mailer-image img{max-height:30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer.suggest-new a.wp-mail-smtp-mailer-image{min-width:140px;display:table-cell;color:#777777;text-decoration:underline;vertical-align:middle;bottom:10px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer.suggest-new .wp-mail-smtp-mailer-text{visibility:hidden}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer.active .wp-mail-smtp-mailer-image{border-color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer.active .wp-mail-smtp-mailer-image img{opacity:1}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer:hover .wp-mail-smtp-mailer-image{border-color:#ccc}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row.wp-mail-smtp-setting-row-mailer .wp-mail-smtp-mailers .wp-mail-smtp-mailer:hover .wp-mail-smtp-mailer-image img{opacity:1}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h2,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h4{color:#444;font-size:20px;font-weight:700;margin:0 0 6px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h2{margin-bottom:15px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row h3{color:#444;font-size:24px;font-weight:600;margin:0 0 20px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p{margin:12px 0 0;font-size:14px;line-height:1.5em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p:first-of-type{margin:8px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.desc{font-style:italic;color:#777}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.buttonned{margin-top:30px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice{margin:5px 0 15px;box-sizing:border-box;background:#fff;border-left:4px solid transparent;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1)}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice.inline-error{border-color:#dc3232;margin-bottom:5px;padding:10px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice.inline-edu-notice{border-color:#809EB0;line-height:1.5em;padding:10px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice a.wp-mail-smtp-mailer-notice-dismiss{float:right;color:#999DA1;margin:0 0 10px 10px;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row p.inline-notice a.wp-mail-smtp-mailer-notice-dismiss:hover{color:#666a6e}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row ul{margin:8px 0 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row ul.list li{margin-left:20px;list-style-type:disc}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list th{padding:5px 5px 5px 0;text-align:left}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.email{padding-right:2em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.status{width:100px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a{border-bottom:1px solid;display:inline-block;margin-right:5px;text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]{color:#a00}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:hover,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:active,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row table.actions-list td.actions a[class*=delete]:focus{color:#400}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]{background-color:#fff;border:1px solid #ddd;border-radius:3px;box-shadow:none;color:#333;display:inline-block;vertical-align:middle;padding:7px 12px;margin:0 10px 0 0;width:400px;min-height:35px;line-height:1.3}@media (max-width: 959px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]{width:300px}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number][readonly],#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password][readonly]{background-color:#f9f9f9}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number].small-text,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password].small-text{width:75px}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number]:focus,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]:focus{border-color:#bbb}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=text]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=email]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=number]:disabled,#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-row input[type=password]:disabled{opacity:0.6}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label{display:block;float:left;width:205px;padding:0 20px 0 0}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label{float:none;width:100%;padding-bottom:15px}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-label label{display:block;font-weight:600;font-size:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-field{display:block;margin:0 0 0 205px;max-width:800px}@media (max-width: 781px){#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-setting-field{margin:0}}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-submit{margin:0;padding:25px 0}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-submit .help-text{margin-left:10px;vertical-align:middle}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-mailer-options .wp-mail-smtp-mailer-option .wp-mail-smtp-setting-row.section-heading{padding:20px 0 !important}#wp-mail-smtp .wp-mail-smtp-page-content .wp-mail-smtp-mailer-options .wp-mail-smtp-mailer-option blockquote{background:#E5E5E5;border-radius:4px;color:#666;font-size:14px;margin:20px 0;padding:15px;width:1005px}#wp-mail-smtp .wp-mail-smtp-page-content.wp-mail-smtp-page-general p{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline{background:#fff;border-left:4px solid #fff;box-shadow:0 1px 1px 0 rgba(0,0,0,0.1);margin:5px 0 15px;padding:1px 12px}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-success{border-left-color:#46b450}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-warning{border-left-color:#ffb900}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-error{border-left-color:#dc3232}#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline.notice-info{border-left-color:#00a0d2}#wp-mail-smtp .wp-mail-smtp-page-content .notice p,#wp-mail-smtp .wp-mail-smtp-page-content .notice-inline p{margin:0.5em 0;padding:2px}#wp-mail-smtp .wp-mail-smtp-page-content pre{white-space:pre-line}#wp-mail-smtp .wp-mail-smtp-page-content.active{display:block}#wp-mail-smtp .wp-mail-smtp-page-content .connected-as{margin-left:30px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug{background-color:#fff;padding:25px 20px 1px 25px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug h2{color:#444;margin:1.4em 0 0.8em;font-size:16px;font-weight:700}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug p{font-size:14px;color:#555;margin-bottom:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol{font-size:14px;color:#555;margin:0 0 1.1em 1.8em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li{margin:0 0 8px 0;line-height:1.5}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li:last-of-type,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li:last-of-type{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ul li ul,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug ol li ul{list-style-type:disc}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug a{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug a:hover{color:#f97f00}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .dashicons-star-filled{color:#FF982D;width:16px;height:16px;font-size:16px;vertical-align:text-top}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .price-off{color:green;font-weight:bold}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle{text-decoration:none;color:#444}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle:hover{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-toggle .dashicons{font-size:15px;height:15px;width:15px;padding-top:3px;border:0;outline:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log{border-left:3px solid #ffb900;padding:0 0 0 20px;margin:0 0 10px 0;font-size:12px;display:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log pre{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-debug .error-log-note{display:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner{background-color:#fff;padding:25px 20px;border:1px solid #dadada;margin:10px 0 0 0;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .wp-mail-smtp-pro-banner-dismiss{position:absolute;right:10px;top:10px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .wp-mail-smtp-pro-banner-dismiss button{background:none;border:none;color:#a9a9a9;cursor:pointer;margin:0;padding:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner h2{color:#444;margin-top:0;font-size:16px;font-weight:700}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner p{font-size:14px;color:#555;margin-bottom:1.1em}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner p:last-of-type{margin:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits{margin:0 0 16px 0;overflow:auto;max-width:1000px}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul{margin:0;padding:0;width:50%;float:left}@media (max-width: 600px){#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul{width:100%;float:none}}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li{margin:0;padding:0 0 2px 16px;color:#555;font-size:14px;position:relative}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li:before{content:'+';position:absolute;top:-1px;left:0}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .benefits ul li.arrow-right:before{content:'→'}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a{color:#FF982D}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:hover,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:active,#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner a:focus{color:#f97f00}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .stars{text-decoration:none}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .stars .dashicons{width:16px;height:16px;font-size:16px;vertical-align:text-top}#wp-mail-smtp .wp-mail-smtp-page-content #wp-mail-smtp-pro-banner .price-off{color:green;font-weight:bold}#wp-mail-smtp .wp-mail-smtp-admin-columns>div[class*="-column-"]{float:left}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-20{width:20%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-33{width:33.33333%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-40{width:40%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-50{width:50%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-60{width:60%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-80{width:80%}#wp-mail-smtp .wp-mail-smtp-admin-columns .wp-mail-smtp-admin-column-last{float:right !important}#wp-mail-smtp .wp-mail-smtp-admin-columns:after{content:"";display:table;clear:both}#wp-mail-smtp .wp-mail-smtp-page-upsell{display:flex;align-items:center;justify-content:center;height:auto;flex-direction:column}#wp-mail-smtp .wp-mail-smtp-page-upsell>*{width:800px}#wp-mail-smtp .wp-mail-smtp-page-upsell *,#wp-mail-smtp .wp-mail-smtp-page-upsell *::before,#wp-mail-smtp .wp-mail-smtp-page-upsell *::after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-mail-smtp .wp-mail-smtp-page-upsell h2{font-size:24px;color:#444444;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell h3{font-size:16px;font-weight:normal;color:#72777C;line-height:1.5em;margin-top:0;margin-bottom:25px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features{margin-bottom:40px;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-items:stretch;align-content:flex-start}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature{display:flex;flex-direction:row;width:360px;align-items:flex-start;align-content:flex-start;margin-right:60px;margin-top:40px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(1){margin-top:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(2){margin-top:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature:nth-of-type(even){margin-right:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-image{width:65px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-image img{display:block;margin:5px auto 0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content{margin-left:20px}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content h4{font-size:15px;margin:0 0 0.5em}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-content .wp-mail-smtp-page-upsell-features .wp-mail-smtp-page-upsell-feature .wp-mail-smtp-page-upsell-feature-content p{color:#72777C;margin:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images{margin-bottom:20px;text-align:center}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images img{width:380px;height:auto;margin-right:20px;border:5px solid #fff;border-radius:5px;box-shadow:0 0 10px 5px rgba(0,0,0,0.15)}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-images img:last-child{margin-right:0}#wp-mail-smtp .wp-mail-smtp-page-upsell .wp-mail-smtp-page-upsell-button{text-align:center}.wp-mail-smtp-btn{border:0;border-radius:3px;cursor:pointer;display:inline-block;margin:0;text-decoration:none;text-align:center;vertical-align:middle;white-space:nowrap;text-shadow:none;box-shadow:none;outline:none}.wp-mail-smtp-btn .dashicons{font-size:16px;width:16px;height:16px}.wp-mail-smtp-btn:disabled{opacity:0.5;cursor:not-allowed}.wp-mail-smtp-btn.wp-mail-smtp-btn-md{font-size:13px;font-weight:600;padding:8px 12px;min-height:35px}.wp-mail-smtp-btn.wp-mail-smtp-btn-lg{font-size:16px;font-weight:600;padding:16px 28px}.wp-mail-smtp-btn.wp-mail-smtp-btn-orange{background-color:#FF982D;border-color:#FF982D;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-orange:focus{background-color:#f97f00;border-color:#f97f00}.wp-mail-smtp-btn.wp-mail-smtp-btn-red{background-color:#DC3232;border-color:#DC3232;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-red:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-red:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-red:focus{background-color:darkred;border-color:darkred}.wp-mail-smtp-btn.wp-mail-smtp-btn-grey{background-color:#f5f5f5;border:1px solid #ccc;color:#666}.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-grey:focus{background-color:#d7d7d7;border-color:#ccc;color:#444}.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey{background-color:#f5f5f5;border:1px solid #ccc;color:#666}.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-light-grey:focus{background-color:#eee;color:#444}.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish{background-color:#738e9e;border:1px solid #738e9e;color:#fff}.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:hover,.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:active,.wp-mail-smtp-btn.wp-mail-smtp-btn-blueish:focus{background-color:#395360;border-color:#395360;color:#fff}
2
 
3
  /*# sourceMappingURL=smtp-admin.min.css.map */
assets/images/providers/pepipost-smtp.png ADDED
Binary file
assets/images/providers/pepipost.png CHANGED
Binary file
assets/js/smtp-about.js CHANGED
@@ -1,190 +1,190 @@
1
- /* global WPMailSMTP, jQuery, wp_mail_smtp_about */
2
-
3
- var WPMailSMTP = window.WPMailSMTP || {};
4
- WPMailSMTP.Admin = WPMailSMTP.Admin || {};
5
-
6
- /**
7
- * WP Mail SMTP Admin area About module.
8
- *
9
- * @since 1.5.0
10
- */
11
- WPMailSMTP.Admin.About = WPMailSMTP.Admin.About || (function ( document, window, $ ) {
12
-
13
- 'use strict';
14
-
15
- /**
16
- * Private functions and properties.
17
- *
18
- * @since 1.5.0
19
- *
20
- * @type {Object}
21
- */
22
- var __private = {};
23
-
24
- /**
25
- * Public functions and properties.
26
- *
27
- * @since 1.5.0
28
- *
29
- * @type {Object}
30
- */
31
- var app = {
32
-
33
- /**
34
- * Start the engine. DOM is not ready yet, use only to init something.
35
- *
36
- * @since 1.5.0
37
- */
38
- init: function () {
39
-
40
- // Do that when DOM is ready.
41
- $( document ).ready( app.ready );
42
- },
43
-
44
- /**
45
- * DOM is fully loaded.
46
- *
47
- * @since 1.5.0
48
- */
49
- ready: function () {
50
-
51
- app.pageHolder = $( '.wp-mail-smtp-page-about' );
52
-
53
- app.bindActions();
54
-
55
- $( '.wp-mail-smtp-page' ).trigger( 'WPMailSMTP.Admin.About.ready' );
56
- },
57
-
58
- /**
59
- * Process all generic actions/events, mostly custom that were fired by our API.
60
- *
61
- * @since 1.5.0
62
- */
63
- bindActions: function () {
64
-
65
- /*
66
- * Make plugins description the same height.
67
- */
68
- jQuery('.wp-mail-smtp-admin-about-plugins .plugin-item .details').matchHeight();
69
-
70
- /*
71
- * Install/Active the plugins.
72
- */
73
- $( document ).on( 'click', '.wp-mail-smtp-admin-about-plugins .plugin-item .action-button .button', function( e ) {
74
- e.preventDefault();
75
-
76
- var $btn = $( this );
77
-
78
- if ( $btn.hasClass( 'disabled' ) || $btn.hasClass( 'loading' ) ) {
79
- return false;
80
- }
81
-
82
- var $plugin = $btn.closest( '.plugin-item' ),
83
- plugin = $btn.attr( 'data-plugin' ),
84
- task,
85
- cssClass,
86
- statusText,
87
- buttonText,
88
- errorText,
89
- successText;
90
-
91
- $btn.prop( 'disabled', true ).addClass( 'loading' );
92
- $btn.text( wp_mail_smtp_about.plugin_processing );
93
-
94
- if ( $btn.hasClass( 'status-inactive' ) ) {
95
- // Activate.
96
- task = 'about_plugin_activate';
97
- cssClass = 'status-active button button-secondary disabled';
98
- statusText = wp_mail_smtp_about.plugin_active;
99
- buttonText = wp_mail_smtp_about.plugin_activated;
100
- errorText = wp_mail_smtp_about.plugin_activate;
101
-
102
- } else if ( $btn.hasClass( 'status-download' ) ) {
103
- // Install & Activate.
104
- task = 'about_plugin_install';
105
- cssClass = 'status-active button disabled';
106
- statusText = wp_mail_smtp_about.plugin_active;
107
- buttonText = wp_mail_smtp_about.plugin_activated;
108
- errorText = wp_mail_smtp_about.plugin_activate;
109
-
110
- } else {
111
- return;
112
- }
113
-
114
- // Setup ajax POST data.
115
- var data = {
116
- action: 'wp_mail_smtp_ajax',
117
- task: task,
118
- nonce : wp_mail_smtp_about.nonce,
119
- plugin: plugin
120
- };
121
-
122
- $.post( wp_mail_smtp_about.ajax_url, data, function( res ) {
123
- var is_install_successful;
124
-
125
- if ( res.success ) {
126
- is_install_successful = true;
127
- if ( 'about_plugin_install' === task ) {
128
- $btn.attr( 'data-plugin', res.data.basename );
129
- successText = res.data.msg;
130
- if ( ! res.data.is_activated ) {
131
- cssClass = 'button';
132
- statusText = wp_mail_smtp_about.plugin_inactive;
133
- buttonText = wp_mail_smtp_about.plugin_activate;
134
- }
135
- } else {
136
- successText = res.data;
137
- }
138
- $plugin.find( '.actions' ).append( '<div class="msg success">'+successText+'</div>' );
139
- $plugin.find( 'span.status-label' )
140
- .removeClass( 'status-active status-inactive status-download' )
141
- .addClass( cssClass )
142
- .removeClass( 'button button-primary button-secondary disabled' )
143
- .text( statusText );
144
- $btn
145
- .removeClass( 'status-active status-inactive status-download' )
146
- .removeClass( 'button button-primary button-secondary disabled' )
147
- .addClass( cssClass ).html( buttonText );
148
- } else {
149
- is_install_successful = false;
150
-
151
- if (
152
- res.hasOwnProperty( 'data' ) &&
153
- res.data.hasOwnProperty( 0 ) &&
154
- res.data[ 0 ].hasOwnProperty( 'code' ) &&
155
- res.data[ 0 ].code === 'download_failed'
156
- ) {
157
- // Specific server-returned error.
158
- $plugin.find( '.actions' ).append( '<div class="msg error">' + wp_mail_smtp_about.plugin_install_error + '</div>' );
159
- }
160
- else {
161
- // Generic error.
162
- $plugin.find( '.actions' ).append( '<div class="msg error">' + res.data + '</div>' );
163
- }
164
-
165
- $btn.html( wp_mail_smtp_about.plugin_download_btn );
166
- }
167
-
168
- if ( is_install_successful ) {
169
- $btn.prop( 'disabled', false );
170
- }
171
- $btn.removeClass( 'loading' );
172
-
173
- // Automatically clear plugin messages after 3 seconds.
174
- setTimeout( function () {
175
- $( '.plugin-item .msg' ).remove();
176
- }, 3000 );
177
-
178
- }).fail( function( xhr ) {
179
- console.log( xhr.responseText );
180
- });
181
- });
182
- }
183
- };
184
-
185
- // Provide access to public functions/properties.
186
- return app;
187
- })( document, window, jQuery );
188
-
189
- // Initialize.
190
- WPMailSMTP.Admin.About.init();
1
+ /* global WPMailSMTP, jQuery, wp_mail_smtp_about */
2
+
3
+ var WPMailSMTP = window.WPMailSMTP || {};
4
+ WPMailSMTP.Admin = WPMailSMTP.Admin || {};
5
+
6
+ /**
7
+ * WP Mail SMTP Admin area About module.
8
+ *
9
+ * @since 1.5.0
10
+ */
11
+ WPMailSMTP.Admin.About = WPMailSMTP.Admin.About || (function ( document, window, $ ) {
12
+
13
+ 'use strict';
14
+
15
+ /**
16
+ * Private functions and properties.
17
+ *
18
+ * @since 1.5.0
19
+ *
20
+ * @type {Object}
21
+ */
22
+ var __private = {};
23
+
24
+ /**
25
+ * Public functions and properties.
26
+ *
27
+ * @since 1.5.0
28
+ *
29
+ * @type {Object}
30
+ */
31
+ var app = {
32
+
33
+ /**
34
+ * Start the engine. DOM is not ready yet, use only to init something.
35
+ *
36
+ * @since 1.5.0
37
+ */
38
+ init: function () {
39
+
40
+ // Do that when DOM is ready.
41
+ $( document ).ready( app.ready );
42
+ },
43
+
44
+ /**
45
+ * DOM is fully loaded.
46
+ *
47
+ * @since 1.5.0
48
+ */
49
+ ready: function () {
50
+
51
+ app.pageHolder = $( '.wp-mail-smtp-page-about' );
52
+
53
+ app.bindActions();
54
+
55
+ $( '.wp-mail-smtp-page' ).trigger( 'WPMailSMTP.Admin.About.ready' );
56
+ },
57
+
58
+ /**
59
+ * Process all generic actions/events, mostly custom that were fired by our API.
60
+ *
61
+ * @since 1.5.0
62
+ */
63
+ bindActions: function () {
64
+
65
+ /*
66
+ * Make plugins description the same height.
67
+ */
68
+ jQuery('.wp-mail-smtp-admin-about-plugins .plugin-item .details').matchHeight();
69
+
70
+ /*
71
+ * Install/Active the plugins.
72
+ */
73
+ $( document ).on( 'click', '.wp-mail-smtp-admin-about-plugins .plugin-item .action-button .button', function( e ) {
74
+ e.preventDefault();
75
+
76
+ var $btn = $( this );
77
+
78
+ if ( $btn.hasClass( 'disabled' ) || $btn.hasClass( 'loading' ) ) {
79
+ return false;
80
+ }
81
+
82
+ var $plugin = $btn.closest( '.plugin-item' ),
83
+ plugin = $btn.attr( 'data-plugin' ),
84
+ task,
85
+ cssClass,
86
+ statusText,
87
+ buttonText,
88
+ errorText,
89
+ successText;
90
+
91
+ $btn.prop( 'disabled', true ).addClass( 'loading' );
92
+ $btn.text( wp_mail_smtp_about.plugin_processing );
93
+
94
+ if ( $btn.hasClass( 'status-inactive' ) ) {
95
+ // Activate.
96
+ task = 'about_plugin_activate';
97
+ cssClass = 'status-active button button-secondary disabled';
98
+ statusText = wp_mail_smtp_about.plugin_active;
99
+ buttonText = wp_mail_smtp_about.plugin_activated;
100
+ errorText = wp_mail_smtp_about.plugin_activate;
101
+
102
+ } else if ( $btn.hasClass( 'status-download' ) ) {
103
+ // Install & Activate.
104
+ task = 'about_plugin_install';
105
+ cssClass = 'status-active button disabled';
106
+ statusText = wp_mail_smtp_about.plugin_active;
107
+ buttonText = wp_mail_smtp_about.plugin_activated;
108
+ errorText = wp_mail_smtp_about.plugin_activate;
109
+
110
+ } else {
111
+ return;
112
+ }
113
+
114
+ // Setup ajax POST data.
115
+ var data = {
116
+ action: 'wp_mail_smtp_ajax',
117
+ task: task,
118
+ nonce : wp_mail_smtp_about.nonce,
119
+ plugin: plugin
120
+ };
121
+
122
+ $.post( wp_mail_smtp_about.ajax_url, data, function( res ) {
123
+ var is_install_successful;
124
+
125
+ if ( res.success ) {
126
+ is_install_successful = true;
127
+ if ( 'about_plugin_install' === task ) {
128
+ $btn.attr( 'data-plugin', res.data.basename );
129
+ successText = res.data.msg;
130
+ if ( ! res.data.is_activated ) {
131
+ cssClass = 'button';
132
+ statusText = wp_mail_smtp_about.plugin_inactive;
133
+ buttonText = wp_mail_smtp_about.plugin_activate;
134
+ }
135
+ } else {
136
+ successText = res.data;
137
+ }
138
+ $plugin.find( '.actions' ).append( '<div class="msg success">'+successText+'</div>' );
139
+ $plugin.find( 'span.status-label' )
140
+ .removeClass( 'status-active status-inactive status-download' )
141
+ .addClass( cssClass )
142
+ .removeClass( 'button button-primary button-secondary disabled' )
143
+ .text( statusText );
144
+ $btn
145
+ .removeClass( 'status-active status-inactive status-download' )
146
+ .removeClass( 'button button-primary button-secondary disabled' )
147
+ .addClass( cssClass ).html( buttonText );
148
+ } else {
149
+ is_install_successful = false;
150
+
151
+ if (
152
+ res.hasOwnProperty( 'data' ) &&
153
+ res.data.hasOwnProperty( 0 ) &&
154
+ res.data[ 0 ].hasOwnProperty( 'code' ) &&
155
+ res.data[ 0 ].code === 'download_failed'
156
+ ) {
157
+ // Specific server-returned error.
158
+ $plugin.find( '.actions' ).append( '<div class="msg error">' + wp_mail_smtp_about.plugin_install_error + '</div>' );
159
+ }
160
+ else {
161
+ // Generic error.
162
+ $plugin.find( '.actions' ).append( '<div class="msg error">' + res.data + '</div>' );
163
+ }
164
+
165
+ $btn.html( wp_mail_smtp_about.plugin_download_btn );
166
+ }
167
+
168
+ if ( is_install_successful ) {
169
+ $btn.prop( 'disabled', false );
170
+ }
171
+ $btn.removeClass( 'loading' );
172
+
173
+ // Automatically clear plugin messages after 3 seconds.
174
+ setTimeout( function () {
175
+ $( '.plugin-item .msg' ).remove();
176
+ }, 3000 );
177
+
178
+ }).fail( function( xhr ) {
179
+ console.log( xhr.responseText );
180
+ });
181
+ });
182
+ }
183
+ };
184
+
185
+ // Provide access to public functions/properties.
186
+ return app;
187
+ })( document, window, jQuery );
188
+
189
+ // Initialize.
190
+ WPMailSMTP.Admin.About.init();
assets/languages/wp-mail-smtp.pot CHANGED
@@ -1,13 +1,13 @@
1
  msgid ""
2
  msgstr ""
3
- "Project-Id-Version: WP Mail SMTP 1.7.1\n"
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-smtp\n"
5
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
6
  "Language-Team: LANGUAGE <LL@li.org>\n"
7
  "MIME-Version: 1.0\n"
8
  "Content-Type: text/plain; charset=UTF-8\n"
9
  "Content-Transfer-Encoding: 8bit\n"
10
- "POT-Creation-Date: 2019-11-11T16:59:39+02:00\n"
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
  "X-Generator: WP-CLI 2.2.0\n"
13
  "X-Domain: wp-mail-smtp\n"
@@ -478,7 +478,7 @@ msgid "Check this if you would like to remove ALL WP Mail SMTP data upon plugin
478
  msgstr ""
479
 
480
  #: src/Admin/Pages/MiscTab.php:215
481
- #: src/Admin/Pages/SettingsTab.php:533
482
  msgid "Settings were successfully saved."
483
  msgstr ""
484
 
@@ -578,93 +578,98 @@ msgstr ""
578
  msgid "Mailer"
579
  msgstr ""
580
 
581
- #: src/Admin/Pages/SettingsTab.php:265
 
 
 
 
 
582
  msgid "Dismiss this notice"
583
  msgstr ""
584
 
585
- #: src/Admin/Pages/SettingsTab.php:307
586
  msgid "You're using WP Mail SMTP Lite - no license needed. Enjoy!"
587
  msgstr ""
588
 
589
  #. translators: %s - WPMailSMTP.com upgrade URL.
590
- #: src/Admin/Pages/SettingsTab.php:313
591
  msgid "To unlock more features consider <strong><a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"wp-mail-smtp-upgrade-modal\">upgrading to PRO</a></strong>."
592
  msgstr ""
593
 
594
- #: src/Admin/Pages/SettingsTab.php:332
595
  msgid "As a valued WP Mail SMTP Lite user you receive <strong>20% off</strong>, automatically applied at checkout!"
596
  msgstr ""
597
 
598
- #: src/Admin/Pages/SettingsTab.php:393
599
  msgid "Get WP Mail SMTP Pro and Unlock all the Powerful Features"
600
  msgstr ""
601
 
602
- #: src/Admin/Pages/SettingsTab.php:397
603
  msgid "Thanks for being a loyal WP Mail SMTP user. Upgrade to WP Mail SMTP Pro to unlock more awesome features and experience why WP Mail SMTP is the most popular SMTP plugin."
604
  msgstr ""
605
 
606
- #: src/Admin/Pages/SettingsTab.php:401
607
  msgid "We know that you will truly love WP Mail SMTP. It's used by over 1,000,000 websites."
608
  msgstr ""
609
 
610
- #: src/Admin/Pages/SettingsTab.php:404
611
  msgid "Pro Features:"
612
  msgstr ""
613
 
614
- #: src/Admin/Pages/SettingsTab.php:408
615
  msgid "Manage Notifications - control which emails your site sends"
616
  msgstr ""
617
 
618
- #: src/Admin/Pages/SettingsTab.php:409
619
  msgid "Email Logging - keep track of every email sent from your site"
620
  msgstr ""
621
 
622
- #: src/Admin/Pages/SettingsTab.php:410
623
  msgid "Office 365 - send emails using your Office 365 account"
624
  msgstr ""
625
 
626
- #: src/Admin/Pages/SettingsTab.php:411
627
  msgid "Amazon SES - harness the power of AWS"
628
  msgstr ""
629
 
630
- #: src/Admin/Pages/SettingsTab.php:412
631
  msgid "Outlook.com - send emails using your Outlook.com account"
632
  msgstr ""
633
 
634
- #: src/Admin/Pages/SettingsTab.php:413
635
  msgid "Access to our world class support team"
636
  msgstr ""
637
 
638
- #: src/Admin/Pages/SettingsTab.php:416
639
  msgid "White Glove Setup - sit back and relax while we handle everything for you"
640
  msgstr ""
641
 
642
- #: src/Admin/Pages/SettingsTab.php:417
643
  msgid "Install WP Mail SMTP Pro plugin"
644
  msgstr ""
645
 
646
- #: src/Admin/Pages/SettingsTab.php:418
647
  msgid "Set up domain name verification (DNS)"
648
  msgstr ""
649
 
650
- #: src/Admin/Pages/SettingsTab.php:419
651
  msgid "Configure Mailgun service"
652
  msgstr ""
653
 
654
- #: src/Admin/Pages/SettingsTab.php:420
655
  msgid "Set up WP Mail SMTP Pro plugin"
656
  msgstr ""
657
 
658
- #: src/Admin/Pages/SettingsTab.php:421
659
  msgid "Test and verify email delivery"
660
  msgstr ""
661
 
662
  #. translators: %s - WPMailSMTP.com URL.
663
- #: src/Admin/Pages/SettingsTab.php:429
664
  msgid "<a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\">Get WP Mail SMTP Pro Today and Unlock all the Powerful Features &raquo;</a>"
665
  msgstr ""
666
 
667
- #: src/Admin/Pages/SettingsTab.php:447
668
  msgid "<strong>Bonus:</strong> WP Mail SMTP users get <span class=\"price-off\">20% off regular price</span>, automatically applied at checkout."
669
  msgstr ""
670
 
@@ -1445,9 +1450,44 @@ msgid "We're sorry, the Microsoft Outlook mailer is not available on your plan.
1445
  msgstr ""
1446
 
1447
  #: src/Providers/Pepipost/Options.php:25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1448
  msgid "Pepipost"
1449
  msgstr ""
1450
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1451
  #: src/Providers/Sendgrid/Options.php:25
1452
  msgid "SendGrid"
1453
  msgstr ""
@@ -1457,11 +1497,6 @@ msgstr ""
1457
  msgid "%1$sSendGrid%2$s is one of the leading transactional email services, sending over 35 billion emails every month. They provide users 100 free emails per day.<br><br>Read our %3$sSendGrid documentation%4$s to learn how to set up SendGrid and improve your email deliverability."
1458
  msgstr ""
1459
 
1460
- #: src/Providers/Sendgrid/Options.php:57
1461
- #: src/Providers/Sendinblue/Options.php:88
1462
- msgid "API Key"
1463
- msgstr ""
1464
-
1465
  #. translators: %s - API key link.
1466
  #: src/Providers/Sendgrid/Options.php:76
1467
  msgid "Follow this link to get an API Key from SendGrid: %s."
@@ -1478,7 +1513,7 @@ msgstr ""
1478
 
1479
  #. translators: %1$s - URL to sendinblue.com site.
1480
  #: src/Providers/Sendinblue/Options.php:31
1481
- msgid "<strong><a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">Sendinblue</a> is our recommended transactional email service.</strong> Founded in 2012, they serve 80,000+ growing companies around the world and send over 30 million emails each day. They understand that transactional emails are the heart of your customer relationships. Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day."
1482
  msgstr ""
1483
 
1484
  #. translators: %2$s - URL to wpmailsmtp.com doc.
@@ -1494,11 +1529,6 @@ msgstr ""
1494
  msgid "Sendinblue"
1495
  msgstr ""
1496
 
1497
- #. translators: %s - sendinblue.com link to get an API Key.
1498
- #: src/Providers/Sendinblue/Options.php:107
1499
- msgid "Follow this link to get an API Key: %s."
1500
- msgstr ""
1501
-
1502
  #: src/Providers/Sendinblue/Options.php:109
1503
  msgid "Get v3 API Key"
1504
  msgstr ""
@@ -1662,6 +1692,6 @@ msgstr ""
1662
  msgid "Your site is running an outdated version of PHP that is no longer supported and may cause issues with %1$s. %2$sRead more%3$s for additional information."
1663
  msgstr ""
1664
 
1665
- #: wp_mail_smtp.php:130
1666
  msgid "Please deactivate the free version of the WP Mail SMTP plugin before activating WP Mail SMTP Pro."
1667
  msgstr ""
1
  msgid ""
2
  msgstr ""
3
+ "Project-Id-Version: WP Mail SMTP 1.8.0\n"
4
  "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-mail-smtp\n"
5
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
6
  "Language-Team: LANGUAGE <LL@li.org>\n"
7
  "MIME-Version: 1.0\n"
8
  "Content-Type: text/plain; charset=UTF-8\n"
9
  "Content-Transfer-Encoding: 8bit\n"
10
+ "POT-Creation-Date: 2019-12-12T16:09:57+02:00\n"
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
  "X-Generator: WP-CLI 2.2.0\n"
13
  "X-Domain: wp-mail-smtp\n"
478
  msgstr ""
479
 
480
  #: src/Admin/Pages/MiscTab.php:215
481
+ #: src/Admin/Pages/SettingsTab.php:542
482
  msgid "Settings were successfully saved."
483
  msgstr ""
484
 
578
  msgid "Mailer"
579
  msgstr ""
580
 
581
+ #: src/Admin/Pages/SettingsTab.php:241
582
+ #: src/Admin/Pages/SettingsTab.php:245
583
+ msgid "Suggest a Mailer"
584
+ msgstr ""
585
+
586
+ #: src/Admin/Pages/SettingsTab.php:274
587
  msgid "Dismiss this notice"
588
  msgstr ""
589
 
590
+ #: src/Admin/Pages/SettingsTab.php:316
591
  msgid "You're using WP Mail SMTP Lite - no license needed. Enjoy!"
592
  msgstr ""
593
 
594
  #. translators: %s - WPMailSMTP.com upgrade URL.
595
+ #: src/Admin/Pages/SettingsTab.php:322
596
  msgid "To unlock more features consider <strong><a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"wp-mail-smtp-upgrade-modal\">upgrading to PRO</a></strong>."
597
  msgstr ""
598
 
599
+ #: src/Admin/Pages/SettingsTab.php:341
600
  msgid "As a valued WP Mail SMTP Lite user you receive <strong>20% off</strong>, automatically applied at checkout!"
601
  msgstr ""
602
 
603
+ #: src/Admin/Pages/SettingsTab.php:402
604
  msgid "Get WP Mail SMTP Pro and Unlock all the Powerful Features"
605
  msgstr ""
606
 
607
+ #: src/Admin/Pages/SettingsTab.php:406
608
  msgid "Thanks for being a loyal WP Mail SMTP user. Upgrade to WP Mail SMTP Pro to unlock more awesome features and experience why WP Mail SMTP is the most popular SMTP plugin."
609
  msgstr ""
610
 
611
+ #: src/Admin/Pages/SettingsTab.php:410
612
  msgid "We know that you will truly love WP Mail SMTP. It's used by over 1,000,000 websites."
613
  msgstr ""
614
 
615
+ #: src/Admin/Pages/SettingsTab.php:413
616
  msgid "Pro Features:"
617
  msgstr ""
618
 
619
+ #: src/Admin/Pages/SettingsTab.php:417
620
  msgid "Manage Notifications - control which emails your site sends"
621
  msgstr ""
622
 
623
+ #: src/Admin/Pages/SettingsTab.php:418
624
  msgid "Email Logging - keep track of every email sent from your site"
625
  msgstr ""
626
 
627
+ #: src/Admin/Pages/SettingsTab.php:419
628
  msgid "Office 365 - send emails using your Office 365 account"
629
  msgstr ""
630
 
631
+ #: src/Admin/Pages/SettingsTab.php:420
632
  msgid "Amazon SES - harness the power of AWS"
633
  msgstr ""
634
 
635
+ #: src/Admin/Pages/SettingsTab.php:421
636
  msgid "Outlook.com - send emails using your Outlook.com account"
637
  msgstr ""
638
 
639
+ #: src/Admin/Pages/SettingsTab.php:422
640
  msgid "Access to our world class support team"
641
  msgstr ""
642
 
643
+ #: src/Admin/Pages/SettingsTab.php:425
644
  msgid "White Glove Setup - sit back and relax while we handle everything for you"
645
  msgstr ""
646
 
647
+ #: src/Admin/Pages/SettingsTab.php:426
648
  msgid "Install WP Mail SMTP Pro plugin"
649
  msgstr ""
650
 
651
+ #: src/Admin/Pages/SettingsTab.php:427
652
  msgid "Set up domain name verification (DNS)"
653
  msgstr ""
654
 
655
+ #: src/Admin/Pages/SettingsTab.php:428
656
  msgid "Configure Mailgun service"
657
  msgstr ""
658
 
659
+ #: src/Admin/Pages/SettingsTab.php:429
660
  msgid "Set up WP Mail SMTP Pro plugin"
661
  msgstr ""
662
 
663
+ #: src/Admin/Pages/SettingsTab.php:430
664
  msgid "Test and verify email delivery"
665
  msgstr ""
666
 
667
  #. translators: %s - WPMailSMTP.com URL.
668
+ #: src/Admin/Pages/SettingsTab.php:438
669
  msgid "<a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\">Get WP Mail SMTP Pro Today and Unlock all the Powerful Features &raquo;</a>"
670
  msgstr ""
671
 
672
+ #: src/Admin/Pages/SettingsTab.php:456
673
  msgid "<strong>Bonus:</strong> WP Mail SMTP users get <span class=\"price-off\">20% off regular price</span>, automatically applied at checkout."
674
  msgstr ""
675
 
1450
  msgstr ""
1451
 
1452
  #: src/Providers/Pepipost/Options.php:25
1453
+ msgid "Pepipost SMTP"
1454
+ msgstr ""
1455
+
1456
+ #. translators: %1$s - URL to pepipost.com site.
1457
+ #: src/Providers/PepipostAPI/Options.php:31
1458
+ msgid "<strong><a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">Pepipost</a> is a recommended transactional email service.</strong> Every month Pepipost delivers over 8 billion emails from 20,000+ customers. Their mission is to reliably send emails in the most efficient way and at the most disruptive pricing ever. Pepipost provides users 30,000 free emails the first 30 days, then 100 emails per day."
1459
+ msgstr ""
1460
+
1461
+ #. translators: %1$s - URL to wpmailsmtp.com doc.
1462
+ #: src/Providers/PepipostAPI/Options.php:34
1463
+ msgid "Read our <a href=\"%2$s\" target=\"_blank\" rel=\"noopener noreferrer\">Pepipost documentation</a> to learn how to configure Pepipost and improve your email deliverability."
1464
+ msgstr ""
1465
+
1466
+ #: src/Providers/PepipostAPI/Options.php:53
1467
+ msgid "Get Pepipost Now (Free)"
1468
+ msgstr ""
1469
+
1470
+ #: src/Providers/PepipostAPI/Options.php:61
1471
  msgid "Pepipost"
1472
  msgstr ""
1473
 
1474
+ #: src/Providers/PepipostAPI/Options.php:88
1475
+ #: src/Providers/Sendgrid/Options.php:57
1476
+ #: src/Providers/Sendinblue/Options.php:88
1477
+ msgid "API Key"
1478
+ msgstr ""
1479
+
1480
+ #. translators: %s - pepipost.com link to get an API Key.
1481
+ #. translators: %s - sendinblue.com link to get an API Key.
1482
+ #: src/Providers/PepipostAPI/Options.php:107
1483
+ #: src/Providers/Sendinblue/Options.php:107
1484
+ msgid "Follow this link to get an API Key: %s."
1485
+ msgstr ""
1486
+
1487
+ #: src/Providers/PepipostAPI/Options.php:109
1488
+ msgid "Get the API Key"
1489
+ msgstr ""
1490
+
1491
  #: src/Providers/Sendgrid/Options.php:25
1492
  msgid "SendGrid"
1493
  msgstr ""
1497
  msgid "%1$sSendGrid%2$s is one of the leading transactional email services, sending over 35 billion emails every month. They provide users 100 free emails per day.<br><br>Read our %3$sSendGrid documentation%4$s to learn how to set up SendGrid and improve your email deliverability."
1498
  msgstr ""
1499
 
 
 
 
 
 
1500
  #. translators: %s - API key link.
1501
  #: src/Providers/Sendgrid/Options.php:76
1502
  msgid "Follow this link to get an API Key from SendGrid: %s."
1513
 
1514
  #. translators: %1$s - URL to sendinblue.com site.
1515
  #: src/Providers/Sendinblue/Options.php:31
1516
+ msgid "<strong><a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">Sendinblue</a> is a recommended transactional email service.</strong> Founded in 2012, they serve 80,000+ growing companies around the world and send over 30 million emails each day. They understand that transactional emails are the heart of your customer relationships. Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day."
1517
  msgstr ""
1518
 
1519
  #. translators: %2$s - URL to wpmailsmtp.com doc.
1529
  msgid "Sendinblue"
1530
  msgstr ""
1531
 
 
 
 
 
 
1532
  #: src/Providers/Sendinblue/Options.php:109
1533
  msgid "Get v3 API Key"
1534
  msgstr ""
1692
  msgid "Your site is running an outdated version of PHP that is no longer supported and may cause issues with %1$s. %2$sRead more%3$s for additional information."
1693
  msgstr ""
1694
 
1695
+ #: wp_mail_smtp.php:131
1696
  msgid "Please deactivate the free version of the WP Mail SMTP plugin before activating WP Mail SMTP Pro."
1697
  msgstr ""
readme.txt CHANGED
@@ -1,377 +1,395 @@
1
- === WP Mail SMTP by WPForms ===
2
- Contributors: wpforms, jaredatch, smub, slaFFik
3
- Tags: smtp, wp mail smtp, wordpress smtp, gmail smtp, sendgrid smtp, mailgun smtp, mail, mailer, phpmailer, wp_mail, email, mailgun, sengrid, gmail, wp smtp
4
- Requires at least: 4.9
5
- Tested up to: 5.3
6
- Stable tag: 1.7.1
7
- Requires PHP: 5.3
8
-
9
- The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million sites.
10
-
11
- == Description ==
12
-
13
- ### WordPress Mail SMTP Plugin
14
-
15
- Having problems with your WordPress site not sending emails? You're not alone. Over 1 million websites use WP Mail SMTP to send their emails reliably.
16
-
17
- Our goal is to make email deliverability easy and reliable. We want to ensure your emails reach the inbox.
18
-
19
- WP Mail SMTP fixes your email deliverability by reconfiguring WordPress to use a proper SMTP provider when sending emails.
20
-
21
- = What is SMTP? =
22
-
23
- SMTP (Simple Mail Transfer Protocol) is an industry standard for sending emails. Proper SMTP configuration helps increase email deliverability by using authentication.
24
-
25
- Popular email clients like Gmail, Yahoo, and Office 365 are in a constant battle with email spammers. One of the things they look at is if an email is originating from the location it claims to be originating from.
26
-
27
- If the proper authentication isn't there, then emails either go in the SPAM folder or worst, don't get delivered at all.
28
-
29
- This is a problem for a lot of WordPress sites because by default, WordPress uses the PHP mail function to send emails generated by WordPress or any contact form plugin like <a href="https://wpforms.com/" rel="friend">WPForms</a>.
30
-
31
- The issue is that most <a href"http://www.wpbeginner.com/wordpress-hosting/" rel="friend">WordPress hosting companies</a> don't have their servers properly configured for sending PHP emails.
32
-
33
- The combination of two causes your WordPress emails to not get delivered.
34
-
35
- = How does WP Mail SMTP work? =
36
-
37
- WP Mail SMTP plugin easily resolves email delivery problems by improving and changing how your WordPress site sends email. We reconfigure the `wp_mail()` function to either use proper SMTP host credentials or leverage a built-in SMTP mail provider.
38
-
39
- When using one of our built-in SMTP mail provider integrations (recommended), emails are sent using the provider's direct API. This means even if your web host is blocking SMTP ports, your emails still send successfully.
40
-
41
- This helps you fix all WordPress not sending email issues.
42
-
43
- WP Mail SMTP plugin includes many different SMTP setup options:
44
-
45
- 1. Sendinblue SMTP <strong>(Recommended)</strong>
46
- 2. Mailgun SMTP
47
- 3. SendGrid SMTP
48
- 4. Gmail SMTP
49
- 5. Microsoft SMTP (Outlook.com and Office 365) <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">[Pro]</a>
50
- 6. Amazon SES SMTP <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">[Pro]</a>
51
- 7. All Other SMTP
52
-
53
- For all options, you can specify the "from name" and "email address" for outgoing emails.
54
-
55
- Instead of having users use different SMTP plugins and workflows for different SMTP providers, we decided to bring it all in one. This is what makes WP Mail SMTP, the best SMTP solution for WordPress.
56
-
57
- = Sendinblue SMTP =
58
-
59
- Sendinblue is our recommended transactional email service.
60
-
61
- They serve 80,000+ growing companies around the world and send over 30 million emails each day.
62
-
63
- Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day.
64
-
65
- Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendinblue-mailer-in-wp-mail-smtp/" rel="friend">Sendinblue documentation</a> for more details.
66
-
67
- = Mailgun SMTP =
68
-
69
- Mailgun SMTP is a popular SMTP service provider that allows you to send large quantities of emails. They allow you to send your first 10,000 emails for free every month.
70
-
71
- WP Mail SMTP plugin offers a native integration with MailGun. All you have to do is connect your Mailgun account, and you will improve your email deliverability.
72
-
73
- Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-mailgun-mailer-in-wp-mail-smtp/" rel="friend">Mailgun documentation</a> for more details.
74
-
75
- = SendGrid SMTP =
76
-
77
- SendGrid has a free SMTP plan that you can use to send up to 100 emails per day. With our native SendGrid SMTP integration, you can easily and securely set up SendGrid SMTP on your WordPress site.
78
-
79
- Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendgrid-mailer-in-wp-mail-smtp/" rel="friend">SendGrid documentation</a> for more details.
80
-
81
- = Gmail SMTP =
82
-
83
- Often bloggers and small business owners don't want to use third-party SMTP services. Well you can use your Gmail or G Suite account for SMTP emails.
84
-
85
- This allows you to use your <a href="http://www.wpbeginner.com/beginners-guide/how-to-setup-a-professional-email-address-with-gmail-and-google-apps/" rel="friend">professional email address</a> and improve email deliverability.
86
-
87
- Unlike other Gmail SMTP plugins, our Gmail SMTP option uses OAuth to authenticate your Google account, keeping your login information 100% secure.
88
-
89
- Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/" rel="friend">Gmail documentation</a> for more details.
90
-
91
- = Microsoft SMTP (Outlook.com and Office 365) =
92
-
93
- Many business use Outlook.com or Office 365 to their to power their email. For those users, the Microsoft mailer can be a great option. This integration allows you to use your existing Outlook.com or Office 365 account to send your emails reliably.
94
-
95
- = Amazon SES SMTP =
96
-
97
- Advanced or technical users can harness the power of Amazon AWS (Amazon Web Services) with the Amazon SES mailer. With this integration, you can send a high volume of emails at a very reasonable rate.
98
-
99
- Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-amazon-ses-mailer-in-wp-mail-smtp/" rel="friend">Amazon SES documentation</a> for more details.
100
-
101
- = Other SMTP =
102
-
103
- WP Mail SMTP plugin also works with all major email services such as Gmail, Yahoo, Outlook, Microsoft Live, and any other email sending service that offers SMTP.
104
-
105
- You can set the following options:
106
-
107
- * Specify an SMTP host.
108
- * Specify an SMTP port.
109
- * Choose SSL / TLS encryption.
110
- * Choose to use SMTP authentication or not.
111
- * Specify an SMTP username and password.
112
-
113
- To see recommended settings for the popular services as well as troubleshooting tips, check out our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-other-smtp-mailer-in-wp-mail-smtp/" rel="friend">SMTP documentation</a>.
114
-
115
- We hope that you find WP Mail SMTP plugin helpful!
116
-
117
- ### WP Mail SMTP PRO
118
-
119
- In addition to native Microsoft and Amazon SES integrations, WP Mail SMTP Pro provides access to many other powerful features and services.
120
-
121
- <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">Click here to purchase WP Mail SMTP Pro now!</a>
122
-
123
- = Email Log =
124
-
125
- Email Logging lets you log and view all emails sent from your site. Email logs are helpful for storing emails for your records, auditing outgoing emails, and debugging during site development.
126
-
127
- = Manage WordPress Emails and Notifications =
128
-
129
- The Manage Notification feature gives you full control over which email notifications WordPress sends. This means you can disable different WordPress notification emails. Don't want to receive emails when new users are created? No problem, turn it off.
130
-
131
- = Expert Support =
132
-
133
- We provide <a href="https://wordpress.org/support/topic/wp-mail-smtp-support-policy/">limited support</a> for the WP Mail SMTP plugin on the WordPress.org forums. Access to our world class one-on-one email support is available to <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">WP Mail SMTP Pro</a> users.
134
-
135
- = White Glove Setup =
136
-
137
- Our White Glove Setup service is a great option that anyone can benefit from. Whether you don't have the time or maybe you feel a bit in over your head - we've got you covered.
138
-
139
- You can sit back and relax while we set up everything for you. White glove setup includes WP Mail SMTP plugin installation and setup, configuration adjustments to your DNS for proper email domain name verification, Mailgun setup, and final testing to confirm everything is passing with flying colors.
140
-
141
- ### Security
142
-
143
- The WP Mail SMTP team takes security very seriously. Not only does the plugin follow all security best practices, but we have several options available to ensure your site is safe and secure.
144
-
145
- - Direct SMTP mailer integrations (recommended), such as Google and Mailgun, use the official provider APIs. This means you never enter your username or password in the plugin settings and these credentials are not stored in the database. Instead, we use tokens or API keys which are much more secure.
146
-
147
- - When using Other SMTP mailer, we provide the option to insert your password in your `wp-config.php` file, so it's not visible in your WordPress settings or saved in the database.
148
-
149
- ### Credits
150
-
151
- WP Mail SMTP plugin was originally created by Callum Macdonald. It is now owned and maintained by the team behind <a href="https://wpforms.com/" rel="friend">WPForms</a> - the best drag & drop form builder for WordPress.
152
-
153
- You can try the <a href="https://wordpress.org/plugins/wpforms-lite/" rel="friend">free version of WPForms plugin</a> to see why it's the best in the market.
154
-
155
- ### What's Next
156
-
157
- If you like this plugin, then consider checking out our other projects:
158
-
159
- * <a href="https://optinmonster.com/" rel="friend" title="OptinMonster">OptinMonster</a> - Get More Email Subscribers with the most popular conversion optimization plugin for WordPress.
160
- * <a href="https://www.monsterinsights.com/" rel="friend" title="MonsterInsights">MonsterInsights</a> - See the Stats that Matter and Grow Your Business with Confidence. Best Google Analytics Plugin for WordPress.
161
- * <a href="https://www.seedprod.com/" rel="friend" title="SeedProd">SeedProd</a> - Jumpstart your website with the #1 Coming Soon & Maintenance Mode Plugin for WordPress.
162
-
163
- Visit <a href="http://www.wpbeginner.com/" rel="friend" title="WPBeginner">WPBeginner</a> to learn from our <a href="http://www.wpbeginner.com/category/wp-tutorials/" rel="friend" title="WordPress Tutorials">WordPress Tutorials</a> and find out about other <a href="http://www.wpbeginner.com/category/plugins/" rel="friend" title="Best WordPress Plugins">best WordPress plugins</a>.
164
-
165
- == Installation ==
166
-
167
- 1. Install WP Mail SMTP by WPForms either via the WordPress.org plugin repository or by uploading the files to your server. (See instructions on <a href="http://www.wpbeginner.com/beginners-guide/step-by-step-guide-to-install-a-wordpress-plugin-for-beginners/" rel="friend">how to install a WordPress plugin</a>)
168
- 2. Activate WP Mail SMTP by WPForms.
169
- 3. Navigate to the Settings area of WP Mail SMTP in the WordPress admin.
170
- 4. Choose your SMTP option (Mailgun SMTP, SendGrid SMTP, Gmail SMTP, or Other SMTP) and follow the instructions to set it up.
171
- 5. Need more help? Get support with <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend" title="WPForms">WP Mail SMTP PRO</a>.
172
-
173
- == Frequently Asked Questions ==
174
-
175
- = Can I use this plugin to send email via Gmail, G Suite, Outlook.com, Office 365, Hotmail, Yahoo, or AOL SMTP? =
176
-
177
- Yes! We have extensive documentation that covers setting up SMTP most popular email services.
178
-
179
- <a href="https://wpforms.com/docs/how-to-set-up-smtp-using-the-wp-mail-smtp-plugin/" rel="friend">Read our docs</a> to see the correct SMTP settings for each service.
180
-
181
- = Help! I need support or have an issue. =
182
-
183
- Please read <a href="https://wordpress.org/support/topic/wp-mail-smtp-support-policy/">our support policy</a> for more information.
184
-
185
- Limited support is available for WP Mail SMTP users via WordPress.org support forums.
186
-
187
- Email support and set up assistance is available to WP Mail SMTP Pro users.
188
-
189
- = I found a bug, now what? =
190
-
191
- If you've stumbled upon a bug, the best place to report it is in the <a href="https://github.com/awesomemotive/wp-mail-smtp">WP Mail SMTP GitHub repository</a>. GitHub is where the plugin is actively developed, and posting there will get your issue quickly seen by our developers (myself and Slava). Once posted, we'll review your bug report and triage the bug. When creating an issue, the more details you can add to your report, the faster the bug can be solved.
192
-
193
- = Can you add feature x, y or z to the plugin? =
194
-
195
- Short answer: maybe.
196
-
197
- By all means please contact us to discuss features or options you'd like to see added to the plugin. We can't guarantee to add all of them, but we will consider all sensible requests. We can be contacted here:
198
- <a href="https://wpmailsmtp.com/contact/" rel="friend">https://wpmailsmtp.com/contact/</a>
199
-
200
- == Screenshots ==
201
-
202
- 1. WP Mail SMTP Settings page
203
- 2. Gmail / G Suite settings
204
- 3. Mailgun settings
205
- 4. SendGrid settings
206
- 5. SMTP settings
207
- 6. Send a Test Email
208
-
209
- == Changelog ==
210
-
211
- = 1.7.1 - 2019-11-11 =
212
- * Fixed: Compatibility with WordPress 5.3.
213
- * Fixed: `Processor::get_default_email()` always returns empty value when server incorrectly configured.
214
-
215
- = 1.7.0 - 2019-10-24 =
216
- * Added: Add a new constant `WPMS_DO_NOT_SEND` to block email sending.
217
- * Fixed: Default email (wordpress@example.com) rewriting in CLI mode.
218
- * Fixed: Incorrect conflicts detection with certain plugins.
219
- * Fixed: various typos in plugin settings.
220
-
221
- = 1.6.2 - 2019-09-02 =
222
- * Fixed: Race condition when loading with certain plugins, that send emails very early. Makes email delivery more reliable.
223
-
224
- = 1.6.0 - 2019-08-21 =
225
- * Added: New transactional mailer: Sendinblue.
226
- * Added: Educate users to use transactional mailers for better deliverability.
227
- * Added: New option and filter to disable admin area delivery error notices.
228
- * Changed: Hide private API key saved in the DB for API based mailers using `input[type=password]`.
229
- * Changed: Update links to various docs, pointing now to https://wpmailsmtp.com.
230
-
231
- = 1.5.2 - 2019-07-18 =
232
- * Fixed: "Redirect URI mismatch" error for "Gmail" mailer when trying to re-authorize an account that was initially created with version < v1.5.0.
233
- * Changed: Make "Authentication" setting in "Other SMTP" mailer ON by default for new users.
234
- * Changed: Mailers docs links now point to wpmailsmtp.com own site.
235
-
236
- = 1.5.1 - 2019-07-12 =
237
- * Fixed: Duplicated emails sent to the first recipient in a loop (and others not receiving their emails).
238
-
239
- = 1.5.0 - 2019-07-09 =
240
- * Added: Loсo plugin support.
241
- * Added: "About us" admin area page.
242
- * Added: Display in debug output a possible conflicting plugin existence.
243
- * Added: Lots of actions and filters to improve flexibility of the plugin.
244
- * Changed: Plugin menu is now top level.
245
- * Changed: Hide secrets/API keys in page DOM in plugin admin area.
246
- * Changed: Do not save constant values into the database when plugin settings are saved.
247
- * Changed: Lots of i18n improvements to support translation for both free and paid version of the plugin.
248
- * Changed: Gmail mailer - allow to change From Name email header.
249
- * Changed: Gmail mailer - display email used to create a connection.
250
- * Changed: WordPress 4.9 is the minimum WordPress version we support.
251
- * Fixed: X-Mailer header should be present in all emails.
252
- * Fixed: PHP notices when migrating under certain circumstances from 0.x version of the plugin.
253
- * Fixed: Options::get_group() now supports values set via constants.
254
-
255
- = 1.4.2 - 2019-03-23 =
256
- * Changed: Tested up to WordPress 5.1.x.
257
- * Changed: Removed TGMPA library.
258
-
259
- = 1.4.1 - 2018-12-03 =
260
- * Fixed: correctly process backslashes in SMTP passwords defined via constants.
261
- * Changed: allow to send a Test Email when Default (none) mailer is selected in plugin settings.
262
-
263
- = 1.4.0 - 2018-11-29 =
264
- * Added: New option: Do Not Send - block emails from being sent.
265
- * Added: New option: Send HTML or plain text emails when doing an Email Test.
266
- * Added: New option: Mailgun region selection - US and EU (US is default to preserve compatibility).
267
- * Fixed: Compatibility with WordPress 3.6+.
268
- * Fixed: Compatibility with WordPress 5.0.
269
- * Fixed: Constants usage is much more reliable now, works correctly on Multisite. Constants are global accross the whole network.
270
- * Fixed: Preserve multipart emails when using Sendgrid/Mailgun mailers (were converted to HTML-only).
271
- * Fixed: Security hardening.
272
- * Changed: Prefill Email Test page From field with currently logged in user email.
273
- * Changed: Update libraries: google/apiclient-services, google/auth, phpseclib/phpseclib and their dependecies.
274
- * Changed: Display in debug output cURL version if Gmail mailing failed.
275
- * Changed: Display in debug output OpenSSL version if it exists if Gmail/SMTP mailing failed.
276
- * Changed: Display plugin version in dashboard error notice when emailing failed.
277
- * Changed: Do not allow to send Test Email if mailer not configured properly.
278
- * Changed: Notify in plugin admin area that Gmail doesn't allow to redefine From Name/Email etc.
279
- * Changed: List all constants with descriptions in plugin main file: wp_mail_smtp.php.
280
- * Changed: TGMPA: change descriptions from "Required" to "Recommended" (labels were incorrect).
281
-
282
- = 1.3.3 - 2018-07-05 =
283
- * Fixed: Compatibility with other plugins, that are using Google Service or Google Client classes.
284
- * Changed: Optimize code loading.
285
-
286
- = 1.3.2 - 2018-06-29 =
287
- * Make sure that other plugins/themes are not conflicting with our TGMPA library.
288
-
289
- = 1.3.1 - 2018-06-29 =
290
- * Fixed: Other SMTP: Clear new Debug messages about failed email delivery on next successful email sending.
291
- * Fixed: Introduce conditional autoloader to workaround Gmail PHP 5.5 requirement and its library compatibility issues vs PHP 5.3+ minimum viable plugin version.
292
-
293
- = 1.3.0 - 2018-06-28 =
294
- * Added: New option: force From Email rewrite regardless of the current value.
295
- * Added: New option: force From Name rewrite regardless of the current value.
296
- * Added: New option: remove all plugin data on plugin uninstall (when user deletes it).
297
- * Added: Notify site admins in wp-admin area with a notice about last failed email delivery. Cleans up on successful delivery.
298
- * Added: Notify site admins in wp-admin area with a notice about possible compatibility issues with other SMTP and email delivery plugins.
299
- * Added: Improve User Debug Experience when doing Email Test - display helpful description and steps to fix the issue.
300
- * Added: New users: provide default SMTP Port value for new users based on Encryption selection.
301
- * Added: New users: notify about not configured plugin settings.
302
- * Added: New users: Recommend free WPForms Lite plugin for those who don't have it.
303
- * Added: SendGrid/Mailgun: provide support for multipart/alternative types of emails.
304
- * Added: Gmail: new button to remove connection and to connect a new Google account.
305
- * Fixed: Support plugin installation into /mu-plugins/ directory.
306
- * Fixed: SendGrid: required text/plain part of email being the first one - fixes plain text emails not having links.
307
- * Fixed: SendGrid and Mailgun: improperly sending plain text emails in html format.
308
- * Fixed: SMTP Debug output was empty in some cases.
309
- * Fixed: Compatibility with lots of other plugins that use Google Analytics library of different versions.
310
- * Fixed: "client_id is empty" is no more a problem, should be fixed.
311
- * Changed: For SendGrid and Mailgun allow using custom defined attachments names if present. Fallback to file name.
312
- * Changed: Gmail: switch to a wider scope to prevent possible issues in certain circumstances.
313
- * Changed: Remove whitespaces start/end of keys, secrets etc.
314
- * Changed: Improved helpful description tests of various options.
315
- * Changed: Improved plugin autoloading functionality.
316
-
317
- = 1.2.5 - 2017-02-05 =
318
- * Fixed: `Return path` can't be turned off.
319
- * Fixed: `Authentication` sometimes can't be turned off.
320
- * Fixed: `Auto TLS` sometimes can't be turned off.
321
- * Fixed: BCC support for Gmail was broken.
322
- * Fixed: Debug output improved to handle SELinux and grsecurity.
323
- * Fixed: Strip slashes from plugin settings (useful for `From Name` option).
324
- * Fixed: Change the way sanitization is done to prevent accidental removal of useful data.
325
- * Fixed: Plugin activation will not overwrite settings back to defaults.
326
- * Fixed: Properly set `Auto TLS` option on plugin activation.
327
- * Fixed: Providers autoloading improved for certain Windows-based installs.
328
- * Fixed: Use the proper path to load translations from plugin's `/languages` directory.
329
- * Changed: Do not autoload on each page request plugin settings from WordPress options table.
330
- * Changed: Do not autoload Pepipost classes unless it's saved as active mailer in settings.
331
-
332
- = 1.2.4 - 2017-01-28 =
333
- * Fixed: Improved escaping in debug reporting.
334
-
335
- = 1.2.3 - 2017-01-22 =
336
- * Fixed: Gmail tokens were reset after clicking Save Settings.
337
- * Fixed: Slight typo in Gmail success message.
338
-
339
- = 1.2.2 - 2017-12-27 =
340
- * Fixed: Correctly handle Mailgun debug message for an incorrect api key.
341
- * Fixed: Fatal error for Gmail and SMTP mailers with Nginx web-server (without Apache at all).
342
- * Changed: Update X-Mailer emails header to show the real sender with a mailer and plugin version.
343
-
344
- = 1.2.1 - 2017-12-21 =
345
- * Fixed: Failed SMTP connections generate fatal errors.
346
-
347
- = 1.2.0 - 2017-12-21 =
348
- * Fixed: Decrease the factual minimum WordPress version from 3.9 to 3.6.
349
- * Changed: Improve debug output for all mail providers.
350
-
351
- = 1.1.0 - 2017-12-18 =
352
- * Added: New option "Auto TLS" for SMTP mailer. Default is enabled. Migration routine for all sites.
353
- * Changed: Improve debug output - clear styles and context-aware content.
354
- * Changed: Better exceptions handling for Google authentication process.
355
- * Changed: Do not sanitize passwords, api keys etc - as they may contain special characters in certain order and sanitization will break those values.
356
- * Changed: Improve wording of some helpful texts inside plugin admin area.
357
- * Fixed: Do not include certain files in dependency libraries that are not used by Google mailer. This should stop flagging plugin by Wordfence and VaultPress.
358
- * Fixed: Constants usage is working now, to define the SMTP password, for example.
359
- * Fixed: Notice for default mailer.
360
-
361
- = 1.0.2 - 2017-12-12 =
362
- * Fixed: PHPMailer using incorrect SMTPSecure value.
363
-
364
- = 1.0.1 - 2017-12-12 =
365
- * Fixed: Global POST processing conflict.
366
-
367
- = 1.0.0 - 2017-12-12 =
368
- * Added: Automatic migration tool to move options from older storage format to a new one.
369
- * Added: Added Gmail & G Suite email provider integration - without your email and password.
370
- * Added: Added SendGrid email provider integration - using the API key only.
371
- * Added: Added Mailgun email provider integration - using the API key and configured domain only.
372
- * Added: New compatibility mode - for PHP 5.2 old plugin will be loaded, for PHP 5.3 and higher - new version of admin area and new functionality.
373
- * Changed: The new look of the admin area.
374
- * Changed: SMTP password field now has "password" type.
375
- * Changed: SMTP password field does not display real password at all when using constants in `wp-config.php` to define it.
376
- * Changed: Escape properly all translations.
377
- * Changed: More helpful test email content (with a mailer name).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Mail SMTP by WPForms ===
2
+ Contributors: wpforms, jaredatch, smub, slaFFik
3
+ Tags: smtp, wp mail smtp, wordpress smtp, gmail smtp, sendgrid smtp, mailgun smtp, mail, mailer, phpmailer, wp_mail, email, mailgun, sengrid, gmail, wp smtp
4
+ Requires at least: 4.9
5
+ Tested up to: 5.3
6
+ Stable tag: 1.8.0
7
+ Requires PHP: 5.3
8
+
9
+ The most popular WordPress SMTP and PHP Mailer plugin. Trusted by over 1 million sites.
10
+
11
+ == Description ==
12
+
13
+ ### WordPress Mail SMTP Plugin
14
+
15
+ Having problems with your WordPress site not sending emails? You're not alone. Over 1 million websites use WP Mail SMTP to send their emails reliably.
16
+
17
+ Our goal is to make email deliverability easy and reliable. We want to ensure your emails reach the inbox.
18
+
19
+ WP Mail SMTP fixes your email deliverability by reconfiguring WordPress to use a proper SMTP provider when sending emails.
20
+
21
+ = What is SMTP? =
22
+
23
+ SMTP (Simple Mail Transfer Protocol) is an industry standard for sending emails. Proper SMTP configuration helps increase email deliverability by using authentication.
24
+
25
+ Popular email clients like Gmail, Yahoo, and Office 365 are in a constant battle with email spammers. One of the things they look at is if an email is originating from the location it claims to be originating from.
26
+
27
+ If the proper authentication isn't there, then emails either go in the SPAM folder or worst, don't get delivered at all.
28
+
29
+ This is a problem for a lot of WordPress sites because by default, WordPress uses the PHP mail function to send emails generated by WordPress or any contact form plugin like <a href="https://wpforms.com/" rel="friend">WPForms</a>.
30
+
31
+ The issue is that most <a href"http://www.wpbeginner.com/wordpress-hosting/" rel="friend">WordPress hosting companies</a> don't have their servers properly configured for sending PHP emails.
32
+
33
+ The combination of two causes your WordPress emails to not get delivered.
34
+
35
+ = How does WP Mail SMTP work? =
36
+
37
+ WP Mail SMTP plugin easily resolves email delivery problems by improving and changing how your WordPress site sends email. We reconfigure the `wp_mail()` function to either use proper SMTP host credentials or leverage a built-in SMTP mail provider.
38
+
39
+ When using one of our built-in SMTP mail provider integrations (recommended), emails are sent using the provider's direct API. This means even if your web host is blocking SMTP ports, your emails still send successfully.
40
+
41
+ This helps you fix all WordPress not sending email issues.
42
+
43
+ WP Mail SMTP plugin includes many different SMTP setup options:
44
+
45
+ 1. Pepipost SMTP <strong>(Recommended)</strong>
46
+ 2. Sendinblue SMTP <strong>(Recommended)</strong>
47
+ 3. Mailgun SMTP
48
+ 4. SendGrid SMTP
49
+ 5. Gmail SMTP
50
+ 6. Microsoft SMTP (Outlook.com and Office 365) <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">[Pro]</a>
51
+ 7. Amazon SES SMTP <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">[Pro]</a>
52
+ 8. All Other SMTP
53
+
54
+ For all options, you can specify the "from name" and "email address" for outgoing emails.
55
+
56
+ Instead of having users use different SMTP plugins and workflows for different SMTP providers, we decided to bring it all in one. This is what makes WP Mail SMTP, the best SMTP solution for WordPress.
57
+
58
+ = Pepipost SMTP =
59
+
60
+ Pepipost is a recommended transactional email service.
61
+
62
+ Every month they delivers over 8 billion emails from 20,000+ customers.
63
+
64
+ Their mission is to reliably send emails in the most efficient way and at the most disruptive pricing ever.
65
+ Pepipost provides users 30,000 emails the first 30 days, then 100 emails per day.
66
+
67
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-pepipost-mailer-in-wp-mail-smtp/" rel="friend">Pepipost documentation</a> for more details.
68
+
69
+ = Sendinblue SMTP =
70
+
71
+ Sendinblue is a recommended transactional email service.
72
+
73
+ They serve 80,000+ growing companies around the world and send over 30 million emails each day.
74
+
75
+ Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day.
76
+
77
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendinblue-mailer-in-wp-mail-smtp/" rel="friend">Sendinblue documentation</a> for more details.
78
+
79
+ = Mailgun SMTP =
80
+
81
+ Mailgun SMTP is a popular SMTP service provider that allows you to send large quantities of emails. They allow you to send your first 10,000 emails for free every month.
82
+
83
+ WP Mail SMTP plugin offers a native integration with MailGun. All you have to do is connect your Mailgun account, and you will improve your email deliverability.
84
+
85
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-mailgun-mailer-in-wp-mail-smtp/" rel="friend">Mailgun documentation</a> for more details.
86
+
87
+ = SendGrid SMTP =
88
+
89
+ SendGrid has a free SMTP plan that you can use to send up to 100 emails per day. With our native SendGrid SMTP integration, you can easily and securely set up SendGrid SMTP on your WordPress site.
90
+
91
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-sendgrid-mailer-in-wp-mail-smtp/" rel="friend">SendGrid documentation</a> for more details.
92
+
93
+ = Gmail SMTP =
94
+
95
+ Often bloggers and small business owners don't want to use third-party SMTP services. Well you can use your Gmail or G Suite account for SMTP emails.
96
+
97
+ This allows you to use your <a href="http://www.wpbeginner.com/beginners-guide/how-to-setup-a-professional-email-address-with-gmail-and-google-apps/" rel="friend">professional email address</a> and improve email deliverability.
98
+
99
+ Unlike other Gmail SMTP plugins, our Gmail SMTP option uses OAuth to authenticate your Google account, keeping your login information 100% secure.
100
+
101
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-gmail-mailer-in-wp-mail-smtp/" rel="friend">Gmail documentation</a> for more details.
102
+
103
+ = Microsoft SMTP (Outlook.com and Office 365) =
104
+
105
+ Many business use Outlook.com or Office 365 to their to power their email. For those users, the Microsoft mailer can be a great option. This integration allows you to use your existing Outlook.com or Office 365 account to send your emails reliably.
106
+
107
+ = Amazon SES SMTP =
108
+
109
+ Advanced or technical users can harness the power of Amazon AWS (Amazon Web Services) with the Amazon SES mailer. With this integration, you can send a high volume of emails at a very reasonable rate.
110
+
111
+ Read our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-amazon-ses-mailer-in-wp-mail-smtp/" rel="friend">Amazon SES documentation</a> for more details.
112
+
113
+ = Other SMTP =
114
+
115
+ WP Mail SMTP plugin also works with all major email services such as Gmail, Yahoo, Outlook, Microsoft Live, and any other email sending service that offers SMTP.
116
+
117
+ You can set the following options:
118
+
119
+ * Specify an SMTP host.
120
+ * Specify an SMTP port.
121
+ * Choose SSL / TLS encryption.
122
+ * Choose to use SMTP authentication or not.
123
+ * Specify an SMTP username and password.
124
+
125
+ To see recommended settings for the popular services as well as troubleshooting tips, check out our <a href="https://wpmailsmtp.com/docs/how-to-set-up-the-other-smtp-mailer-in-wp-mail-smtp/" rel="friend">SMTP documentation</a>.
126
+
127
+ We hope that you find WP Mail SMTP plugin helpful!
128
+
129
+ ### WP Mail SMTP PRO
130
+
131
+ In addition to native Microsoft and Amazon SES integrations, WP Mail SMTP Pro provides access to many other powerful features and services.
132
+
133
+ <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">Click here to purchase WP Mail SMTP Pro now!</a>
134
+
135
+ = Email Log =
136
+
137
+ Email Logging lets you log and view all emails sent from your site. Email logs are helpful for storing emails for your records, auditing outgoing emails, and debugging during site development.
138
+
139
+ = Manage WordPress Emails and Notifications =
140
+
141
+ The Manage Notification feature gives you full control over which email notifications WordPress sends. This means you can disable different WordPress notification emails. Don't want to receive emails when new users are created? No problem, turn it off.
142
+
143
+ = Expert Support =
144
+
145
+ We provide <a href="https://wordpress.org/support/topic/wp-mail-smtp-support-policy/">limited support</a> for the WP Mail SMTP plugin on the WordPress.org forums. Access to our world class one-on-one email support is available to <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend">WP Mail SMTP Pro</a> users.
146
+
147
+ = White Glove Setup =
148
+
149
+ Our White Glove Setup service is a great option that anyone can benefit from. Whether you don't have the time or maybe you feel a bit in over your head - we've got you covered.
150
+
151
+ You can sit back and relax while we set up everything for you. White glove setup includes WP Mail SMTP plugin installation and setup, configuration adjustments to your DNS for proper email domain name verification, Mailgun setup, and final testing to confirm everything is passing with flying colors.
152
+
153
+ ### Security
154
+
155
+ The WP Mail SMTP team takes security very seriously. Not only does the plugin follow all security best practices, but we have several options available to ensure your site is safe and secure.
156
+
157
+ - Direct SMTP mailer integrations (recommended), such as Google and Mailgun, use the official provider APIs. This means you never enter your username or password in the plugin settings and these credentials are not stored in the database. Instead, we use tokens or API keys which are much more secure.
158
+
159
+ - When using Other SMTP mailer, we provide the option to insert your password in your `wp-config.php` file, so it's not visible in your WordPress settings or saved in the database.
160
+
161
+ ### Credits
162
+
163
+ WP Mail SMTP plugin was originally created by Callum Macdonald. It is now owned and maintained by the team behind <a href="https://wpforms.com/" rel="friend">WPForms</a> - the best drag & drop form builder for WordPress.
164
+
165
+ You can try the <a href="https://wordpress.org/plugins/wpforms-lite/" rel="friend">free version of WPForms plugin</a> to see why it's the best in the market.
166
+
167
+ ### What's Next
168
+
169
+ If you like this plugin, then consider checking out our other projects:
170
+
171
+ * <a href="https://optinmonster.com/" rel="friend" title="OptinMonster">OptinMonster</a> - Get More Email Subscribers with the most popular conversion optimization plugin for WordPress.
172
+ * <a href="https://www.monsterinsights.com/" rel="friend" title="MonsterInsights">MonsterInsights</a> - See the Stats that Matter and Grow Your Business with Confidence. Best Google Analytics Plugin for WordPress.
173
+ * <a href="https://www.seedprod.com/" rel="friend" title="SeedProd">SeedProd</a> - Jumpstart your website with the #1 Coming Soon & Maintenance Mode Plugin for WordPress.
174
+
175
+ Visit <a href="http://www.wpbeginner.com/" rel="friend" title="WPBeginner">WPBeginner</a> to learn from our <a href="http://www.wpbeginner.com/category/wp-tutorials/" rel="friend" title="WordPress Tutorials">WordPress Tutorials</a> and find out about other <a href="http://www.wpbeginner.com/category/plugins/" rel="friend" title="Best WordPress Plugins">best WordPress plugins</a>.
176
+
177
+ == Installation ==
178
+
179
+ 1. Install WP Mail SMTP by WPForms either via the WordPress.org plugin repository or by uploading the files to your server. (See instructions on <a href="http://www.wpbeginner.com/beginners-guide/step-by-step-guide-to-install-a-wordpress-plugin-for-beginners/" rel="friend">how to install a WordPress plugin</a>)
180
+ 2. Activate WP Mail SMTP by WPForms.
181
+ 3. Navigate to the Settings area of WP Mail SMTP in the WordPress admin.
182
+ 4. Choose your SMTP option (Mailgun SMTP, SendGrid SMTP, Gmail SMTP, or Other SMTP) and follow the instructions to set it up.
183
+ 5. Need more help? Get support with <a href="https://wpmailsmtp.com/?utm_source=wprepo&utm_medium=link&utm_campaign=liteversion&utm_content=readme" rel="friend" title="WPForms">WP Mail SMTP PRO</a>.
184
+
185
+ == Frequently Asked Questions ==
186
+
187
+ = Can I use this plugin to send email via Gmail, G Suite, Outlook.com, Office 365, Hotmail, Yahoo, or AOL SMTP? =
188
+
189
+ Yes! We have extensive documentation that covers setting up SMTP most popular email services.
190
+
191
+ <a href="https://wpforms.com/docs/how-to-set-up-smtp-using-the-wp-mail-smtp-plugin/" rel="friend">Read our docs</a> to see the correct SMTP settings for each service.
192
+
193
+ = Help! I need support or have an issue. =
194
+
195
+ Please read <a href="https://wordpress.org/support/topic/wp-mail-smtp-support-policy/">our support policy</a> for more information.
196
+
197
+ Limited support is available for WP Mail SMTP users via WordPress.org support forums.
198
+
199
+ Email support and set up assistance is available to WP Mail SMTP Pro users.
200
+
201
+ = I found a bug, now what? =
202
+
203
+ If you've stumbled upon a bug, the best place to report it is in the <a href="https://github.com/awesomemotive/wp-mail-smtp">WP Mail SMTP GitHub repository</a>. GitHub is where the plugin is actively developed, and posting there will get your issue quickly seen by our developers (myself and Slava). Once posted, we'll review your bug report and triage the bug. When creating an issue, the more details you can add to your report, the faster the bug can be solved.
204
+
205
+ = Can you add feature x, y or z to the plugin? =
206
+
207
+ Short answer: maybe.
208
+
209
+ By all means please contact us to discuss features or options you'd like to see added to the plugin. We can't guarantee to add all of them, but we will consider all sensible requests. We can be contacted here:
210
+ <a href="https://wpmailsmtp.com/contact/" rel="friend">https://wpmailsmtp.com/contact/</a>
211
+
212
+ == Screenshots ==
213
+
214
+ 1. WP Mail SMTP Settings page
215
+ 2. Gmail / G Suite settings
216
+ 3. Mailgun settings
217
+ 4. SendGrid settings
218
+ 5. SMTP settings
219
+ 6. Send a Test Email
220
+
221
+ == Changelog ==
222
+
223
+ = 1.8.0 - 2019-12-12 =
224
+ * Added: New recommended mailer: Pepipost.
225
+ * Added: "Suggest a Mailer" link in a list of mailers to send us your ideas about new ones.
226
+ * Fixed: Sendgrid: Content ID for attachments missing.
227
+ * Changed: Timeout to HTTP requests (pepipost, sendgrid, mailgun), same as max_execution_time, to prevent fails when sending emails with big attachments.
228
+
229
+ = 1.7.1 - 2019-11-11 =
230
+ * Fixed: Compatibility with WordPress 5.3.
231
+ * Fixed: `Processor::get_default_email()` always returns empty value when server incorrectly configured.
232
+
233
+ = 1.7.0 - 2019-10-24 =
234
+ * Added: Add a new constant `WPMS_DO_NOT_SEND` to block email sending.
235
+ * Fixed: Default email (wordpress@example.com) rewriting in CLI mode.
236
+ * Fixed: Incorrect conflicts detection with certain plugins.
237
+ * Fixed: various typos in plugin settings.
238
+
239
+ = 1.6.2 - 2019-09-02 =
240
+ * Fixed: Race condition when loading with certain plugins, that send emails very early. Makes email delivery more reliable.
241
+
242
+ = 1.6.0 - 2019-08-21 =
243
+ * Added: New transactional mailer: Sendinblue.
244
+ * Added: Educate users to use transactional mailers for better deliverability.
245
+ * Added: New option and filter to disable admin area delivery error notices.
246
+ * Changed: Hide private API key saved in the DB for API based mailers using `input[type=password]`.
247
+ * Changed: Update links to various docs, pointing now to https://wpmailsmtp.com.
248
+
249
+ = 1.5.2 - 2019-07-18 =
250
+ * Fixed: "Redirect URI mismatch" error for "Gmail" mailer when trying to re-authorize an account that was initially created with version < v1.5.0.
251
+ * Changed: Make "Authentication" setting in "Other SMTP" mailer ON by default for new users.
252
+ * Changed: Mailers docs links now point to wpmailsmtp.com own site.
253
+
254
+ = 1.5.1 - 2019-07-12 =
255
+ * Fixed: Duplicated emails sent to the first recipient in a loop (and others not receiving their emails).
256
+
257
+ = 1.5.0 - 2019-07-09 =
258
+ * Added: Loсo plugin support.
259
+ * Added: "About us" admin area page.
260
+ * Added: Display in debug output a possible conflicting plugin existence.
261
+ * Added: Lots of actions and filters to improve flexibility of the plugin.
262
+ * Changed: Plugin menu is now top level.
263
+ * Changed: Hide secrets/API keys in page DOM in plugin admin area.
264
+ * Changed: Do not save constant values into the database when plugin settings are saved.
265
+ * Changed: Lots of i18n improvements to support translation for both free and paid version of the plugin.
266
+ * Changed: Gmail mailer - allow to change From Name email header.
267
+ * Changed: Gmail mailer - display email used to create a connection.
268
+ * Changed: WordPress 4.9 is the minimum WordPress version we support.
269
+ * Fixed: X-Mailer header should be present in all emails.
270
+ * Fixed: PHP notices when migrating under certain circumstances from 0.x version of the plugin.
271
+ * Fixed: Options::get_group() now supports values set via constants.
272
+
273
+ = 1.4.2 - 2019-03-23 =
274
+ * Changed: Tested up to WordPress 5.1.x.
275
+ * Changed: Removed TGMPA library.
276
+
277
+ = 1.4.1 - 2018-12-03 =
278
+ * Fixed: correctly process backslashes in SMTP passwords defined via constants.
279
+ * Changed: allow to send a Test Email when Default (none) mailer is selected in plugin settings.
280
+
281
+ = 1.4.0 - 2018-11-29 =
282
+ * Added: New option: Do Not Send - block emails from being sent.
283
+ * Added: New option: Send HTML or plain text emails when doing an Email Test.
284
+ * Added: New option: Mailgun region selection - US and EU (US is default to preserve compatibility).
285
+ * Fixed: Compatibility with WordPress 3.6+.
286
+ * Fixed: Compatibility with WordPress 5.0.
287
+ * Fixed: Constants usage is much more reliable now, works correctly on Multisite. Constants are global accross the whole network.
288
+ * Fixed: Preserve multipart emails when using Sendgrid/Mailgun mailers (were converted to HTML-only).
289
+ * Fixed: Security hardening.
290
+ * Changed: Prefill Email Test page From field with currently logged in user email.
291
+ * Changed: Update libraries: google/apiclient-services, google/auth, phpseclib/phpseclib and their dependecies.
292
+ * Changed: Display in debug output cURL version if Gmail mailing failed.
293
+ * Changed: Display in debug output OpenSSL version if it exists if Gmail/SMTP mailing failed.
294
+ * Changed: Display plugin version in dashboard error notice when emailing failed.
295
+ * Changed: Do not allow to send Test Email if mailer not configured properly.
296
+ * Changed: Notify in plugin admin area that Gmail doesn't allow to redefine From Name/Email etc.
297
+ * Changed: List all constants with descriptions in plugin main file: wp_mail_smtp.php.
298
+ * Changed: TGMPA: change descriptions from "Required" to "Recommended" (labels were incorrect).
299
+
300
+ = 1.3.3 - 2018-07-05 =
301
+ * Fixed: Compatibility with other plugins, that are using Google Service or Google Client classes.
302
+ * Changed: Optimize code loading.
303
+
304
+ = 1.3.2 - 2018-06-29 =
305
+ * Make sure that other plugins/themes are not conflicting with our TGMPA library.
306
+
307
+ = 1.3.1 - 2018-06-29 =
308
+ * Fixed: Other SMTP: Clear new Debug messages about failed email delivery on next successful email sending.
309
+ * Fixed: Introduce conditional autoloader to workaround Gmail PHP 5.5 requirement and its library compatibility issues vs PHP 5.3+ minimum viable plugin version.
310
+
311
+ = 1.3.0 - 2018-06-28 =
312
+ * Added: New option: force From Email rewrite regardless of the current value.
313
+ * Added: New option: force From Name rewrite regardless of the current value.
314
+ * Added: New option: remove all plugin data on plugin uninstall (when user deletes it).
315
+ * Added: Notify site admins in wp-admin area with a notice about last failed email delivery. Cleans up on successful delivery.
316
+ * Added: Notify site admins in wp-admin area with a notice about possible compatibility issues with other SMTP and email delivery plugins.
317
+ * Added: Improve User Debug Experience when doing Email Test - display helpful description and steps to fix the issue.
318
+ * Added: New users: provide default SMTP Port value for new users based on Encryption selection.
319
+ * Added: New users: notify about not configured plugin settings.
320
+ * Added: New users: Recommend free WPForms Lite plugin for those who don't have it.
321
+ * Added: SendGrid/Mailgun: provide support for multipart/alternative types of emails.
322
+ * Added: Gmail: new button to remove connection and to connect a new Google account.
323
+ * Fixed: Support plugin installation into /mu-plugins/ directory.
324
+ * Fixed: SendGrid: required text/plain part of email being the first one - fixes plain text emails not having links.
325
+ * Fixed: SendGrid and Mailgun: improperly sending plain text emails in html format.
326
+ * Fixed: SMTP Debug output was empty in some cases.
327
+ * Fixed: Compatibility with lots of other plugins that use Google Analytics library of different versions.
328
+ * Fixed: "client_id is empty" is no more a problem, should be fixed.
329
+ * Changed: For SendGrid and Mailgun allow using custom defined attachments names if present. Fallback to file name.
330
+ * Changed: Gmail: switch to a wider scope to prevent possible issues in certain circumstances.
331
+ * Changed: Remove whitespaces start/end of keys, secrets etc.
332
+ * Changed: Improved helpful description tests of various options.
333
+ * Changed: Improved plugin autoloading functionality.
334
+
335
+ = 1.2.5 - 2017-02-05 =
336
+ * Fixed: `Return path` can't be turned off.
337
+ * Fixed: `Authentication` sometimes can't be turned off.
338
+ * Fixed: `Auto TLS` sometimes can't be turned off.
339
+ * Fixed: BCC support for Gmail was broken.
340
+ * Fixed: Debug output improved to handle SELinux and grsecurity.
341
+ * Fixed: Strip slashes from plugin settings (useful for `From Name` option).
342
+ * Fixed: Change the way sanitization is done to prevent accidental removal of useful data.
343
+ * Fixed: Plugin activation will not overwrite settings back to defaults.
344
+ * Fixed: Properly set `Auto TLS` option on plugin activation.
345
+ * Fixed: Providers autoloading improved for certain Windows-based installs.
346
+ * Fixed: Use the proper path to load translations from plugin's `/languages` directory.
347
+ * Changed: Do not autoload on each page request plugin settings from WordPress options table.
348
+ * Changed: Do not autoload Pepipost classes unless it's saved as active mailer in settings.
349
+
350
+ = 1.2.4 - 2017-01-28 =
351
+ * Fixed: Improved escaping in debug reporting.
352
+
353
+ = 1.2.3 - 2017-01-22 =
354
+ * Fixed: Gmail tokens were reset after clicking Save Settings.
355
+ * Fixed: Slight typo in Gmail success message.
356
+
357
+ = 1.2.2 - 2017-12-27 =
358
+ * Fixed: Correctly handle Mailgun debug message for an incorrect api key.
359
+ * Fixed: Fatal error for Gmail and SMTP mailers with Nginx web-server (without Apache at all).
360
+ * Changed: Update X-Mailer emails header to show the real sender with a mailer and plugin version.
361
+
362
+ = 1.2.1 - 2017-12-21 =
363
+ * Fixed: Failed SMTP connections generate fatal errors.
364
+
365
+ = 1.2.0 - 2017-12-21 =
366
+ * Fixed: Decrease the factual minimum WordPress version from 3.9 to 3.6.
367
+ * Changed: Improve debug output for all mail providers.
368
+
369
+ = 1.1.0 - 2017-12-18 =
370
+ * Added: New option "Auto TLS" for SMTP mailer. Default is enabled. Migration routine for all sites.
371
+ * Changed: Improve debug output - clear styles and context-aware content.
372
+ * Changed: Better exceptions handling for Google authentication process.
373
+ * Changed: Do not sanitize passwords, api keys etc - as they may contain special characters in certain order and sanitization will break those values.
374
+ * Changed: Improve wording of some helpful texts inside plugin admin area.
375
+ * Fixed: Do not include certain files in dependency libraries that are not used by Google mailer. This should stop flagging plugin by Wordfence and VaultPress.
376
+ * Fixed: Constants usage is working now, to define the SMTP password, for example.
377
+ * Fixed: Notice for default mailer.
378
+
379
+ = 1.0.2 - 2017-12-12 =
380
+ * Fixed: PHPMailer using incorrect SMTPSecure value.
381
+
382
+ = 1.0.1 - 2017-12-12 =
383
+ * Fixed: Global POST processing conflict.
384
+
385
+ = 1.0.0 - 2017-12-12 =
386
+ * Added: Automatic migration tool to move options from older storage format to a new one.
387
+ * Added: Added Gmail & G Suite email provider integration - without your email and password.
388
+ * Added: Added SendGrid email provider integration - using the API key only.
389
+ * Added: Added Mailgun email provider integration - using the API key and configured domain only.
390
+ * Added: New compatibility mode - for PHP 5.2 old plugin will be loaded, for PHP 5.3 and higher - new version of admin area and new functionality.
391
+ * Changed: The new look of the admin area.
392
+ * Changed: SMTP password field now has "password" type.
393
+ * Changed: SMTP password field does not display real password at all when using constants in `wp-config.php` to define it.
394
+ * Changed: Escape properly all translations.
395
+ * Changed: More helpful test email content (with a mailer name).
src/Admin/Area.php CHANGED
@@ -1,889 +1,889 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Admin;
4
-
5
- use WPMailSMTP\WP;
6
- use WPMailSMTP\Options;
7
-
8
- /**
9
- * Class Area registers and process all wp-admin display functionality.
10
- *
11
- * @since 1.0.0
12
- */
13
- class Area {
14
-
15
- /**
16
- * @since 1.0.0
17
- *
18
- * @var string Slug of the admin area page.
19
- */
20
- const SLUG = 'wp-mail-smtp';
21
-
22
- /**
23
- * @since 1.0.0
24
- *
25
- * @var string Admin page unique hook.
26
- */
27
- public $hook;
28
-
29
- /**
30
- * @since 1.0.0
31
- *
32
- * @var PageAbstract[]
33
- */
34
- private $pages;
35
-
36
- /**
37
- * @since 1.5.0
38
- *
39
- * @var array List of official registered pages.
40
- */
41
- public static $pages_registered = array( 'general', 'logs', 'about' );
42
-
43
- /**
44
- * Area constructor.
45
- *
46
- * @since 1.0.0
47
- */
48
- public function __construct() {
49
- $this->hooks();
50
- }
51
-
52
- /**
53
- * Assign all hooks to proper places.
54
- *
55
- * @since 1.0.0
56
- */
57
- protected function hooks() {
58
-
59
- // Add the Settings link to a plugin on Plugins page.
60
- add_filter( 'plugin_action_links', array( $this, 'add_plugin_action_link' ), 10, 2 );
61
-
62
- // Add the options page.
63
- add_action( 'admin_menu', array( $this, 'add_admin_options_page' ) );
64
-
65
- // Admin footer text.
66
- add_filter( 'admin_footer_text', array( $this, 'get_admin_footer' ), 1, 2 );
67
-
68
- // Enqueue admin area scripts and styles.
69
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
70
-
71
- // Process the admin page forms actions.
72
- add_action( 'admin_init', array( $this, 'process_actions' ) );
73
-
74
- // Display custom notices based on the error/success codes.
75
- add_action( 'admin_init', array( $this, 'display_custom_auth_notices' ) );
76
-
77
- // Display notice instructing the user to complete plugin setup.
78
- add_action( 'admin_init', array( $this, 'display_setup_notice' ) );
79
-
80
- // Outputs the plugin admin header.
81
- add_action( 'in_admin_header', array( $this, 'display_admin_header' ), 100 );
82
-
83
- // Hide all unrelated to the plugin notices on the plugin admin pages.
84
- add_action( 'admin_print_scripts', array( $this, 'hide_unrelated_notices' ) );
85
-
86
- // Process all AJAX requests.
87
- add_action( 'wp_ajax_wp_mail_smtp_ajax', array( $this, 'process_ajax' ) );
88
- }
89
-
90
- /**
91
- * Display custom notices based on the error/success codes.
92
- *
93
- * @since 1.0.0
94
- */
95
- public function display_custom_auth_notices() {
96
-
97
- $error = isset( $_GET['error'] ) ? sanitize_key( $_GET['error'] ) : ''; // phpcs:ignore
98
- $success = isset( $_GET['success'] ) ? sanitize_key( $_GET['success'] ) : ''; // phpcs:ignore
99
-
100
- if ( empty( $error ) && empty( $success ) ) {
101
- return;
102
- }
103
-
104
- if ( ! current_user_can( 'manage_options' ) ) {
105
- return;
106
- }
107
-
108
- switch ( $error ) {
109
- case 'google_access_denied':
110
- WP::add_admin_notice(
111
- /* translators: %s - error code, returned by Google API. */
112
- sprintf( esc_html__( 'There was an error while processing the authentication request: %s. Please try again.', 'wp-mail-smtp' ), '<code>' . $error . '</code>' ),
113
- WP::ADMIN_NOTICE_ERROR
114
- );
115
- break;
116
-
117
- case 'google_no_code_scope':
118
- case 'microsoft_no_code':
119
- WP::add_admin_notice(
120
- esc_html__( 'There was an error while processing the authentication request. Please try again.', 'wp-mail-smtp' ),
121
- WP::ADMIN_NOTICE_ERROR
122
- );
123
- break;
124
-
125
- case 'google_no_clients':
126
- WP::add_admin_notice(
127
- esc_html__( 'There was an error while processing the authentication request. Please make sure that you have Client ID and Client Secret both valid and saved.', 'wp-mail-smtp' ),
128
- WP::ADMIN_NOTICE_ERROR
129
- );
130
- break;
131
- }
132
-
133
- switch ( $success ) {
134
- case 'google_site_linked':
135
- WP::add_admin_notice(
136
- esc_html__( 'You have successfully linked the current site with your Google API project. Now you can start sending emails through Gmail.', 'wp-mail-smtp' ),
137
- WP::ADMIN_NOTICE_SUCCESS
138
- );
139
- break;
140
- case 'microsoft_site_linked':
141
- WP::add_admin_notice(
142
- esc_html__( 'You have successfully linked the current site with your Microsoft API project. Now you can start sending emails through Outlook.', 'wp-mail-smtp' ),
143
- WP::ADMIN_NOTICE_SUCCESS
144
- );
145
- break;
146
- }
147
- }
148
-
149
- /**
150
- * Display notice instructing the user to complete plugin setup.
151
- *
152
- * @since 1.3.0
153
- */
154
- public function display_setup_notice() {
155
-
156
- // Bail if we're not on a plugin page.
157
- if ( ! $this->is_admin_page( 'general' ) ) {
158
- return;
159
- }
160
-
161
- $default_options = wp_json_encode( Options::get_defaults() );
162
- $current_options = wp_json_encode( Options::init()->get_all() );
163
-
164
- // Check if the current settings are the same as the default settings.
165
- if ( $current_options !== $default_options ) {
166
- return;
167
- }
168
-
169
- // Display notice informing user further action is needed.
170
- WP::add_admin_notice(
171
- sprintf(
172
- wp_kses(
173
- /* translators: %s - Mailer anchor link. */
174
- __( 'Thanks for using WP Mail SMTP! To complete the plugin setup and start sending emails, <strong>please select and configure your <a href="%s">Mailer</a></strong>.', 'wp-mail-smtp' ),
175
- array(
176
- 'a' => array(
177
- 'href' => array(),
178
- ),
179
- 'strong' => array(),
180
- )
181
- ),
182
- wp_mail_smtp()->get_admin()->get_admin_page_url( self::SLUG . '#wp-mail-smtp-setting-row-mailer' )
183
- ),
184
- WP::ADMIN_NOTICE_INFO
185
- );
186
- }
187
-
188
- /**
189
- * Add admin area menu item.
190
- *
191
- * @since 1.0.0
192
- * @since 1.5.0 Moved the menu to the top level. Added several more pages.
193
- */
194
- public function add_admin_options_page() {
195
-
196
- $this->hook = \add_menu_page(
197
- \esc_html__( 'WP Mail SMTP', 'wp-mail-smtp' ),
198
- \esc_html__( 'WP Mail SMTP', 'wp-mail-smtp' ),
199
- 'manage_options',
200
- self::SLUG,
201
- array( $this, 'display' ),
202
- 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiM5ZWEzYTgiIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgdmlld0JveD0iMCAwIDQzIDM0Ij48cGF0aCBkPSJNMC4wMDcsMy41ODVWMjAuNDIxcTAsMy41ODYsMy43NTEsMy41ODVMMjAsMjRWMTlIMzBWMTQuMDE0bDAuOTkxLTFMMzQsMTNWMy41ODVRMzQsMCwzMC4yNDksMEgzLjc1OFEwLjAwNywwLC4wMDcsMy41ODVoMFpNMy41MjQsNi4xNTdhMS40OSwxLjQ5LDAsMCwxLS41MDgtMC45MzUsMS41ODEsMS41ODEsMCwwLDEsLjI3NC0xLjIwOCwxLjQ0OSwxLjQ0OSwwLDAsMSwxLjA5NC0uNjYzLDEuNzU2LDEuNzU2LDAsMCwxLDEuMjUuMzEybDExLjQwOSw3LjcxNkwyOC4zNzQsMy42NjNhMS45NiwxLjk2LDAsMCwxLDEuMjg5LS4zMTIsMS41NDYsMS41NDYsMCwwLDEsMS4wOTQuNjYzLDEuNCwxLjQsMCwwLDEsLjI3MywxLjIwOCwxLjY3LDEuNjcsMCwwLDEtLjU0Ny45MzVMMTcuMDQzLDE3LjIyNVoiLz48cGF0aCBkPSJNMjIsMjhIMzJsLTAuMDA5LDQuNjI0YTEuMTI2LDEuMTI2LDAsMCwwLDEuOTIyLjhsOC4yNS04LjIzNmExLjEyNiwxLjEyNiwwLDAsMCwwLTEuNTk0bC04LjI1LTguMjQxYTEuMTI2LDEuMTI2LDAsMCwwLTEuOTIyLjh2NC44NjZMMjIsMjF2N1oiLz48L3N2Zz4=',
203
- 98
204
- );
205
-
206
- \add_submenu_page(
207
- self::SLUG,
208
- $this->get_current_tab_title() . ' &lsaquo; ' . \esc_html__( 'Settings', 'wp-mail-smtp' ),
209
- \esc_html__( 'Settings', 'wp-mail-smtp' ),
210
- 'manage_options',
211
- self::SLUG,
212
- array( $this, 'display' )
213
- );
214
- \add_submenu_page(
215
- self::SLUG,
216
- \esc_html__( 'Email Log', 'wp-mail-smtp' ),
217
- \esc_html__( 'Email Log', 'wp-mail-smtp' ),
218
- 'manage_options',
219
- self::SLUG . '-logs',
220
- array( $this, 'display' )
221
- );
222
- \add_submenu_page(
223
- self::SLUG,
224
- \esc_html__( 'About Us', 'wp-mail-smtp' ),
225
- \esc_html__( 'About Us', 'wp-mail-smtp' ),
226
- 'manage_options',
227
- self::SLUG . '-about',
228
- array( $this, 'display' )
229
- );
230
- }
231
-
232
- /**
233
- * Enqueue admin area scripts and styles.
234
- *
235
- * @since 1.0.0
236
- * @since 1.5.0 Added new assets for new pages.
237
- * @since 1.7.0 Added jQuery Confirm library css/js files.
238
- *
239
- * @param string $hook
240
- */
241
- public function enqueue_assets( $hook ) {
242
-
243
- if ( strpos( $hook, self::SLUG ) === false ) {
244
- return;
245
- }
246
-
247
- // General styles and js.
248
- \wp_enqueue_style(
249
- 'wp-mail-smtp-admin',
250
- \wp_mail_smtp()->assets_url . '/css/smtp-admin.min.css',
251
- false,
252
- WPMS_PLUGIN_VER
253
- );
254
-
255
- \wp_enqueue_script(
256
- 'wp-mail-smtp-admin',
257
- \wp_mail_smtp()->assets_url . '/js/smtp-admin' . WP::asset_min() . '.js',
258
- array( 'jquery' ),
259
- WPMS_PLUGIN_VER,
260
- false
261
- );
262
-
263
- \wp_localize_script(
264
- 'wp-mail-smtp-admin',
265
- 'wp_mail_smtp',
266
- array(
267
- 'text_provider_remove' => esc_html__( 'Are you sure you want to reset the current provider connection? You will need to immediately create a new one to be able to send emails.', 'wp-mail-smtp' ),
268
- 'education' => array(
269
- 'upgrade_icon_lock' => '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lock" class="svg-inline--fa fa-lock fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"></path></svg>',
270
- 'upgrade_title' => esc_html__( '%name% is a PRO Feature', 'wp-mail-smtp' ),
271
- 'upgrade_button' => esc_html__( 'Upgrade to Pro', 'wp-mail-smtp' ),
272
- 'upgrade_url' => 'https://wpmailsmtp.com/lite-upgrade/?discount=SMTPLITEUPGRADE&utm_source=WordPress&utm_medium=plugin-settings&utm_campaign=liteplugin',
273
- 'upgrade_bonus' => '<p>' .
274
- wp_kses(
275
- __( '<strong>Bonus:</strong> WP Mail SMTP users get <span>20% off</span> regular price,<br>applied at checkout.', 'wp-mail-smtp' ),
276
- array(
277
- 'strong' => true,
278
- 'span' => true,
279
- 'br' => true,
280
- )
281
- )
282
- . '</p>',
283
- 'upgrade_doc' => '<a href="https://wpmailsmtp.com/docs/how-to-upgrade-wp-mail-smtp-to-pro-version/?utm_source=WordPress&amp;utm_medium=link&amp;utm_campaign=liteplugin" target="_blank" rel="noopener noreferrer" class="already-purchased">
284
- ' . esc_html__( 'Already purchased?', 'wp-mail-smtp' ) . '
285
- </a>',
286
- ),
287
- )
288
- );
289
-
290
- /*
291
- * jQuery Confirm library v3.3.4.
292
- */
293
- \wp_enqueue_style(
294
- 'wp-mail-smtp-admin-jconfirm',
295
- \wp_mail_smtp()->assets_url . '/libs/jquery-confirm.min.css',
296
- array( 'wp-mail-smtp-admin' ),
297
- '3.3.4'
298
- );
299
- \wp_enqueue_script(
300
- 'wp-mail-smtp-admin-jconfirm',
301
- \wp_mail_smtp()->assets_url . '/libs/jquery-confirm.min.js',
302
- array( 'wp-mail-smtp-admin' ),
303
- '3.3.4',
304
- false
305
- );
306
-
307
- /*
308
- * Logs page.
309
- */
310
- if ( $this->is_admin_page( 'logs' ) ) {
311
- \wp_enqueue_style(
312
- 'wp-mail-smtp-admin-logs',
313
- apply_filters( 'wp_mail_smtp_admin_enqueue_assets_logs_css', '' ),
314
- array( 'wp-mail-smtp-admin' ),
315
- WPMS_PLUGIN_VER
316
- );
317
-
318
- \wp_enqueue_script(
319
- 'wp-mail-smtp-admin-logs',
320
- apply_filters( 'wp_mail_smtp_admin_enqueue_assets_logs_js', '' ),
321
- array( 'wp-mail-smtp-admin' ),
322
- WPMS_PLUGIN_VER,
323
- false
324
- );
325
- }
326
-
327
- /*
328
- * About page.
329
- */
330
- if ( $this->is_admin_page( 'about' ) ) {
331
-
332
- \wp_enqueue_style(
333
- 'wp-mail-smtp-admin-about',
334
- \wp_mail_smtp()->assets_url . '/css/smtp-about.min.css',
335
- array( 'wp-mail-smtp-admin' ),
336
- WPMS_PLUGIN_VER
337
- );
338
-
339
- \wp_enqueue_script(
340
- 'wp-mail-smtp-admin-about',
341
- \wp_mail_smtp()->assets_url . '/js/smtp-about' . WP::asset_min() . '.js',
342
- array( 'wp-mail-smtp-admin' ),
343
- '0.7.2',
344
- false
345
- );
346
-
347
- $settings = array(
348
- 'ajax_url' => \admin_url( 'admin-ajax.php' ),
349
- 'nonce' => \wp_create_nonce( 'wp-mail-smtp-about' ),
350
- // Strings.
351
- 'plugin_activate' => \esc_html__( 'Activate', 'wp-mail-smtp' ),
352
- 'plugin_activated' => \esc_html__( 'Activated', 'wp-mail-smtp' ),
353
- 'plugin_active' => \esc_html__( 'Active', 'wp-mail-smtp' ),
354
- 'plugin_inactive' => \esc_html__( 'Inactive', 'wp-mail-smtp' ),
355
- 'plugin_processing' => \esc_html__( 'Processing...', 'wp-mail-smtp' ),
356
- 'plugin_install_error' => \esc_html__( 'Could not install a plugin. Please download from WordPress.org and install manually.', 'wp-mail-smtp' ),
357
- 'plugin_install_activate_btn' => \esc_html__( 'Install and Activate', 'wp-mail-smtp' ),
358
- 'plugin_activate_btn' => \esc_html__( 'Activate', 'wp-mail-smtp' ),
359
- 'plugin_download_btn' => \esc_html__( 'Download', 'wp-mail-smtp' ),
360
- );
361
-
362
- \wp_localize_script(
363
- 'wp-mail-smtp-admin-about',
364
- 'wp_mail_smtp_about',
365
- $settings
366
- );
367
-
368
- \wp_enqueue_script(
369
- 'wp-mail-smtp-admin-about-matchheight',
370
- \wp_mail_smtp()->assets_url . '/js/jquery.matchHeight.min.js',
371
- array( 'wp-mail-smtp-admin' ),
372
- '0.7.2',
373
- false
374
- );
375
- }
376
-
377
- do_action( 'wp_mail_smtp_admin_area_enqueue_assets', $hook );
378
- }
379
-
380
- /**
381
- * Outputs the plugin admin header.
382
- *
383
- * @since 1.0.0
384
- */
385
- public function display_admin_header() {
386
-
387
- // Bail if we're not on a plugin page.
388
- if ( ! $this->is_admin_page() ) {
389
- return;
390
- }
391
- ?>
392
-
393
- <div id="wp-mail-smtp-header">
394
- <!--suppress HtmlUnknownTarget -->
395
- <img class="wp-mail-smtp-header-logo" src="<?php echo esc_url( wp_mail_smtp()->assets_url ); ?>/images/logo.svg" alt="WP Mail SMTP"/>
396
- </div>
397
-
398
- <?php
399
- }
400
-
401
- /**
402
- * Display a text to ask users to review the plugin on WP.org.
403
- *
404
- * @since 1.0.0
405
- *
406
- * @param string $text
407
- *
408
- * @return string
409
- */
410
- public function get_admin_footer( $text ) {
411
-
412
- if ( $this->is_admin_page() ) {
413
- $url = 'https://wordpress.org/support/plugin/wp-mail-smtp/reviews/?filter=5#new-post';
414
-
415
- $text = sprintf(
416
- wp_kses(
417
- /* translators: %1$s - WP.org link; %2$s - same WP.org link. */
418
- __( 'Please rate <strong>WP Mail SMTP</strong> <a href="%1$s" target="_blank" rel="noopener noreferrer">&#9733;&#9733;&#9733;&#9733;&#9733;</a> on <a href="%2$s" target="_blank" rel="noopener noreferrer">WordPress.org</a> to help us spread the word. Thank you from the WP Mail SMTP team!', 'wp-mail-smtp' ),
419
- array(
420
- 'strong' => array(),
421
- 'a' => array(
422
- 'href' => array(),
423
- 'target' => array(),
424
- 'rel' => array(),
425
- ),
426
- )
427
- ),
428
- $url,
429
- $url
430
- );
431
- }
432
-
433
- return $text;
434
- }
435
-
436
- /**
437
- * Display content of the admin area page.
438
- *
439
- * @since 1.0.0
440
- * @since 1.5.0 Rewrite to distinguish between General tabs and separate pages.
441
- */
442
- public function display() {
443
-
444
- // Bail if we're not on a plugin page.
445
- if ( ! $this->is_admin_page() ) {
446
- return;
447
- }
448
-
449
- $page = ! empty( $_GET['page'] ) ? \sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore
450
- ?>
451
-
452
- <div class="wrap" id="wp-mail-smtp">
453
-
454
- <?php
455
- switch ( $page ) {
456
- case self::SLUG:
457
- ?>
458
-
459
- <div class="wp-mail-smtp-page wp-mail-smtp-page-general wp-mail-smtp-tab-<?php echo esc_attr( $this->get_current_tab() ); ?>">
460
- <?php $this->display_tabs(); ?>
461
- </div>
462
-
463
- <?php
464
- break;
465
-
466
- case self::SLUG . '-logs':
467
- $logs_class = apply_filters( 'wp_mail_smtp_admin_display_get_logs_fqcn', '\WPMailSMTP\Admin\Pages\Logs' );
468
- /** @var \WPMailSMTP\Admin\PageAbstract $logs */
469
- $logs = new $logs_class();
470
-
471
- $is_archive = wp_mail_smtp()->is_pro() && wp_mail_smtp()->pro->get_logs()->is_archive();
472
- ?>
473
-
474
- <div class="wp-mail-smtp-page wp-mail-smtp-page-logs <?php echo $is_archive ? 'wp-mail-smtp-page-logs-archive' : 'wp-mail-smtp-page-logs-single'; ?>">
475
- <?php $logs->display(); ?>
476
- </div>
477
-
478
- <?php
479
- break;
480
-
481
- case self::SLUG . '-about':
482
- $about = new Pages\About();
483
- ?>
484
-
485
- <div class="wp-mail-smtp-page wp-mail-smtp-page-about wp-mail-smtp-tab-about-<?php echo \esc_attr( $about->get_current_tab() ); ?>">
486
- <?php $about->display(); ?>
487
- </div>
488
-
489
- <?php
490
- break;
491
- }
492
- ?>
493
- </div>
494
-
495
- <?php
496
- }
497
-
498
- /**
499
- * Display General page tabs.
500
- *
501
- * @since 1.5.0
502
- */
503
- protected function display_tabs() {
504
- ?>
505
-
506
- <div class="wp-mail-smtp-page-title">
507
- <?php
508
- foreach ( $this->get_pages() as $page_slug => $page ) :
509
- $label = $page->get_label();
510
- if ( empty( $label ) ) {
511
- continue;
512
- }
513
- $class = $page_slug === $this->get_current_tab() ? 'active' : '';
514
- ?>
515
-
516
- <a href="<?php echo esc_url( $page->get_link() ); ?>" class="tab <?php echo esc_attr( $class ); ?>">
517
- <?php echo esc_html( $label ); ?>
518
- </a>
519
-
520
- <?php endforeach; ?>
521
- </div>
522
-
523
- <div class="wp-mail-smtp-page-content">
524
- <h1 class="screen-reader-text">
525
- <?php echo esc_html( $this->get_current_tab_title() ); ?>
526
- </h1>
527
-
528
- <?php $this->display_current_tab_content(); ?>
529
- </div>
530
-
531
- <?php
532
- }
533
-
534
- /**
535
- * Get the current tab content.
536
- *
537
- * @since 1.0.0
538
- */
539
- public function display_current_tab_content() {
540
-
541
- $pages = $this->get_pages();
542
-
543
- if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
544
- return;
545
- }
546
-
547
- $pages[ $this->get_current_tab() ]->display();
548
- }
549
-
550
- /**
551
- * Get the current admin area tab.
552
- *
553
- * @since 1.0.0
554
- *
555
- * @return string
556
- */
557
- protected function get_current_tab() {
558
-
559
- $current = '';
560
-
561
- if ( $this->is_admin_page( 'general' ) ) {
562
- $current = ! empty( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'settings'; // phpcs:ignore
563
- }
564
-
565
- return $current;
566
- }
567
-
568
- /**
569
- * Get the array of default registered tabs for General page admin area.
570
- *
571
- * @since 1.0.0
572
- *
573
- * @return \WPMailSMTP\Admin\PageAbstract[]
574
- */
575
- public function get_pages() {
576
-
577
- if ( empty( $this->pages ) ) {
578
- $this->pages = array(
579
- 'settings' => new Pages\SettingsTab(),
580
- 'test' => new Pages\TestTab(),
581
- 'logs' => new Pages\LogsTab(),
582
- 'control' => new Pages\ControlTab(),
583
- 'misc' => new Pages\MiscTab(),
584
- 'auth' => new Pages\AuthTab(),
585
- );
586
- }
587
-
588
- return apply_filters( 'wp_mail_smtp_admin_get_pages', $this->pages );
589
- }
590
-
591
- /**
592
- * Get the current tab title.
593
- *
594
- * @since 1.0.0
595
- *
596
- * @return string
597
- */
598
- public function get_current_tab_title() {
599
-
600
- $pages = $this->get_pages();
601
-
602
- if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
603
- return '';
604
- }
605
-
606
- return $pages[ $this->get_current_tab() ]->get_title();
607
- }
608
-
609
- /**
610
- * Check whether we are on an admin page.
611
- *
612
- * @since 1.0.0
613
- * @since 1.5.0 Added support for new pages.
614
- *
615
- * @param array|string $slug ID(s) of a plugin page. Possible values: 'general', 'logs', 'about' or array of them.
616
- *
617
- * @return bool
618
- */
619
- public function is_admin_page( $slug = array() ) {
620
-
621
- $cur_page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore
622
- $check = self::SLUG;
623
- $pages_equal = false;
624
-
625
- if ( is_string( $slug ) ) {
626
- $slug = sanitize_key( $slug );
627
-
628
- if (
629
- in_array( $slug, self::$pages_registered, true ) &&
630
- $slug !== 'general'
631
- ) {
632
- $check = self::SLUG . '-' . $slug;
633
- }
634
-
635
- $pages_equal = $cur_page === $check;
636
- } elseif ( is_array( $slug ) ) {
637
- if ( empty( $slug ) ) {
638
- $slug = array_map( function ( $v ) {
639
- if ( $v === 'general' ) {
640
- return Area::SLUG;
641
- }
642
- return Area::SLUG . '-' . $v;
643
- }, self::$pages_registered );
644
- } else {
645
- $slug = array_map( function ( $v ) {
646
- if ( $v === 'general' ) {
647
- return Area::SLUG;
648
- }
649
- return Area::SLUG . '-' . sanitize_key( $v );
650
- }, $slug );
651
- }
652
-
653
- $pages_equal = in_array( $cur_page, $slug, true );
654
- }
655
-
656
- return is_admin() && $pages_equal;
657
- }
658
-
659
- /**
660
- * Give ability to use either admin area option or a filter to hide error notices about failed email delivery.
661
- * Filter has higher priority and overrides an option.
662
- *
663
- * @since 1.6.0
664
- *
665
- * @return bool
666
- */
667
- public function is_error_delivery_notice_enabled() {
668
-
669
- $is_hard_enabled = (bool) apply_filters( 'wp_mail_smtp_admin_is_error_delivery_notice_enabled', true );
670
-
671
- // If someone changed the value to false using a filter - disable completely.
672
- if ( ! $is_hard_enabled ) {
673
- return false;
674
- }
675
-
676
- return ! (bool) Options::init()->get( 'general', 'email_delivery_errors_hidden' );
677
- }
678
-
679
- /**
680
- * All possible plugin forms manipulation will be done here.
681
- *
682
- * @since 1.0.0
683
- */
684
- public function process_actions() {
685
-
686
- // Bail if we're not on a plugin General page.
687
- if ( ! $this->is_admin_page( 'general' ) ) {
688
- return;
689
- }
690
-
691
- $pages = $this->get_pages();
692
-
693
- // Allow to process only own tabs.
694
- if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
695
- return;
696
- }
697
-
698
- // Process POST only if it exists.
699
- if ( ! empty( $_POST ) ) {
700
- if ( ! empty( $_POST['wp-mail-smtp'] ) ) {
701
- $post = $_POST['wp-mail-smtp'];
702
- } else {
703
- $post = array();
704
- }
705
-
706
- $pages[ $this->get_current_tab() ]->process_post( $post );
707
- }
708
-
709
- // This won't do anything for most pages.
710
- // Works for plugin page only, when GET params are allowed.
711
- $pages[ $this->get_current_tab() ]->process_auth();
712
- }
713
-
714
- /**
715
- * Process all AJAX requests.
716
- *
717
- * @since 1.3.0
718
- * @since 1.5.0 Added tasks to process plugins management.
719
- */
720
- public function process_ajax() {
721
-
722
- $data = array();
723
-
724
- // Only admins can fire these ajax requests.
725
- if ( ! current_user_can( 'manage_options' ) ) {
726
- wp_send_json_error( $data );
727
- }
728
-
729
- if ( empty( $_POST['task'] ) ) { // phpcs:ignore
730
- wp_send_json_error( $data );
731
- }
732
-
733
- $task = sanitize_key( $_POST['task'] ); // phpcs:ignore
734
-
735
- switch ( $task ) {
736
- case 'pro_banner_dismiss':
737
- update_user_meta( get_current_user_id(), 'wp_mail_smtp_pro_banner_dismissed', true );
738
- $data['message'] = esc_html__( 'WP Mail SMTP Pro related message was successfully dismissed.', 'wp-mail-smtp' );
739
- break;
740
-
741
- case 'about_plugin_install':
742
- Pages\About::ajax_plugin_install();
743
- break;
744
-
745
- case 'about_plugin_activate':
746
- Pages\About::ajax_plugin_activate();
747
- break;
748
-
749
- case 'notice_dismiss':
750
- $notice = sanitize_key( $_POST['notice'] ); // phpcs:ignore
751
- $mailer = sanitize_key( $_POST['mailer'] ); // phpcs:ignore
752
- if ( empty( $notice ) || empty( $mailer ) ) {
753
- break;
754
- }
755
-
756
- update_user_meta( get_current_user_id(), "wp_mail_smtp_notice_{$notice}_for_{$mailer}_dismissed", true );
757
- $data['message'] = esc_html__( 'Educational notice for this mailer was successfully dismissed.', 'wp-mail-smtp' );
758
- break;
759
-
760
- default:
761
- // Allow custom tasks data processing being added here.
762
- $data = apply_filters( 'wp_mail_smtp_admin_process_ajax_' . $task . '_data', $data );
763
- }
764
-
765
- // Final ability to rewrite all the data, just in case.
766
- $data = (array) apply_filters( 'wp_mail_smtp_admin_process_ajax_data', $data, $task );
767
-
768
- if ( empty( $data ) ) {
769
- wp_send_json_error( $data );
770
- }
771
-
772
- wp_send_json_success( $data );
773
- }
774
-
775
- /**
776
- * Add a link to Settings page of a plugin on Plugins page.
777
- *
778
- * @since 1.0.0
779
- * @since 1.5.0 Added a link to Email Log.
780
- *
781
- * @param array $links
782
- * @param string $file
783
- *
784
- * @return mixed
785
- */
786
- public function add_plugin_action_link( $links, $file ) {
787
-
788
- // Will target both pro and lite version of a plugin.
789
- if ( strpos( $file, 'wp-mail-smtp' ) === false ) {
790
- return $links;
791
- }
792
-
793
- $settings_link = '<a href="' . esc_url( $this->get_admin_page_url() ) . '">' . esc_html__( 'Settings', 'wp-mail-smtp' ) . '</a>';
794
- $logs_link = '<a href="' . esc_url( $this->get_admin_page_url( self::SLUG . '-logs' ) ) . '">' . esc_html__( 'Email Log', 'wp-mail-smtp' ) . '</a>';
795
-
796
- array_unshift( $links, $settings_link, $logs_link );
797
-
798
- return $links;
799
- }
800
-
801
- /**
802
- * Get plugin admin area page URL.
803
- *
804
- * @since 1.0.0
805
- * @since 1.5.0 URL is changed to support the top level position of the plugin admin area.
806
- *
807
- * @param string $page
808
- *
809
- * @return string
810
- */
811
- public function get_admin_page_url( $page = '' ) {
812
-
813
- if ( empty( $page ) ) {
814
- $page = self::SLUG;
815
- }
816
-
817
- return add_query_arg(
818
- 'page',
819
- $page,
820
- admin_url( 'admin.php' )
821
- );
822
- }
823
-
824
- /**
825
- * Remove all non-WP Mail SMTP plugin notices from plugin pages.
826
- *
827
- * @since 1.0.0
828
- */
829
- public function hide_unrelated_notices() {
830
-
831
- // Bail if we're not on our screen or page.
832
- if ( empty( $_REQUEST['page'] ) || strpos( $_REQUEST['page'], self::SLUG ) === false ) {
833
- return;
834
- }
835
-
836
- global $wp_filter;
837
-
838
- if ( ! empty( $wp_filter['user_admin_notices']->callbacks ) && is_array( $wp_filter['user_admin_notices']->callbacks ) ) {
839
- foreach ( $wp_filter['user_admin_notices']->callbacks as $priority => $hooks ) {
840
- foreach ( $hooks as $name => $arr ) {
841
- if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
842
- unset( $wp_filter['user_admin_notices']->callbacks[ $priority ][ $name ] );
843
- continue;
844
- }
845
- if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
846
- continue;
847
- }
848
- if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
849
- unset( $wp_filter['user_admin_notices']->callbacks[ $priority ][ $name ] );
850
- }
851
- }
852
- }
853
- }
854
-
855
- if ( ! empty( $wp_filter['admin_notices']->callbacks ) && is_array( $wp_filter['admin_notices']->callbacks ) ) {
856
- foreach ( $wp_filter['admin_notices']->callbacks as $priority => $hooks ) {
857
- foreach ( $hooks as $name => $arr ) {
858
- if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
859
- unset( $wp_filter['admin_notices']->callbacks[ $priority ][ $name ] );
860
- continue;
861
- }
862
- if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
863
- continue;
864
- }
865
- if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
866
- unset( $wp_filter['admin_notices']->callbacks[ $priority ][ $name ] );
867
- }
868
- }
869
- }
870
- }
871
-
872
- if ( ! empty( $wp_filter['all_admin_notices']->callbacks ) && is_array( $wp_filter['all_admin_notices']->callbacks ) ) {
873
- foreach ( $wp_filter['all_admin_notices']->callbacks as $priority => $hooks ) {
874
- foreach ( $hooks as $name => $arr ) {
875
- if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
876
- unset( $wp_filter['all_admin_notices']->callbacks[ $priority ][ $name ] );
877
- continue;
878
- }
879
- if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
880
- continue;
881
- }
882
- if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
883
- unset( $wp_filter['all_admin_notices']->callbacks[ $priority ][ $name ] );
884
- }
885
- }
886
- }
887
- }
888
- }
889
- }
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Admin;
4
+
5
+ use WPMailSMTP\WP;
6
+ use WPMailSMTP\Options;
7
+
8
+ /**
9
+ * Class Area registers and process all wp-admin display functionality.
10
+ *
11
+ * @since 1.0.0
12
+ */
13
+ class Area {
14
+
15
+ /**
16
+ * @since 1.0.0
17
+ *
18
+ * @var string Slug of the admin area page.
19
+ */
20
+ const SLUG = 'wp-mail-smtp';
21
+
22
+ /**
23
+ * @since 1.0.0
24
+ *
25
+ * @var string Admin page unique hook.
26
+ */
27
+ public $hook;
28
+
29
+ /**
30
+ * @since 1.0.0
31
+ *
32
+ * @var PageAbstract[]
33
+ */
34
+ private $pages;
35
+
36
+ /**
37
+ * @since 1.5.0
38
+ *
39
+ * @var array List of official registered pages.
40
+ */
41
+ public static $pages_registered = array( 'general', 'logs', 'about' );
42
+
43
+ /**
44
+ * Area constructor.
45
+ *
46
+ * @since 1.0.0
47
+ */
48
+ public function __construct() {
49
+ $this->hooks();
50
+ }
51
+
52
+ /**
53
+ * Assign all hooks to proper places.
54
+ *
55
+ * @since 1.0.0
56
+ */
57
+ protected function hooks() {
58
+
59
+ // Add the Settings link to a plugin on Plugins page.
60
+ add_filter( 'plugin_action_links', array( $this, 'add_plugin_action_link' ), 10, 2 );
61
+
62
+ // Add the options page.
63
+ add_action( 'admin_menu', array( $this, 'add_admin_options_page' ) );
64
+
65
+ // Admin footer text.
66
+ add_filter( 'admin_footer_text', array( $this, 'get_admin_footer' ), 1, 2 );
67
+
68
+ // Enqueue admin area scripts and styles.
69
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
70
+
71
+ // Process the admin page forms actions.
72
+ add_action( 'admin_init', array( $this, 'process_actions' ) );
73
+
74
+ // Display custom notices based on the error/success codes.
75
+ add_action( 'admin_init', array( $this, 'display_custom_auth_notices' ) );
76
+
77
+ // Display notice instructing the user to complete plugin setup.
78
+ add_action( 'admin_init', array( $this, 'display_setup_notice' ) );
79
+
80
+ // Outputs the plugin admin header.
81
+ add_action( 'in_admin_header', array( $this, 'display_admin_header' ), 100 );
82
+
83
+ // Hide all unrelated to the plugin notices on the plugin admin pages.
84
+ add_action( 'admin_print_scripts', array( $this, 'hide_unrelated_notices' ) );
85
+
86
+ // Process all AJAX requests.
87
+ add_action( 'wp_ajax_wp_mail_smtp_ajax', array( $this, 'process_ajax' ) );
88
+ }
89
+
90
+ /**
91
+ * Display custom notices based on the error/success codes.
92
+ *
93
+ * @since 1.0.0
94
+ */
95
+ public function display_custom_auth_notices() {
96
+
97
+ $error = isset( $_GET['error'] ) ? sanitize_key( $_GET['error'] ) : ''; // phpcs:ignore
98
+ $success = isset( $_GET['success'] ) ? sanitize_key( $_GET['success'] ) : ''; // phpcs:ignore
99
+
100
+ if ( empty( $error ) && empty( $success ) ) {
101
+ return;
102
+ }
103
+
104
+ if ( ! current_user_can( 'manage_options' ) ) {
105
+ return;
106
+ }
107
+
108
+ switch ( $error ) {
109
+ case 'google_access_denied':
110
+ WP::add_admin_notice(
111
+ /* translators: %s - error code, returned by Google API. */
112
+ sprintf( esc_html__( 'There was an error while processing the authentication request: %s. Please try again.', 'wp-mail-smtp' ), '<code>' . $error . '</code>' ),
113
+ WP::ADMIN_NOTICE_ERROR
114
+ );
115
+ break;
116
+
117
+ case 'google_no_code_scope':
118
+ case 'microsoft_no_code':
119
+ WP::add_admin_notice(
120
+ esc_html__( 'There was an error while processing the authentication request. Please try again.', 'wp-mail-smtp' ),
121
+ WP::ADMIN_NOTICE_ERROR
122
+ );
123
+ break;
124
+
125
+ case 'google_no_clients':
126
+ WP::add_admin_notice(
127
+ esc_html__( 'There was an error while processing the authentication request. Please make sure that you have Client ID and Client Secret both valid and saved.', 'wp-mail-smtp' ),
128
+ WP::ADMIN_NOTICE_ERROR
129
+ );
130
+ break;
131
+ }
132
+
133
+ switch ( $success ) {
134
+ case 'google_site_linked':
135
+ WP::add_admin_notice(
136
+ esc_html__( 'You have successfully linked the current site with your Google API project. Now you can start sending emails through Gmail.', 'wp-mail-smtp' ),
137
+ WP::ADMIN_NOTICE_SUCCESS
138
+ );
139
+ break;
140
+ case 'microsoft_site_linked':
141
+ WP::add_admin_notice(
142
+ esc_html__( 'You have successfully linked the current site with your Microsoft API project. Now you can start sending emails through Outlook.', 'wp-mail-smtp' ),
143
+ WP::ADMIN_NOTICE_SUCCESS
144
+ );
145
+ break;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Display notice instructing the user to complete plugin setup.
151
+ *
152
+ * @since 1.3.0
153
+ */
154
+ public function display_setup_notice() {
155
+
156
+ // Bail if we're not on a plugin page.
157
+ if ( ! $this->is_admin_page( 'general' ) ) {
158
+ return;
159
+ }
160
+
161
+ $default_options = wp_json_encode( Options::get_defaults() );
162
+ $current_options = wp_json_encode( Options::init()->get_all() );
163
+
164
+ // Check if the current settings are the same as the default settings.
165
+ if ( $current_options !== $default_options ) {
166
+ return;
167
+ }
168
+
169
+ // Display notice informing user further action is needed.
170
+ WP::add_admin_notice(
171
+ sprintf(
172
+ wp_kses(
173
+ /* translators: %s - Mailer anchor link. */
174
+ __( 'Thanks for using WP Mail SMTP! To complete the plugin setup and start sending emails, <strong>please select and configure your <a href="%s">Mailer</a></strong>.', 'wp-mail-smtp' ),
175
+ array(
176
+ 'a' => array(
177
+ 'href' => array(),
178
+ ),
179
+ 'strong' => array(),
180
+ )
181
+ ),
182
+ wp_mail_smtp()->get_admin()->get_admin_page_url( self::SLUG . '#wp-mail-smtp-setting-row-mailer' )
183
+ ),
184
+ WP::ADMIN_NOTICE_INFO
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Add admin area menu item.
190
+ *
191
+ * @since 1.0.0
192
+ * @since 1.5.0 Moved the menu to the top level. Added several more pages.
193
+ */
194
+ public function add_admin_options_page() {
195
+
196
+ $this->hook = \add_menu_page(
197
+ \esc_html__( 'WP Mail SMTP', 'wp-mail-smtp' ),
198
+ \esc_html__( 'WP Mail SMTP', 'wp-mail-smtp' ),
199
+ 'manage_options',
200
+ self::SLUG,
201
+ array( $this, 'display' ),
202
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiM5ZWEzYTgiIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgdmlld0JveD0iMCAwIDQzIDM0Ij48cGF0aCBkPSJNMC4wMDcsMy41ODVWMjAuNDIxcTAsMy41ODYsMy43NTEsMy41ODVMMjAsMjRWMTlIMzBWMTQuMDE0bDAuOTkxLTFMMzQsMTNWMy41ODVRMzQsMCwzMC4yNDksMEgzLjc1OFEwLjAwNywwLC4wMDcsMy41ODVoMFpNMy41MjQsNi4xNTdhMS40OSwxLjQ5LDAsMCwxLS41MDgtMC45MzUsMS41ODEsMS41ODEsMCwwLDEsLjI3NC0xLjIwOCwxLjQ0OSwxLjQ0OSwwLDAsMSwxLjA5NC0uNjYzLDEuNzU2LDEuNzU2LDAsMCwxLDEuMjUuMzEybDExLjQwOSw3LjcxNkwyOC4zNzQsMy42NjNhMS45NiwxLjk2LDAsMCwxLDEuMjg5LS4zMTIsMS41NDYsMS41NDYsMCwwLDEsMS4wOTQuNjYzLDEuNCwxLjQsMCwwLDEsLjI3MywxLjIwOCwxLjY3LDEuNjcsMCwwLDEtLjU0Ny45MzVMMTcuMDQzLDE3LjIyNVoiLz48cGF0aCBkPSJNMjIsMjhIMzJsLTAuMDA5LDQuNjI0YTEuMTI2LDEuMTI2LDAsMCwwLDEuOTIyLjhsOC4yNS04LjIzNmExLjEyNiwxLjEyNiwwLDAsMCwwLTEuNTk0bC04LjI1LTguMjQxYTEuMTI2LDEuMTI2LDAsMCwwLTEuOTIyLjh2NC44NjZMMjIsMjF2N1oiLz48L3N2Zz4=',
203
+ 98
204
+ );
205
+
206
+ \add_submenu_page(
207
+ self::SLUG,
208
+ $this->get_current_tab_title() . ' &lsaquo; ' . \esc_html__( 'Settings', 'wp-mail-smtp' ),
209
+ \esc_html__( 'Settings', 'wp-mail-smtp' ),
210
+ 'manage_options',
211
+ self::SLUG,
212
+ array( $this, 'display' )
213
+ );
214
+ \add_submenu_page(
215
+ self::SLUG,
216
+ \esc_html__( 'Email Log', 'wp-mail-smtp' ),
217
+ \esc_html__( 'Email Log', 'wp-mail-smtp' ),
218
+ 'manage_options',
219
+ self::SLUG . '-logs',
220
+ array( $this, 'display' )
221
+ );
222
+ \add_submenu_page(
223
+ self::SLUG,
224
+ \esc_html__( 'About Us', 'wp-mail-smtp' ),
225
+ \esc_html__( 'About Us', 'wp-mail-smtp' ),
226
+ 'manage_options',
227
+ self::SLUG . '-about',
228
+ array( $this, 'display' )
229
+ );
230
+ }
231
+
232
+ /**
233
+ * Enqueue admin area scripts and styles.
234
+ *
235
+ * @since 1.0.0
236
+ * @since 1.5.0 Added new assets for new pages.
237
+ * @since 1.7.0 Added jQuery Confirm library css/js files.
238
+ *
239
+ * @param string $hook
240
+ */
241
+ public function enqueue_assets( $hook ) {
242
+
243
+ if ( strpos( $hook, self::SLUG ) === false ) {
244
+ return;
245
+ }
246
+
247
+ // General styles and js.
248
+ \wp_enqueue_style(
249
+ 'wp-mail-smtp-admin',
250
+ \wp_mail_smtp()->assets_url . '/css/smtp-admin.min.css',
251
+ false,
252
+ WPMS_PLUGIN_VER
253
+ );
254
+
255
+ \wp_enqueue_script(
256
+ 'wp-mail-smtp-admin',
257
+ \wp_mail_smtp()->assets_url . '/js/smtp-admin' . WP::asset_min() . '.js',
258
+ array( 'jquery' ),
259
+ WPMS_PLUGIN_VER,
260
+ false
261
+ );
262
+
263
+ \wp_localize_script(
264
+ 'wp-mail-smtp-admin',
265
+ 'wp_mail_smtp',
266
+ array(
267
+ 'text_provider_remove' => esc_html__( 'Are you sure you want to reset the current provider connection? You will need to immediately create a new one to be able to send emails.', 'wp-mail-smtp' ),
268
+ 'education' => array(
269
+ 'upgrade_icon_lock' => '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="lock" class="svg-inline--fa fa-lock fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"></path></svg>',
270
+ 'upgrade_title' => esc_html__( '%name% is a PRO Feature', 'wp-mail-smtp' ),
271
+ 'upgrade_button' => esc_html__( 'Upgrade to Pro', 'wp-mail-smtp' ),
272
+ 'upgrade_url' => 'https://wpmailsmtp.com/lite-upgrade/?discount=SMTPLITEUPGRADE&utm_source=WordPress&utm_medium=plugin-settings&utm_campaign=liteplugin',
273
+ 'upgrade_bonus' => '<p>' .
274
+ wp_kses(
275
+ __( '<strong>Bonus:</strong> WP Mail SMTP users get <span>20% off</span> regular price,<br>applied at checkout.', 'wp-mail-smtp' ),
276
+ array(
277
+ 'strong' => true,
278
+ 'span' => true,
279
+ 'br' => true,
280
+ )
281
+ )
282
+ . '</p>',
283
+ 'upgrade_doc' => '<a href="https://wpmailsmtp.com/docs/how-to-upgrade-wp-mail-smtp-to-pro-version/?utm_source=WordPress&amp;utm_medium=link&amp;utm_campaign=liteplugin" target="_blank" rel="noopener noreferrer" class="already-purchased">
284
+ ' . esc_html__( 'Already purchased?', 'wp-mail-smtp' ) . '
285
+ </a>',
286
+ ),
287
+ )
288
+ );
289
+
290
+ /*
291
+ * jQuery Confirm library v3.3.4.
292
+ */
293
+ \wp_enqueue_style(
294
+ 'wp-mail-smtp-admin-jconfirm',
295
+ \wp_mail_smtp()->assets_url . '/libs/jquery-confirm.min.css',
296
+ array( 'wp-mail-smtp-admin' ),
297
+ '3.3.4'
298
+ );
299
+ \wp_enqueue_script(
300
+ 'wp-mail-smtp-admin-jconfirm',
301
+ \wp_mail_smtp()->assets_url . '/libs/jquery-confirm.min.js',
302
+ array( 'wp-mail-smtp-admin' ),
303
+ '3.3.4',
304
+ false
305
+ );
306
+
307
+ /*
308
+ * Logs page.
309
+ */
310
+ if ( $this->is_admin_page( 'logs' ) ) {
311
+ \wp_enqueue_style(
312
+ 'wp-mail-smtp-admin-logs',
313
+ apply_filters( 'wp_mail_smtp_admin_enqueue_assets_logs_css', '' ),
314
+ array( 'wp-mail-smtp-admin' ),
315
+ WPMS_PLUGIN_VER
316
+ );
317
+
318
+ \wp_enqueue_script(
319
+ 'wp-mail-smtp-admin-logs',
320
+ apply_filters( 'wp_mail_smtp_admin_enqueue_assets_logs_js', '' ),
321
+ array( 'wp-mail-smtp-admin' ),
322
+ WPMS_PLUGIN_VER,
323
+ false
324
+ );
325
+ }
326
+
327
+ /*
328
+ * About page.
329
+ */
330
+ if ( $this->is_admin_page( 'about' ) ) {
331
+
332
+ \wp_enqueue_style(
333
+ 'wp-mail-smtp-admin-about',
334
+ \wp_mail_smtp()->assets_url . '/css/smtp-about.min.css',
335
+ array( 'wp-mail-smtp-admin' ),
336
+ WPMS_PLUGIN_VER
337
+ );
338
+
339
+ \wp_enqueue_script(
340
+ 'wp-mail-smtp-admin-about',
341
+ \wp_mail_smtp()->assets_url . '/js/smtp-about' . WP::asset_min() . '.js',
342
+ array( 'wp-mail-smtp-admin' ),
343
+ '0.7.2',
344
+ false
345
+ );
346
+
347
+ $settings = array(
348
+ 'ajax_url' => \admin_url( 'admin-ajax.php' ),
349
+ 'nonce' => \wp_create_nonce( 'wp-mail-smtp-about' ),
350
+ // Strings.
351
+ 'plugin_activate' => \esc_html__( 'Activate', 'wp-mail-smtp' ),
352
+ 'plugin_activated' => \esc_html__( 'Activated', 'wp-mail-smtp' ),
353
+ 'plugin_active' => \esc_html__( 'Active', 'wp-mail-smtp' ),
354
+ 'plugin_inactive' => \esc_html__( 'Inactive', 'wp-mail-smtp' ),
355
+ 'plugin_processing' => \esc_html__( 'Processing...', 'wp-mail-smtp' ),
356
+ 'plugin_install_error' => \esc_html__( 'Could not install a plugin. Please download from WordPress.org and install manually.', 'wp-mail-smtp' ),
357
+ 'plugin_install_activate_btn' => \esc_html__( 'Install and Activate', 'wp-mail-smtp' ),
358
+ 'plugin_activate_btn' => \esc_html__( 'Activate', 'wp-mail-smtp' ),
359
+ 'plugin_download_btn' => \esc_html__( 'Download', 'wp-mail-smtp' ),
360
+ );
361
+
362
+ \wp_localize_script(
363
+ 'wp-mail-smtp-admin-about',
364
+ 'wp_mail_smtp_about',
365
+ $settings
366
+ );
367
+
368
+ \wp_enqueue_script(
369
+ 'wp-mail-smtp-admin-about-matchheight',
370
+ \wp_mail_smtp()->assets_url . '/js/jquery.matchHeight.min.js',
371
+ array( 'wp-mail-smtp-admin' ),
372
+ '0.7.2',
373
+ false
374
+ );
375
+ }
376
+
377
+ do_action( 'wp_mail_smtp_admin_area_enqueue_assets', $hook );
378
+ }
379
+
380
+ /**
381
+ * Outputs the plugin admin header.
382
+ *
383
+ * @since 1.0.0
384
+ */
385
+ public function display_admin_header() {
386
+
387
+ // Bail if we're not on a plugin page.
388
+ if ( ! $this->is_admin_page() ) {
389
+ return;
390
+ }
391
+ ?>
392
+
393
+ <div id="wp-mail-smtp-header">
394
+ <!--suppress HtmlUnknownTarget -->
395
+ <img class="wp-mail-smtp-header-logo" src="<?php echo esc_url( wp_mail_smtp()->assets_url ); ?>/images/logo.svg" alt="WP Mail SMTP"/>
396
+ </div>
397
+
398
+ <?php
399
+ }
400
+
401
+ /**
402
+ * Display a text to ask users to review the plugin on WP.org.
403
+ *
404
+ * @since 1.0.0
405
+ *
406
+ * @param string $text
407
+ *
408
+ * @return string
409
+ */
410
+ public function get_admin_footer( $text ) {
411
+
412
+ if ( $this->is_admin_page() ) {
413
+ $url = 'https://wordpress.org/support/plugin/wp-mail-smtp/reviews/?filter=5#new-post';
414
+
415
+ $text = sprintf(
416
+ wp_kses(
417
+ /* translators: %1$s - WP.org link; %2$s - same WP.org link. */
418
+ __( 'Please rate <strong>WP Mail SMTP</strong> <a href="%1$s" target="_blank" rel="noopener noreferrer">&#9733;&#9733;&#9733;&#9733;&#9733;</a> on <a href="%2$s" target="_blank" rel="noopener noreferrer">WordPress.org</a> to help us spread the word. Thank you from the WP Mail SMTP team!', 'wp-mail-smtp' ),
419
+ array(
420
+ 'strong' => array(),
421
+ 'a' => array(
422
+ 'href' => array(),
423
+ 'target' => array(),
424
+ 'rel' => array(),
425
+ ),
426
+ )
427
+ ),
428
+ $url,
429
+ $url
430
+ );
431
+ }
432
+
433
+ return $text;
434
+ }
435
+
436
+ /**
437
+ * Display content of the admin area page.
438
+ *
439
+ * @since 1.0.0
440
+ * @since 1.5.0 Rewrite to distinguish between General tabs and separate pages.
441
+ */
442
+ public function display() {
443
+
444
+ // Bail if we're not on a plugin page.
445
+ if ( ! $this->is_admin_page() ) {
446
+ return;
447
+ }
448
+
449
+ $page = ! empty( $_GET['page'] ) ? \sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore
450
+ ?>
451
+
452
+ <div class="wrap" id="wp-mail-smtp">
453
+
454
+ <?php
455
+ switch ( $page ) {
456
+ case self::SLUG:
457
+ ?>
458
+
459
+ <div class="wp-mail-smtp-page wp-mail-smtp-page-general wp-mail-smtp-tab-<?php echo esc_attr( $this->get_current_tab() ); ?>">
460
+ <?php $this->display_tabs(); ?>
461
+ </div>
462
+
463
+ <?php
464
+ break;
465
+
466
+ case self::SLUG . '-logs':
467
+ $logs_class = apply_filters( 'wp_mail_smtp_admin_display_get_logs_fqcn', '\WPMailSMTP\Admin\Pages\Logs' );
468
+ /** @var \WPMailSMTP\Admin\PageAbstract $logs */
469
+ $logs = new $logs_class();
470
+
471
+ $is_archive = wp_mail_smtp()->is_pro() && wp_mail_smtp()->pro->get_logs()->is_archive();
472
+ ?>
473
+
474
+ <div class="wp-mail-smtp-page wp-mail-smtp-page-logs <?php echo $is_archive ? 'wp-mail-smtp-page-logs-archive' : 'wp-mail-smtp-page-logs-single'; ?>">
475
+ <?php $logs->display(); ?>
476
+ </div>
477
+
478
+ <?php
479
+ break;
480
+
481
+ case self::SLUG . '-about':
482
+ $about = new Pages\About();
483
+ ?>
484
+
485
+ <div class="wp-mail-smtp-page wp-mail-smtp-page-about wp-mail-smtp-tab-about-<?php echo \esc_attr( $about->get_current_tab() ); ?>">
486
+ <?php $about->display(); ?>
487
+ </div>
488
+
489
+ <?php
490
+ break;
491
+ }
492
+ ?>
493
+ </div>
494
+
495
+ <?php
496
+ }
497
+
498
+ /**
499
+ * Display General page tabs.
500
+ *
501
+ * @since 1.5.0
502
+ */
503
+ protected function display_tabs() {
504
+ ?>
505
+
506
+ <div class="wp-mail-smtp-page-title">
507
+ <?php
508
+ foreach ( $this->get_pages() as $page_slug => $page ) :
509
+ $label = $page->get_label();
510
+ if ( empty( $label ) ) {
511
+ continue;
512
+ }
513
+ $class = $page_slug === $this->get_current_tab() ? 'active' : '';
514
+ ?>
515
+
516
+ <a href="<?php echo esc_url( $page->get_link() ); ?>" class="tab <?php echo esc_attr( $class ); ?>">
517
+ <?php echo esc_html( $label ); ?>
518
+ </a>
519
+
520
+ <?php endforeach; ?>
521
+ </div>
522
+
523
+ <div class="wp-mail-smtp-page-content">
524
+ <h1 class="screen-reader-text">
525
+ <?php echo esc_html( $this->get_current_tab_title() ); ?>
526
+ </h1>
527
+
528
+ <?php $this->display_current_tab_content(); ?>
529
+ </div>
530
+
531
+ <?php
532
+ }
533
+
534
+ /**
535
+ * Get the current tab content.
536
+ *
537
+ * @since 1.0.0
538
+ */
539
+ public function display_current_tab_content() {
540
+
541
+ $pages = $this->get_pages();
542
+
543
+ if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
544
+ return;
545
+ }
546
+
547
+ $pages[ $this->get_current_tab() ]->display();
548
+ }
549
+
550
+ /**
551
+ * Get the current admin area tab.
552
+ *
553
+ * @since 1.0.0
554
+ *
555
+ * @return string
556
+ */
557
+ protected function get_current_tab() {
558
+
559
+ $current = '';
560
+
561
+ if ( $this->is_admin_page( 'general' ) ) {
562
+ $current = ! empty( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'settings'; // phpcs:ignore
563
+ }
564
+
565
+ return $current;
566
+ }
567
+
568
+ /**
569
+ * Get the array of default registered tabs for General page admin area.
570
+ *
571
+ * @since 1.0.0
572
+ *
573
+ * @return \WPMailSMTP\Admin\PageAbstract[]
574
+ */
575
+ public function get_pages() {
576
+
577
+ if ( empty( $this->pages ) ) {
578
+ $this->pages = array(
579
+ 'settings' => new Pages\SettingsTab(),
580
+ 'test' => new Pages\TestTab(),
581
+ 'logs' => new Pages\LogsTab(),
582
+ 'control' => new Pages\ControlTab(),
583
+ 'misc' => new Pages\MiscTab(),
584
+ 'auth' => new Pages\AuthTab(),
585
+ );
586
+ }
587
+
588
+ return apply_filters( 'wp_mail_smtp_admin_get_pages', $this->pages );
589
+ }
590
+
591
+ /**
592
+ * Get the current tab title.
593
+ *
594
+ * @since 1.0.0
595
+ *
596
+ * @return string
597
+ */
598
+ public function get_current_tab_title() {
599
+
600
+ $pages = $this->get_pages();
601
+
602
+ if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
603
+ return '';
604
+ }
605
+
606
+ return $pages[ $this->get_current_tab() ]->get_title();
607
+ }
608
+
609
+ /**
610
+ * Check whether we are on an admin page.
611
+ *
612
+ * @since 1.0.0
613
+ * @since 1.5.0 Added support for new pages.
614
+ *
615
+ * @param array|string $slug ID(s) of a plugin page. Possible values: 'general', 'logs', 'about' or array of them.
616
+ *
617
+ * @return bool
618
+ */
619
+ public function is_admin_page( $slug = array() ) {
620
+
621
+ $cur_page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore
622
+ $check = self::SLUG;
623
+ $pages_equal = false;
624
+
625
+ if ( is_string( $slug ) ) {
626
+ $slug = sanitize_key( $slug );
627
+
628
+ if (
629
+ in_array( $slug, self::$pages_registered, true ) &&
630
+ $slug !== 'general'
631
+ ) {
632
+ $check = self::SLUG . '-' . $slug;
633
+ }
634
+
635
+ $pages_equal = $cur_page === $check;
636
+ } elseif ( is_array( $slug ) ) {
637
+ if ( empty( $slug ) ) {
638
+ $slug = array_map( function ( $v ) {
639
+ if ( $v === 'general' ) {
640
+ return Area::SLUG;
641
+ }
642
+ return Area::SLUG . '-' . $v;
643
+ }, self::$pages_registered );
644
+ } else {
645
+ $slug = array_map( function ( $v ) {
646
+ if ( $v === 'general' ) {
647
+ return Area::SLUG;
648
+ }
649
+ return Area::SLUG . '-' . sanitize_key( $v );
650
+ }, $slug );
651
+ }
652
+
653
+ $pages_equal = in_array( $cur_page, $slug, true );
654
+ }
655
+
656
+ return is_admin() && $pages_equal;
657
+ }
658
+
659
+ /**
660
+ * Give ability to use either admin area option or a filter to hide error notices about failed email delivery.
661
+ * Filter has higher priority and overrides an option.
662
+ *
663
+ * @since 1.6.0
664
+ *
665
+ * @return bool
666
+ */
667
+ public function is_error_delivery_notice_enabled() {
668
+
669
+ $is_hard_enabled = (bool) apply_filters( 'wp_mail_smtp_admin_is_error_delivery_notice_enabled', true );
670
+
671
+ // If someone changed the value to false using a filter - disable completely.
672
+ if ( ! $is_hard_enabled ) {
673
+ return false;
674
+ }
675
+
676
+ return ! (bool) Options::init()->get( 'general', 'email_delivery_errors_hidden' );
677
+ }
678
+
679
+ /**
680
+ * All possible plugin forms manipulation will be done here.
681
+ *
682
+ * @since 1.0.0
683
+ */
684
+ public function process_actions() {
685
+
686
+ // Bail if we're not on a plugin General page.
687
+ if ( ! $this->is_admin_page( 'general' ) ) {
688
+ return;
689
+ }
690
+
691
+ $pages = $this->get_pages();
692
+
693
+ // Allow to process only own tabs.
694
+ if ( ! array_key_exists( $this->get_current_tab(), $pages ) ) {
695
+ return;
696
+ }
697
+
698
+ // Process POST only if it exists.
699
+ if ( ! empty( $_POST ) ) {
700
+ if ( ! empty( $_POST['wp-mail-smtp'] ) ) {
701
+ $post = $_POST['wp-mail-smtp'];
702
+ } else {
703
+ $post = array();
704
+ }
705
+
706
+ $pages[ $this->get_current_tab() ]->process_post( $post );
707
+ }
708
+
709
+ // This won't do anything for most pages.
710
+ // Works for plugin page only, when GET params are allowed.
711
+ $pages[ $this->get_current_tab() ]->process_auth();
712
+ }
713
+
714
+ /**
715
+ * Process all AJAX requests.
716
+ *
717
+ * @since 1.3.0
718
+ * @since 1.5.0 Added tasks to process plugins management.
719
+ */
720
+ public function process_ajax() {
721
+
722
+ $data = array();
723
+
724
+ // Only admins can fire these ajax requests.
725
+ if ( ! current_user_can( 'manage_options' ) ) {
726
+ wp_send_json_error( $data );
727
+ }
728
+
729
+ if ( empty( $_POST['task'] ) ) { // phpcs:ignore
730
+ wp_send_json_error( $data );
731
+ }
732
+
733
+ $task = sanitize_key( $_POST['task'] ); // phpcs:ignore
734
+
735
+ switch ( $task ) {
736
+ case 'pro_banner_dismiss':
737
+ update_user_meta( get_current_user_id(), 'wp_mail_smtp_pro_banner_dismissed', true );
738
+ $data['message'] = esc_html__( 'WP Mail SMTP Pro related message was successfully dismissed.', 'wp-mail-smtp' );
739
+ break;
740
+
741
+ case 'about_plugin_install':
742
+ Pages\About::ajax_plugin_install();
743
+ break;
744
+
745
+ case 'about_plugin_activate':
746
+ Pages\About::ajax_plugin_activate();
747
+ break;
748
+
749
+ case 'notice_dismiss':
750
+ $notice = sanitize_key( $_POST['notice'] ); // phpcs:ignore
751
+ $mailer = sanitize_key( $_POST['mailer'] ); // phpcs:ignore
752
+ if ( empty( $notice ) || empty( $mailer ) ) {
753
+ break;
754
+ }
755
+
756
+ update_user_meta( get_current_user_id(), "wp_mail_smtp_notice_{$notice}_for_{$mailer}_dismissed", true );
757
+ $data['message'] = esc_html__( 'Educational notice for this mailer was successfully dismissed.', 'wp-mail-smtp' );
758
+ break;
759
+
760
+ default:
761
+ // Allow custom tasks data processing being added here.
762
+ $data = apply_filters( 'wp_mail_smtp_admin_process_ajax_' . $task . '_data', $data );
763
+ }
764
+
765
+ // Final ability to rewrite all the data, just in case.
766
+ $data = (array) apply_filters( 'wp_mail_smtp_admin_process_ajax_data', $data, $task );
767
+
768
+ if ( empty( $data ) ) {
769
+ wp_send_json_error( $data );
770
+ }
771
+
772
+ wp_send_json_success( $data );
773
+ }
774
+
775
+ /**
776
+ * Add a link to Settings page of a plugin on Plugins page.
777
+ *
778
+ * @since 1.0.0
779
+ * @since 1.5.0 Added a link to Email Log.
780
+ *
781
+ * @param array $links
782
+ * @param string $file
783
+ *
784
+ * @return mixed
785
+ */
786
+ public function add_plugin_action_link( $links, $file ) {
787
+
788
+ // Will target both pro and lite version of a plugin.
789
+ if ( strpos( $file, 'wp-mail-smtp' ) === false ) {
790
+ return $links;
791
+ }
792
+
793
+ $settings_link = '<a href="' . esc_url( $this->get_admin_page_url() ) . '">' . esc_html__( 'Settings', 'wp-mail-smtp' ) . '</a>';
794
+ $logs_link = '<a href="' . esc_url( $this->get_admin_page_url( self::SLUG . '-logs' ) ) . '">' . esc_html__( 'Email Log', 'wp-mail-smtp' ) . '</a>';
795
+
796
+ array_unshift( $links, $settings_link, $logs_link );
797
+
798
+ return $links;
799
+ }
800
+
801
+ /**
802
+ * Get plugin admin area page URL.
803
+ *
804
+ * @since 1.0.0
805
+ * @since 1.5.0 URL is changed to support the top level position of the plugin admin area.
806
+ *
807
+ * @param string $page
808
+ *
809
+ * @return string
810
+ */
811
+ public function get_admin_page_url( $page = '' ) {
812
+
813
+ if ( empty( $page ) ) {
814
+ $page = self::SLUG;
815
+ }
816
+
817
+ return add_query_arg(
818
+ 'page',
819
+ $page,
820
+ admin_url( 'admin.php' )
821
+ );
822
+ }
823
+
824
+ /**
825
+ * Remove all non-WP Mail SMTP plugin notices from plugin pages.
826
+ *
827
+ * @since 1.0.0
828
+ */
829
+ public function hide_unrelated_notices() {
830
+
831
+ // Bail if we're not on our screen or page.
832
+ if ( empty( $_REQUEST['page'] ) || strpos( $_REQUEST['page'], self::SLUG ) === false ) {
833
+ return;
834
+ }
835
+
836
+ global $wp_filter;
837
+
838
+ if ( ! empty( $wp_filter['user_admin_notices']->callbacks ) && is_array( $wp_filter['user_admin_notices']->callbacks ) ) {
839
+ foreach ( $wp_filter['user_admin_notices']->callbacks as $priority => $hooks ) {
840
+ foreach ( $hooks as $name => $arr ) {
841
+ if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
842
+ unset( $wp_filter['user_admin_notices']->callbacks[ $priority ][ $name ] );
843
+ continue;
844
+ }
845
+ if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
846
+ continue;
847
+ }
848
+ if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
849
+ unset( $wp_filter['user_admin_notices']->callbacks[ $priority ][ $name ] );
850
+ }
851
+ }
852
+ }
853
+ }
854
+
855
+ if ( ! empty( $wp_filter['admin_notices']->callbacks ) && is_array( $wp_filter['admin_notices']->callbacks ) ) {
856
+ foreach ( $wp_filter['admin_notices']->callbacks as $priority => $hooks ) {
857
+ foreach ( $hooks as $name => $arr ) {
858
+ if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
859
+ unset( $wp_filter['admin_notices']->callbacks[ $priority ][ $name ] );
860
+ continue;
861
+ }
862
+ if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
863
+ continue;
864
+ }
865
+ if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
866
+ unset( $wp_filter['admin_notices']->callbacks[ $priority ][ $name ] );
867
+ }
868
+ }
869
+ }
870
+ }
871
+
872
+ if ( ! empty( $wp_filter['all_admin_notices']->callbacks ) && is_array( $wp_filter['all_admin_notices']->callbacks ) ) {
873
+ foreach ( $wp_filter['all_admin_notices']->callbacks as $priority => $hooks ) {
874
+ foreach ( $hooks as $name => $arr ) {
875
+ if ( is_object( $arr['function'] ) && $arr['function'] instanceof \Closure ) {
876
+ unset( $wp_filter['all_admin_notices']->callbacks[ $priority ][ $name ] );
877
+ continue;
878
+ }
879
+ if ( ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) && strpos( strtolower( get_class( $arr['function'][0] ) ), 'wpmailsmtp' ) !== false ) {
880
+ continue;
881
+ }
882
+ if ( ! empty( $name ) && strpos( strtolower( $name ), 'wpmailsmtp' ) === false ) {
883
+ unset( $wp_filter['all_admin_notices']->callbacks[ $priority ][ $name ] );
884
+ }
885
+ }
886
+ }
887
+ }
888
+ }
889
+ }
src/Admin/Pages/About.php CHANGED
@@ -1,702 +1,702 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Admin\Pages;
4
-
5
- use WPMailSMTP\Admin\Area;
6
- use WPMailSMTP\Admin\PageAbstract;
7
- use WPMailSMTP\Admin\PluginsInstallSkin;
8
- use WPMailSMTP\Admin\PluginsInstallUpgrader;
9
-
10
- /**
11
- * Class About to display a page with About Us and Versus content.
12
- *
13
- * @since 1.5.0
14
- */
15
- class About extends PageAbstract {
16
-
17
- /**
18
- * @since 1.5.0
19
- *
20
- * @var string Slug of a page.
21
- */
22
- protected $slug = 'about';
23
-
24
- /**
25
- * @since 1.5.0
26
- *
27
- * @var array List of supported tabs.
28
- */
29
- protected $tabs = array( 'about', 'versus' );
30
-
31
- /**
32
- * Get the page/tab link.
33
- *
34
- * @since 1.5.0
35
- *
36
- * @param string $tab Tab to generate a link to.
37
- *
38
- * @return string
39
- */
40
- public function get_link( $tab = '' ) {
41
-
42
- return add_query_arg(
43
- 'tab',
44
- $this->get_defined_tab( $tab ),
45
- admin_url( 'admin.php?page=' . Area::SLUG . '-' . $this->slug )
46
- );
47
- }
48
-
49
- /**
50
- * Get the current tab.
51
- *
52
- * @since 1.5.0
53
- *
54
- * @return string Current tab.
55
- */
56
- public function get_current_tab() {
57
-
58
- if ( empty( $_GET['tab'] ) ) { // phpcs:ignore
59
- return $this->slug;
60
- }
61
-
62
- return $this->get_defined_tab( $_GET['tab'] ); // phpcs:ignore
63
- }
64
-
65
- /**
66
- * Get the defined or default tab.
67
- *
68
- * @since 1.5.0
69
- *
70
- * @param string $tab Tab to check.
71
- *
72
- * @return string Defined tab. Fallback to default one if it doesn't exist.
73
- */
74
- protected function get_defined_tab( $tab ) {
75
-
76
- $tab = \sanitize_key( $tab );
77
-
78
- return \in_array( $tab, $this->tabs, true ) ? $tab : $this->slug;
79
- }
80
-
81
- /**
82
- * Get label for a tab.
83
- * Process only those that exists.
84
- * Defaults to "About Us".
85
- *
86
- * @since 1.5.0
87
- *
88
- * @param string $tab Tab to get label for.
89
- *
90
- * @return string
91
- */
92
- public function get_label( $tab = '' ) {
93
-
94
- switch ( $this->get_defined_tab( $tab ) ) {
95
- case 'versus':
96
- $label = \sprintf(
97
- /* translators: %s - plugin current license type. */
98
- \esc_html__( '%s vs Pro', 'wp-mail-smtp' ),
99
- \ucfirst( \wp_mail_smtp()->get_license_type() )
100
- );
101
- break;
102
-
103
- case 'about':
104
- default:
105
- $label = \esc_html__( 'About Us', 'wp-mail-smtp' );
106
- break;
107
- }
108
-
109
- return $label;
110
- }
111
-
112
- /**
113
- * @inheritdoc
114
- */
115
- public function get_title() {
116
- return $this->get_label( $this->get_current_tab() );
117
- }
118
-
119
- /**
120
- * Display About page content based on the current tab.
121
- *
122
- * @since 1.5.0
123
- */
124
- public function display() {
125
- ?>
126
-
127
- <div class="wp-mail-smtp-page-title">
128
- <a href="<?php echo \esc_url( $this->get_link() ); ?>" class="tab <?php echo $this->get_current_tab() === 'about' ? 'active' : ''; ?>">
129
- <?php echo \esc_html( $this->get_label( 'about' ) ); ?>
130
- </a>
131
-
132
- <?php if ( \wp_mail_smtp()->get_license_type() === 'lite' ) : ?>
133
- <a href="<?php echo \esc_url( $this->get_link( 'versus' ) ); ?>" class="tab <?php echo $this->get_current_tab() === 'versus' ? 'active' : ''; ?>">
134
- <?php echo \esc_html( $this->get_label( 'versus' ) ); ?>
135
- </a>
136
- <?php endif; ?>
137
- </div>
138
-
139
- <div class="wp-mail-smtp-page-content">
140
- <h1 class="screen-reader-text">
141
- <?php echo \esc_html( $this->get_label( $this->get_current_tab() ) ); ?>
142
- </h1>
143
-
144
- <?php
145
- $callback = 'display_' . $this->get_current_tab();
146
-
147
- if ( \method_exists( $this, $callback ) ) {
148
- $this->{$callback}();
149
- } else {
150
- $this->display_about();
151
- }
152
- ?>
153
- </div>
154
-
155
- <?php
156
- }
157
-
158
- /**
159
- * Display an "About Us" tab content.
160
- *
161
- * @since 1.5.0
162
- */
163
- protected function display_about() {
164
- ?>
165
-
166
- <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-columns">
167
-
168
- <div class="wp-mail-smtp-admin-column-60">
169
- <h3>
170
- <?php esc_html_e( 'Hello and welcome to WP Mail SMTP, the easiest and most popular WordPress SMTP plugin. We build software that helps your site reliably deliver emails every time.', 'wp-mail-smtp' ); ?>
171
- </h3>
172
-
173
- <p>
174
- <?php esc_html_e( 'Email deliverability has been a well-documented problem for all WordPress websites. However as WPForms grew, we became more aware of this painful issue that affects our users and the larger WordPress community. So we decided to solve this problem and make a solution that\'s beginner friendly.', 'wp-mail-smtp' ); ?>
175
- </p>
176
- <p>
177
- <?php esc_html_e( 'Our goal is to make reliable email deliverability easy for WordPress.', 'wp-mail-smtp' ); ?>
178
- </p>
179
- <p>
180
- <?php
181
- printf(
182
- wp_kses(
183
- /* translators: %1$s - WPBeginner URL, %2$s - OptinMonster URL, %3$s - MonsterInsights URL. */
184
- __( 'WP Mail SMTP is brought to you by the same team that\'s behind the most user friendly WordPress forms, <a href="%1$s" target="_blank" rel="noopener noreferrer">WPForms</a>, the largest WordPress resource site, <a href="%2$s" target="_blank" rel="noopener noreferrer">WPBeginner</a>, the most popular lead-generation software, <a href="%3$s" target="_blank" rel="noopener noreferrer">OptinMonster</a>, and the best WordPress analytics plugin, <a href="%4$s" target="_blank" rel="noopener noreferrer">MonsterInsights</a>.', 'wp-mail-smtp' ),
185
- array(
186
- 'a' => array(
187
- 'href' => array(),
188
- 'rel' => array(),
189
- 'target' => array(),
190
- ),
191
- )
192
- ),
193
- 'https://wpforms.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
194
- 'https://www.wpbeginner.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
195
- 'https://optinmonster.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
196
- 'https://www.monsterinsights.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp'
197
- );
198
- ?>
199
- </p>
200
- <p>
201
- <?php esc_html_e( 'Yup, we know a thing or two about building awesome products that customers love.', 'wp-mail-smtp' ); ?>
202
- </p>
203
- </div>
204
-
205
- <div class="wp-mail-smtp-admin-column-40 wp-mail-smtp-admin-column-last">
206
- <figure>
207
- <img src="<?php echo esc_url( wp_mail_smtp()->assets_url . '/images/about/team.jpg' ); ?>" alt="<?php esc_attr_e( 'The WPForms Team photo', 'wp-mail-smtp' ); ?>">
208
- <figcaption>
209
- <?php esc_html_e( 'The WPForms Team', 'wp-mail-smtp' ); ?>
210
- </figcaption>
211
- </figure>
212
- </div>
213
-
214
- </div>
215
-
216
- <div class="wp-mail-smtp-admin-about-plugins">
217
- <div class="plugins-container">
218
- <?php
219
- foreach ( $this->get_am_plugins() as $key => $plugin ) :
220
- $is_url_external = false;
221
-
222
- $data = $this->get_about_plugins_data( $plugin );
223
-
224
- if ( isset( $plugin['pro'] ) && \array_key_exists( $plugin['pro']['path'], \get_plugins() ) ) {
225
- $is_url_external = true;
226
- $plugin = $plugin['pro'];
227
-
228
- $data = array_merge( $data, $this->get_about_plugins_data( $plugin, true ) );
229
- }
230
-
231
- ?>
232
- <div class="plugin-container">
233
- <div class="plugin-item">
234
- <div class="details wp-mail-smtp-clear">
235
- <img src="<?php echo \esc_url( $plugin['icon'] ); ?>">
236
- <h5 class="plugin-name">
237
- <?php echo $plugin['name']; ?>
238
- </h5>
239
- <p class="plugin-desc">
240
- <?php echo $plugin['desc']; ?>
241
- </p>
242
- </div>
243
- <div class="actions wp-mail-smtp-clear">
244
- <div class="status">
245
- <strong>
246
- <?php
247
- \printf(
248
- /* translators: %s - status HTML text. */
249
- \esc_html__( 'Status: %s', 'wp-mail-smtp' ),
250
- '<span class="status-label ' . $data['status_class'] . '">' . $data['status_text'] . '</span>'
251
- );
252
- ?>
253
- </strong>
254
- </div>
255
- <div class="action-button">
256
- <?php
257
- $go_to_class = '';
258
- if ( $is_url_external && $data['status_class'] === 'status-download' ) {
259
- $go_to_class = 'go_to';
260
- }
261
- ?>
262
- <a href="<?php echo \esc_url( $plugin['url'] ); ?>"
263
- class="<?php echo \esc_attr( $data['action_class'] ); ?> <?php echo $go_to_class; ?>"
264
- data-plugin="<?php echo $data['plugin_src']; ?>">
265
- <?php echo $data['action_text']; ?>
266
- </a>
267
- </div>
268
- </div>
269
- </div>
270
- </div>
271
- <?php endforeach; ?>
272
- </div>
273
- </div>
274
-
275
- <?php
276
- }
277
-
278
- /**
279
- * Generate all the required CSS classed and labels to be used in rendering.
280
- *
281
- * @since 1.5.0
282
- *
283
- * @param array $plugin
284
- * @param bool $is_pro
285
- *
286
- * @return mixed
287
- */
288
- protected function get_about_plugins_data( $plugin, $is_pro = false ) {
289
-
290
- $data = array();
291
-
292
- if ( \array_key_exists( $plugin['path'], \get_plugins() ) ) {
293
- if ( \is_plugin_active( $plugin['path'] ) ) {
294
- // Status text/status.
295
- $data['status_class'] = 'status-active';
296
- $data['status_text'] = \esc_html__( 'Active', 'wp-mail-smtp' );
297
- // Button text/status.
298
- $data['action_class'] = $data['status_class'] . ' button button-secondary disabled';
299
- $data['action_text'] = \esc_html__( 'Activated', 'wp-mail-smtp' );
300
- $data['plugin_src'] = \esc_attr( $plugin['path'] );
301
- } else {
302
- // Status text/status.
303
- $data['status_class'] = 'status-inactive';
304
- $data['status_text'] = \esc_html__( 'Inactive', 'wp-mail-smtp' );
305
- // Button text/status.
306
- $data['action_class'] = $data['status_class'] . ' button button-secondary';
307
- $data['action_text'] = \esc_html__( 'Activate', 'wp-mail-smtp' );
308
- $data['plugin_src'] = \esc_attr( $plugin['path'] );
309
- }
310
- } else {
311
- if ( ! $is_pro ) {
312
- // Doesn't exist, install.
313
- // Status text/status.
314
- $data['status_class'] = 'status-download';
315
- $data['status_text'] = \esc_html__( 'Not Installed', 'wp-mail-smtp' );
316
- // Button text/status.
317
- $data['action_class'] = $data['status_class'] . ' button button-primary';
318
- $data['action_text'] = \esc_html__( 'Install Plugin', 'wp-mail-smtp' );
319
- $data['plugin_src'] = \esc_url( $plugin['url'] );
320
- }
321
- }
322
-
323
- return $data;
324
- }
325
-
326
- /**
327
- * List of AM plugins that we propose to install.
328
- *
329
- * @since 1.5.0
330
- *
331
- * @return array
332
- */
333
- private function get_am_plugins() {
334
-
335
- $data = array(
336
- 'mi' => array(
337
- 'path' => 'google-analytics-for-wordpress/googleanalytics.php',
338
- 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-mi.png',
339
- 'name' => \esc_html__( 'MonsterInsights', 'wp-mail-smtp' ),
340
- 'desc' => \esc_html__( 'MonsterInsights makes it “effortless” to properly connect your WordPress site with Google Analytics, so you can start making data-driven decisions to grow your business.', 'wp-mail-smtp' ),
341
- 'url' => 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.zip',
342
- 'pro' => array(
343
- 'path' => 'google-analytics-premium/googleanalytics-premium.php',
344
- 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-mi.png',
345
- 'name' => \esc_html__( 'MonsterInsights Pro', 'wp-mail-smtp' ),
346
- 'desc' => \esc_html__( 'MonsterInsights makes it “effortless” to properly connect your WordPress site with Google Analytics, so you can start making data-driven decisions to grow your business.', 'wp-mail-smtp' ),
347
- 'url' => 'https://www.monsterinsights.com/?utm_source=WordPress&utm_medium=about&utm_campaign=smtp',
348
- ),
349
- ),
350
- 'om' => array(
351
- 'path' => 'optinmonster/optin-monster-wp-api.php',
352
- 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-om.png',
353
- 'name' => \esc_html__( 'OptinMonster', 'wp-mail-smtp' ),
354
- 'desc' => \esc_html__( 'Our high-converting optin forms like Exit-Intent® popups, Fullscreen Welcome Mats, and Scroll boxes help you dramatically boost conversions and get more email subscribers.', 'wp-mail-smtp' ),
355
- 'url' => 'https://downloads.wordpress.org/plugin/optinmonster.zip',
356
- ),
357
- 'wpforms' => array(
358
- 'path' => 'wpforms-lite/wpforms.php',
359
- 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-wpf.png',
360
- 'name' => \esc_html__( 'Contact Forms by WPForms', 'wp-mail-smtp' ),
361
- 'desc' => \esc_html__( 'The best WordPress contact form plugin. Drag & Drop online form builder that helps you create beautiful contact forms with just a few clicks.', 'wp-mail-smtp' ),
362
- 'url' => 'https://downloads.wordpress.org/plugin/wpforms-lite.zip',
363
- 'pro' => array(
364
- 'path' => 'wpforms/wpforms.php',
365
- 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-wpf.png',
366
- 'name' => \esc_html__( 'WPForms Pro', 'wp-mail-smtp' ),
367
- 'desc' => \esc_html__( 'The best WordPress contact form plugin. Drag & Drop online form builder that helps you create beautiful contact forms with just a few clicks.', 'wp-mail-smtp' ),
368
- 'url' => 'https://wpforms.com/?utm_source=WordPress&utm_medium=about&utm_campaign=smtp',
369
- ),
370
- ),
371
- );
372
-
373
- return $data;
374
- }
375
-
376
- /**
377
- * Active the given plugin.
378
- *
379
- * @since 1.5.0
380
- */
381
- public static function ajax_plugin_activate() {
382
-
383
- // Run a security check.
384
- \check_ajax_referer( 'wp-mail-smtp-about', 'nonce' );
385
-
386
- $error = \esc_html__( 'Could not activate the plugin. Please activate it from the Plugins page.', 'wp-mail-smtp' );
387
-
388
- // Check for permissions.
389
- if ( ! \current_user_can( 'activate_plugins' ) ) {
390
- \wp_send_json_error( $error );
391
- }
392
-
393
- if ( isset( $_POST['plugin'] ) ) {
394
-
395
- $activate = \activate_plugins( $_POST['plugin'] ); // phpcs:ignore
396
-
397
- if ( ! \is_wp_error( $activate ) ) {
398
- \wp_send_json_success( esc_html__( 'Plugin activated.', 'wp-mail-smtp' ) );
399
- }
400
- }
401
-
402
- \wp_send_json_error( $error );
403
- }
404
-
405
- /**
406
- * Install & activate the given plugin.
407
- *
408
- * @since 1.5.0
409
- */
410
- public static function ajax_plugin_install() {
411
-
412
- // Run a security check.
413
- \check_ajax_referer( 'wp-mail-smtp-about', 'nonce' );
414
-
415
- $error = \esc_html__( 'Could not install the plugin.', 'wp-mail-smtp' );
416
-
417
- // Check for permissions.
418
- if ( ! \current_user_can( 'activate_plugins' ) ) {
419
- \wp_send_json_error( $error );
420
- }
421
-
422
- if ( empty( $_POST['plugin'] ) ) {
423
- \wp_send_json_error();
424
- }
425
-
426
- // Set the current screen to avoid undefined notices.
427
- \set_current_screen( 'wp-mail-smtp_page_wp-mail-smtp-about' );
428
-
429
- // Prepare variables.
430
- $url = \esc_url_raw(
431
- \add_query_arg(
432
- array(
433
- 'page' => 'wp-mail-smtp-about',
434
- ),
435
- \admin_url( 'admin.php' )
436
- )
437
- );
438
-
439
- $creds = \request_filesystem_credentials( $url, '', false, false, null );
440
-
441
- // Check for file system permissions.
442
- if ( false === $creds ) {
443
- \wp_send_json_error( $error );
444
- }
445
-
446
- if ( ! \WP_Filesystem( $creds ) ) {
447
- \wp_send_json_error( $error );
448
- }
449
-
450
- // Do not allow WordPress to search/download translations, as this will break JS output.
451
- \remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
452
-
453
- // Create the plugin upgrader with our custom skin.
454
- $installer = new PluginsInstallUpgrader( new PluginsInstallSkin() );
455
-
456
- // Error check.
457
- if ( ! \method_exists( $installer, 'install' ) || empty( $_POST['plugin'] ) ) {
458
- \wp_send_json_error( $error );
459
- }
460
-
461
- $installer->install( $_POST['plugin'] ); // phpcs:ignore
462
-
463
- // Flush the cache and return the newly installed plugin basename.
464
- \wp_cache_flush();
465
-
466
- if ( $installer->plugin_info() ) {
467
-
468
- $plugin_basename = $installer->plugin_info();
469
-
470
- // Activate the plugin silently.
471
- $activated = \activate_plugin( $plugin_basename );
472
-
473
- if ( ! \is_wp_error( $activated ) ) {
474
- \wp_send_json_success(
475
- array(
476
- 'msg' => \esc_html__( 'Plugin installed & activated.', 'wp-mail-smtp' ),
477
- 'is_activated' => true,
478
- 'basename' => $plugin_basename,
479
- )
480
- );
481
- } else {
482
- \wp_send_json_success(
483
- array(
484
- 'msg' => esc_html__( 'Plugin installed.', 'wp-mail-smtp' ),
485
- 'is_activated' => false,
486
- 'basename' => $plugin_basename,
487
- )
488
- );
489
- }
490
- }
491
-
492
- \wp_send_json_error( $error );
493
- }
494
-
495
- /**
496
- * Display a "Lite vs Pro" tab content.
497
- *
498
- * @since 1.5.0
499
- */
500
- protected function display_versus() {
501
-
502
- $license = \wp_mail_smtp()->get_license_type();
503
- ?>
504
-
505
- <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-squashed">
506
- <h1 class="centered">
507
- <strong>
508
- <?php
509
- \printf(
510
- /* translators: %s - plugin current license type. */
511
- \esc_html__( '%s vs Pro', 'wp-mail-smtp' ),
512
- \esc_html( \ucfirst( $license ) )
513
- );
514
- ?>
515
- </strong>
516
- </h1>
517
-
518
- <p class="centered <?php echo ( $license === 'pro' ? 'hidden' : '' ); ?>">
519
- <?php esc_html_e( 'Get the most out of WP Mail SMTP by upgrading to Pro and unlocking all of the powerful features.', 'wp-mail-smtp' ); ?>
520
- </p>
521
- </div>
522
-
523
- <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-squashed wp-mail-smtp-admin-about-section-hero wp-mail-smtp-admin-about-section-table">
524
-
525
- <div class="wp-mail-smtp-admin-about-section-hero-main wp-mail-smtp-admin-columns">
526
- <div class="wp-mail-smtp-admin-column-33">
527
- <h3 class="no-margin">
528
- <?php esc_html_e( 'Feature', 'wp-mail-smtp' ); ?>
529
- </h3>
530
- </div>
531
- <div class="wp-mail-smtp-admin-column-33">
532
- <h3 class="no-margin">
533
- <?php echo esc_html( ucfirst( $license ) ); ?>
534
- </h3>
535
- </div>
536
- <div class="wp-mail-smtp-admin-column-33">
537
- <h3 class="no-margin">
538
- <?php esc_html_e( 'Pro', 'wp-mail-smtp' ); ?>
539
- </h3>
540
- </div>
541
- </div>
542
- <div class="wp-mail-smtp-admin-about-section-hero-extra no-padding wp-mail-smtp-admin-columns">
543
-
544
- <table>
545
- <?php
546
- foreach ( $this->get_license_features() as $slug => $name ) {
547
- $current = $this->get_license_data( $slug, $license );
548
- $pro = $this->get_license_data( $slug, 'pro' );
549
- ?>
550
- <tr class="wp-mail-smtp-admin-columns">
551
- <td class="wp-mail-smtp-admin-column-33">
552
- <p><?php echo $name; ?></p>
553
- </td>
554
- <td class="wp-mail-smtp-admin-column-33">
555
- <p class="features-<?php echo esc_attr( $current['status'] ); ?>">
556
- <?php echo \implode( '<br>', $current['text'] ); ?>
557
- </p>
558
- </td>
559
- <td class="wp-mail-smtp-admin-column-33">
560
- <p class="features-full">
561
- <?php echo \implode( '<br>', $pro['text'] ); ?>
562
- </p>
563
- </td>
564
- </tr>
565
- <?php
566
- }
567
- ?>
568
- </table>
569
-
570
- </div>
571
-
572
- </div>
573
-
574
- <?php if ( 'lite' === $license ) : ?>
575
- <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-hero">
576
- <div class="wp-mail-smtp-admin-about-section-hero-main no-border">
577
- <h3 class="call-to-action centered">
578
- <a href="<?php echo esc_url( wp_mail_smtp()->get_upgrade_link( 'lite-vs-pro' ) ); ?>" target="_blank" rel="noopener noreferrer">
579
- <?php \esc_html_e( 'Get WP Mail SMTP Pro Today and Unlock all of these Powerful Features', 'wp-mail-smtp' ); ?>
580
- </a>
581
- </h3>
582
-
583
- <p class="centered">
584
- <?php
585
- echo \wp_kses(
586
- \__( 'Bonus: WP Mail SMTP Lite users get <span class="price-off">20% off regular price</span>, automatically applied at checkout.', 'wp-mail-smtp' ),
587
- array(
588
- 'span' => array(
589
- 'class' => array(),
590
- ),
591
- )
592
- );
593
- ?>
594
- </p>
595
- </div>
596
- </div>
597
- <?php endif; ?>
598
-
599
- <?php
600
- }
601
-
602
- /**
603
- * Get the list of features for all licenses.
604
- *
605
- * @since 1.5.0
606
- *
607
- * @return array
608
- */
609
- private function get_license_features() {
610
-
611
- return array(
612
- 'log' => \esc_html__( 'Email Log', 'wp-mail-smtp' ),
613
- 'control' => \esc_html__( 'Email Controls', 'wp-mail-smtp' ),
614
- 'mailers' => \esc_html__( 'Additional Mailers', 'wp-mail-smtp' ),
615
- 'support' => \esc_html__( 'Customer Support', 'wp-mail-smtp' ),
616
- );
617
- }
618
-
619
- /**
620
- * Get the array of data that compared the license data.
621
- *
622
- * @since 1.5.0
623
- *
624
- * @param string $feature Feature name.
625
- * @param string $license License type to get data for.
626
- *
627
- * @return array|false
628
- */
629
- private function get_license_data( $feature, $license ) {
630
-
631
- $data = array(
632
- 'log' => array(
633
- 'lite' => array(
634
- 'status' => 'none',
635
- 'text' => array(
636
- '<strong>' . esc_html__( 'Emails are not logged', 'wp-mail-smtp' ) . '</strong>',
637
- ),
638
- ),
639
- 'pro' => array(
640
- 'status' => 'full',
641
- 'text' => array(
642
- '<strong>' . esc_html__( 'Complete Email Log management inside WordPress', 'wp-mail-smtp' ) . '</strong>',
643
- ),
644
- ),
645
- ),
646
- 'control' => array(
647
- 'lite' => array(
648
- 'status' => 'none',
649
- 'text' => array(
650
- '<strong>' . esc_html__( 'No controls over whether default WordPress emails are sent', 'wp-mail-smtp' ) . '</strong>',
651
- ),
652
- ),
653
- 'pro' => array(
654
- 'status' => 'full',
655
- 'text' => array(
656
- '<strong>' . esc_html__( 'Complete Email Controls management for most default WordPress emails', 'wp-mail-smtp' ) . '</strong>',
657
- ),
658
- ),
659
- ),
660
- 'mailers' => array(
661
- 'lite' => array(
662
- 'status' => 'none',
663
- 'text' => array(
664
- '<strong>' . esc_html__( 'Only default list of mailers', 'wp-mail-smtp' ) . '</strong>',
665
- ),
666
- ),
667
- 'pro' => array(
668
- 'status' => 'full',
669
- 'text' => array(
670
- '<strong>' . esc_html__( 'Additional mailers: Microsoft Outlook (with Office365 support) and Amazon SES', 'wp-mail-smtp' ) . '</strong>',
671
- ),
672
- ),
673
- ),
674
- 'support' => array(
675
- 'lite' => array(
676
- 'status' => 'none',
677
- 'text' => array(
678
- '<strong>' . esc_html__( 'Limited Support', 'wp-mail-smtp' ) . '</strong>',
679
- ),
680
- ),
681
- 'pro' => array(
682
- 'status' => 'full',
683
- 'text' => array(
684
- '<strong>' . esc_html__( 'Priority Support', 'wp-mail-smtp' ) . '</strong>',
685
- ),
686
- ),
687
- ),
688
- );
689
-
690
- // Wrong feature?
691
- if ( ! isset( $data[ $feature ] ) ) {
692
- return false;
693
- }
694
-
695
- // Wrong license type?
696
- if ( ! isset( $data[ $feature ][ $license ] ) ) {
697
- return false;
698
- }
699
-
700
- return $data[ $feature ][ $license ];
701
- }
702
- }
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Admin\Pages;
4
+
5
+ use WPMailSMTP\Admin\Area;
6
+ use WPMailSMTP\Admin\PageAbstract;
7
+ use WPMailSMTP\Admin\PluginsInstallSkin;
8
+ use WPMailSMTP\Admin\PluginsInstallUpgrader;
9
+
10
+ /**
11
+ * Class About to display a page with About Us and Versus content.
12
+ *
13
+ * @since 1.5.0
14
+ */
15
+ class About extends PageAbstract {
16
+
17
+ /**
18
+ * @since 1.5.0
19
+ *
20
+ * @var string Slug of a page.
21
+ */
22
+ protected $slug = 'about';
23
+
24
+ /**
25
+ * @since 1.5.0
26
+ *
27
+ * @var array List of supported tabs.
28
+ */
29
+ protected $tabs = array( 'about', 'versus' );
30
+
31
+ /**
32
+ * Get the page/tab link.
33
+ *
34
+ * @since 1.5.0
35
+ *
36
+ * @param string $tab Tab to generate a link to.
37
+ *
38
+ * @return string
39
+ */
40
+ public function get_link( $tab = '' ) {
41
+
42
+ return add_query_arg(
43
+ 'tab',
44
+ $this->get_defined_tab( $tab ),
45
+ admin_url( 'admin.php?page=' . Area::SLUG . '-' . $this->slug )
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Get the current tab.
51
+ *
52
+ * @since 1.5.0
53
+ *
54
+ * @return string Current tab.
55
+ */
56
+ public function get_current_tab() {
57
+
58
+ if ( empty( $_GET['tab'] ) ) { // phpcs:ignore
59
+ return $this->slug;
60
+ }
61
+
62
+ return $this->get_defined_tab( $_GET['tab'] ); // phpcs:ignore
63
+ }
64
+
65
+ /**
66
+ * Get the defined or default tab.
67
+ *
68
+ * @since 1.5.0
69
+ *
70
+ * @param string $tab Tab to check.
71
+ *
72
+ * @return string Defined tab. Fallback to default one if it doesn't exist.
73
+ */
74
+ protected function get_defined_tab( $tab ) {
75
+
76
+ $tab = \sanitize_key( $tab );
77
+
78
+ return \in_array( $tab, $this->tabs, true ) ? $tab : $this->slug;
79
+ }
80
+
81
+ /**
82
+ * Get label for a tab.
83
+ * Process only those that exists.
84
+ * Defaults to "About Us".
85
+ *
86
+ * @since 1.5.0
87
+ *
88
+ * @param string $tab Tab to get label for.
89
+ *
90
+ * @return string
91
+ */
92
+ public function get_label( $tab = '' ) {
93
+
94
+ switch ( $this->get_defined_tab( $tab ) ) {
95
+ case 'versus':
96
+ $label = \sprintf(
97
+ /* translators: %s - plugin current license type. */
98
+ \esc_html__( '%s vs Pro', 'wp-mail-smtp' ),
99
+ \ucfirst( \wp_mail_smtp()->get_license_type() )
100
+ );
101
+ break;
102
+
103
+ case 'about':
104
+ default:
105
+ $label = \esc_html__( 'About Us', 'wp-mail-smtp' );
106
+ break;
107
+ }
108
+
109
+ return $label;
110
+ }
111
+
112
+ /**
113
+ * @inheritdoc
114
+ */
115
+ public function get_title() {
116
+ return $this->get_label( $this->get_current_tab() );
117
+ }
118
+
119
+ /**
120
+ * Display About page content based on the current tab.
121
+ *
122
+ * @since 1.5.0
123
+ */
124
+ public function display() {
125
+ ?>
126
+
127
+ <div class="wp-mail-smtp-page-title">
128
+ <a href="<?php echo \esc_url( $this->get_link() ); ?>" class="tab <?php echo $this->get_current_tab() === 'about' ? 'active' : ''; ?>">
129
+ <?php echo \esc_html( $this->get_label( 'about' ) ); ?>
130
+ </a>
131
+
132
+ <?php if ( \wp_mail_smtp()->get_license_type() === 'lite' ) : ?>
133
+ <a href="<?php echo \esc_url( $this->get_link( 'versus' ) ); ?>" class="tab <?php echo $this->get_current_tab() === 'versus' ? 'active' : ''; ?>">
134
+ <?php echo \esc_html( $this->get_label( 'versus' ) ); ?>
135
+ </a>
136
+ <?php endif; ?>
137
+ </div>
138
+
139
+ <div class="wp-mail-smtp-page-content">
140
+ <h1 class="screen-reader-text">
141
+ <?php echo \esc_html( $this->get_label( $this->get_current_tab() ) ); ?>
142
+ </h1>
143
+
144
+ <?php
145
+ $callback = 'display_' . $this->get_current_tab();
146
+
147
+ if ( \method_exists( $this, $callback ) ) {
148
+ $this->{$callback}();
149
+ } else {
150
+ $this->display_about();
151
+ }
152
+ ?>
153
+ </div>
154
+
155
+ <?php
156
+ }
157
+
158
+ /**
159
+ * Display an "About Us" tab content.
160
+ *
161
+ * @since 1.5.0
162
+ */
163
+ protected function display_about() {
164
+ ?>
165
+
166
+ <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-columns">
167
+
168
+ <div class="wp-mail-smtp-admin-column-60">
169
+ <h3>
170
+ <?php esc_html_e( 'Hello and welcome to WP Mail SMTP, the easiest and most popular WordPress SMTP plugin. We build software that helps your site reliably deliver emails every time.', 'wp-mail-smtp' ); ?>
171
+ </h3>
172
+
173
+ <p>
174
+ <?php esc_html_e( 'Email deliverability has been a well-documented problem for all WordPress websites. However as WPForms grew, we became more aware of this painful issue that affects our users and the larger WordPress community. So we decided to solve this problem and make a solution that\'s beginner friendly.', 'wp-mail-smtp' ); ?>
175
+ </p>
176
+ <p>
177
+ <?php esc_html_e( 'Our goal is to make reliable email deliverability easy for WordPress.', 'wp-mail-smtp' ); ?>
178
+ </p>
179
+ <p>
180
+ <?php
181
+ printf(
182
+ wp_kses(
183
+ /* translators: %1$s - WPBeginner URL, %2$s - OptinMonster URL, %3$s - MonsterInsights URL. */
184
+ __( 'WP Mail SMTP is brought to you by the same team that\'s behind the most user friendly WordPress forms, <a href="%1$s" target="_blank" rel="noopener noreferrer">WPForms</a>, the largest WordPress resource site, <a href="%2$s" target="_blank" rel="noopener noreferrer">WPBeginner</a>, the most popular lead-generation software, <a href="%3$s" target="_blank" rel="noopener noreferrer">OptinMonster</a>, and the best WordPress analytics plugin, <a href="%4$s" target="_blank" rel="noopener noreferrer">MonsterInsights</a>.', 'wp-mail-smtp' ),
185
+ array(
186
+ 'a' => array(
187
+ 'href' => array(),
188
+ 'rel' => array(),
189
+ 'target' => array(),
190
+ ),
191
+ )
192
+ ),
193
+ 'https://wpforms.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
194
+ 'https://www.wpbeginner.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
195
+ 'https://optinmonster.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp',
196
+ 'https://www.monsterinsights.com/?utm_source=wpmailsmtpplugin&utm_medium=pluginaboutpage&utm_campaign=aboutwpmailsmtp'
197
+ );
198
+ ?>
199
+ </p>
200
+ <p>
201
+ <?php esc_html_e( 'Yup, we know a thing or two about building awesome products that customers love.', 'wp-mail-smtp' ); ?>
202
+ </p>
203
+ </div>
204
+
205
+ <div class="wp-mail-smtp-admin-column-40 wp-mail-smtp-admin-column-last">
206
+ <figure>
207
+ <img src="<?php echo esc_url( wp_mail_smtp()->assets_url . '/images/about/team.jpg' ); ?>" alt="<?php esc_attr_e( 'The WPForms Team photo', 'wp-mail-smtp' ); ?>">
208
+ <figcaption>
209
+ <?php esc_html_e( 'The WPForms Team', 'wp-mail-smtp' ); ?>
210
+ </figcaption>
211
+ </figure>
212
+ </div>
213
+
214
+ </div>
215
+
216
+ <div class="wp-mail-smtp-admin-about-plugins">
217
+ <div class="plugins-container">
218
+ <?php
219
+ foreach ( $this->get_am_plugins() as $key => $plugin ) :
220
+ $is_url_external = false;
221
+
222
+ $data = $this->get_about_plugins_data( $plugin );
223
+
224
+ if ( isset( $plugin['pro'] ) && \array_key_exists( $plugin['pro']['path'], \get_plugins() ) ) {
225
+ $is_url_external = true;
226
+ $plugin = $plugin['pro'];
227
+
228
+ $data = array_merge( $data, $this->get_about_plugins_data( $plugin, true ) );
229
+ }
230
+
231
+ ?>
232
+ <div class="plugin-container">
233
+ <div class="plugin-item">
234
+ <div class="details wp-mail-smtp-clear">
235
+ <img src="<?php echo \esc_url( $plugin['icon'] ); ?>">
236
+ <h5 class="plugin-name">
237
+ <?php echo $plugin['name']; ?>
238
+ </h5>
239
+ <p class="plugin-desc">
240
+ <?php echo $plugin['desc']; ?>
241
+ </p>
242
+ </div>
243
+ <div class="actions wp-mail-smtp-clear">
244
+ <div class="status">
245
+ <strong>
246
+ <?php
247
+ \printf(
248
+ /* translators: %s - status HTML text. */
249
+ \esc_html__( 'Status: %s', 'wp-mail-smtp' ),
250
+ '<span class="status-label ' . $data['status_class'] . '">' . $data['status_text'] . '</span>'
251
+ );
252
+ ?>
253
+ </strong>
254
+ </div>
255
+ <div class="action-button">
256
+ <?php
257
+ $go_to_class = '';
258
+ if ( $is_url_external && $data['status_class'] === 'status-download' ) {
259
+ $go_to_class = 'go_to';
260
+ }
261
+ ?>
262
+ <a href="<?php echo \esc_url( $plugin['url'] ); ?>"
263
+ class="<?php echo \esc_attr( $data['action_class'] ); ?> <?php echo $go_to_class; ?>"
264
+ data-plugin="<?php echo $data['plugin_src']; ?>">
265
+ <?php echo $data['action_text']; ?>
266
+ </a>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ <?php endforeach; ?>
272
+ </div>
273
+ </div>
274
+
275
+ <?php
276
+ }
277
+
278
+ /**
279
+ * Generate all the required CSS classed and labels to be used in rendering.
280
+ *
281
+ * @since 1.5.0
282
+ *
283
+ * @param array $plugin
284
+ * @param bool $is_pro
285
+ *
286
+ * @return mixed
287
+ */
288
+ protected function get_about_plugins_data( $plugin, $is_pro = false ) {
289
+
290
+ $data = array();
291
+
292
+ if ( \array_key_exists( $plugin['path'], \get_plugins() ) ) {
293
+ if ( \is_plugin_active( $plugin['path'] ) ) {
294
+ // Status text/status.
295
+ $data['status_class'] = 'status-active';
296
+ $data['status_text'] = \esc_html__( 'Active', 'wp-mail-smtp' );
297
+ // Button text/status.
298
+ $data['action_class'] = $data['status_class'] . ' button button-secondary disabled';
299
+ $data['action_text'] = \esc_html__( 'Activated', 'wp-mail-smtp' );
300
+ $data['plugin_src'] = \esc_attr( $plugin['path'] );
301
+ } else {
302
+ // Status text/status.
303
+ $data['status_class'] = 'status-inactive';
304
+ $data['status_text'] = \esc_html__( 'Inactive', 'wp-mail-smtp' );
305
+ // Button text/status.
306
+ $data['action_class'] = $data['status_class'] . ' button button-secondary';
307
+ $data['action_text'] = \esc_html__( 'Activate', 'wp-mail-smtp' );
308
+ $data['plugin_src'] = \esc_attr( $plugin['path'] );
309
+ }
310
+ } else {
311
+ if ( ! $is_pro ) {
312
+ // Doesn't exist, install.
313
+ // Status text/status.
314
+ $data['status_class'] = 'status-download';
315
+ $data['status_text'] = \esc_html__( 'Not Installed', 'wp-mail-smtp' );
316
+ // Button text/status.
317
+ $data['action_class'] = $data['status_class'] . ' button button-primary';
318
+ $data['action_text'] = \esc_html__( 'Install Plugin', 'wp-mail-smtp' );
319
+ $data['plugin_src'] = \esc_url( $plugin['url'] );
320
+ }
321
+ }
322
+
323
+ return $data;
324
+ }
325
+
326
+ /**
327
+ * List of AM plugins that we propose to install.
328
+ *
329
+ * @since 1.5.0
330
+ *
331
+ * @return array
332
+ */
333
+ private function get_am_plugins() {
334
+
335
+ $data = array(
336
+ 'mi' => array(
337
+ 'path' => 'google-analytics-for-wordpress/googleanalytics.php',
338
+ 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-mi.png',
339
+ 'name' => \esc_html__( 'MonsterInsights', 'wp-mail-smtp' ),
340
+ 'desc' => \esc_html__( 'MonsterInsights makes it “effortless” to properly connect your WordPress site with Google Analytics, so you can start making data-driven decisions to grow your business.', 'wp-mail-smtp' ),
341
+ 'url' => 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.zip',
342
+ 'pro' => array(
343
+ 'path' => 'google-analytics-premium/googleanalytics-premium.php',
344
+ 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-mi.png',
345
+ 'name' => \esc_html__( 'MonsterInsights Pro', 'wp-mail-smtp' ),
346
+ 'desc' => \esc_html__( 'MonsterInsights makes it “effortless” to properly connect your WordPress site with Google Analytics, so you can start making data-driven decisions to grow your business.', 'wp-mail-smtp' ),
347
+ 'url' => 'https://www.monsterinsights.com/?utm_source=WordPress&utm_medium=about&utm_campaign=smtp',
348
+ ),
349
+ ),
350
+ 'om' => array(
351
+ 'path' => 'optinmonster/optin-monster-wp-api.php',
352
+ 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-om.png',
353
+ 'name' => \esc_html__( 'OptinMonster', 'wp-mail-smtp' ),
354
+ 'desc' => \esc_html__( 'Our high-converting optin forms like Exit-Intent® popups, Fullscreen Welcome Mats, and Scroll boxes help you dramatically boost conversions and get more email subscribers.', 'wp-mail-smtp' ),
355
+ 'url' => 'https://downloads.wordpress.org/plugin/optinmonster.zip',
356
+ ),
357
+ 'wpforms' => array(
358
+ 'path' => 'wpforms-lite/wpforms.php',
359
+ 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-wpf.png',
360
+ 'name' => \esc_html__( 'Contact Forms by WPForms', 'wp-mail-smtp' ),
361
+ 'desc' => \esc_html__( 'The best WordPress contact form plugin. Drag & Drop online form builder that helps you create beautiful contact forms with just a few clicks.', 'wp-mail-smtp' ),
362
+ 'url' => 'https://downloads.wordpress.org/plugin/wpforms-lite.zip',
363
+ 'pro' => array(
364
+ 'path' => 'wpforms/wpforms.php',
365
+ 'icon' => \wp_mail_smtp()->assets_url . '/images/about/plugin-wpf.png',
366
+ 'name' => \esc_html__( 'WPForms Pro', 'wp-mail-smtp' ),
367
+ 'desc' => \esc_html__( 'The best WordPress contact form plugin. Drag & Drop online form builder that helps you create beautiful contact forms with just a few clicks.', 'wp-mail-smtp' ),
368
+ 'url' => 'https://wpforms.com/?utm_source=WordPress&utm_medium=about&utm_campaign=smtp',
369
+ ),
370
+ ),
371
+ );
372
+
373
+ return $data;
374
+ }
375
+
376
+ /**
377
+ * Active the given plugin.
378
+ *
379
+ * @since 1.5.0
380
+ */
381
+ public static function ajax_plugin_activate() {
382
+
383
+ // Run a security check.
384
+ \check_ajax_referer( 'wp-mail-smtp-about', 'nonce' );
385
+
386
+ $error = \esc_html__( 'Could not activate the plugin. Please activate it from the Plugins page.', 'wp-mail-smtp' );
387
+
388
+ // Check for permissions.
389
+ if ( ! \current_user_can( 'activate_plugins' ) ) {
390
+ \wp_send_json_error( $error );
391
+ }
392
+
393
+ if ( isset( $_POST['plugin'] ) ) {
394
+
395
+ $activate = \activate_plugins( $_POST['plugin'] ); // phpcs:ignore
396
+
397
+ if ( ! \is_wp_error( $activate ) ) {
398
+ \wp_send_json_success( esc_html__( 'Plugin activated.', 'wp-mail-smtp' ) );
399
+ }
400
+ }
401
+
402
+ \wp_send_json_error( $error );
403
+ }
404
+
405
+ /**
406
+ * Install & activate the given plugin.
407
+ *
408
+ * @since 1.5.0
409
+ */
410
+ public static function ajax_plugin_install() {
411
+
412
+ // Run a security check.
413
+ \check_ajax_referer( 'wp-mail-smtp-about', 'nonce' );
414
+
415
+ $error = \esc_html__( 'Could not install the plugin.', 'wp-mail-smtp' );
416
+
417
+ // Check for permissions.
418
+ if ( ! \current_user_can( 'activate_plugins' ) ) {
419
+ \wp_send_json_error( $error );
420
+ }
421
+
422
+ if ( empty( $_POST['plugin'] ) ) {
423
+ \wp_send_json_error();
424
+ }
425
+
426
+ // Set the current screen to avoid undefined notices.
427
+ \set_current_screen( 'wp-mail-smtp_page_wp-mail-smtp-about' );
428
+
429
+ // Prepare variables.
430
+ $url = \esc_url_raw(
431
+ \add_query_arg(
432
+ array(
433
+ 'page' => 'wp-mail-smtp-about',
434
+ ),
435
+ \admin_url( 'admin.php' )
436
+ )
437
+ );
438
+
439
+ $creds = \request_filesystem_credentials( $url, '', false, false, null );
440
+
441
+ // Check for file system permissions.
442
+ if ( false === $creds ) {
443
+ \wp_send_json_error( $error );
444
+ }
445
+
446
+ if ( ! \WP_Filesystem( $creds ) ) {
447
+ \wp_send_json_error( $error );
448
+ }
449
+
450
+ // Do not allow WordPress to search/download translations, as this will break JS output.
451
+ \remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
452
+
453
+ // Create the plugin upgrader with our custom skin.
454
+ $installer = new PluginsInstallUpgrader( new PluginsInstallSkin() );
455
+
456
+ // Error check.
457
+ if ( ! \method_exists( $installer, 'install' ) || empty( $_POST['plugin'] ) ) {
458
+ \wp_send_json_error( $error );
459
+ }
460
+
461
+ $installer->install( $_POST['plugin'] ); // phpcs:ignore
462
+
463
+ // Flush the cache and return the newly installed plugin basename.
464
+ \wp_cache_flush();
465
+
466
+ if ( $installer->plugin_info() ) {
467
+
468
+ $plugin_basename = $installer->plugin_info();
469
+
470
+ // Activate the plugin silently.
471
+ $activated = \activate_plugin( $plugin_basename );
472
+
473
+ if ( ! \is_wp_error( $activated ) ) {
474
+ \wp_send_json_success(
475
+ array(
476
+ 'msg' => \esc_html__( 'Plugin installed & activated.', 'wp-mail-smtp' ),
477
+ 'is_activated' => true,
478
+ 'basename' => $plugin_basename,
479
+ )
480
+ );
481
+ } else {
482
+ \wp_send_json_success(
483
+ array(
484
+ 'msg' => esc_html__( 'Plugin installed.', 'wp-mail-smtp' ),
485
+ 'is_activated' => false,
486
+ 'basename' => $plugin_basename,
487
+ )
488
+ );
489
+ }
490
+ }
491
+
492
+ \wp_send_json_error( $error );
493
+ }
494
+
495
+ /**
496
+ * Display a "Lite vs Pro" tab content.
497
+ *
498
+ * @since 1.5.0
499
+ */
500
+ protected function display_versus() {
501
+
502
+ $license = \wp_mail_smtp()->get_license_type();
503
+ ?>
504
+
505
+ <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-squashed">
506
+ <h1 class="centered">
507
+ <strong>
508
+ <?php
509
+ \printf(
510
+ /* translators: %s - plugin current license type. */
511
+ \esc_html__( '%s vs Pro', 'wp-mail-smtp' ),
512
+ \esc_html( \ucfirst( $license ) )
513
+ );
514
+ ?>
515
+ </strong>
516
+ </h1>
517
+
518
+ <p class="centered <?php echo ( $license === 'pro' ? 'hidden' : '' ); ?>">
519
+ <?php esc_html_e( 'Get the most out of WP Mail SMTP by upgrading to Pro and unlocking all of the powerful features.', 'wp-mail-smtp' ); ?>
520
+ </p>
521
+ </div>
522
+
523
+ <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-squashed wp-mail-smtp-admin-about-section-hero wp-mail-smtp-admin-about-section-table">
524
+
525
+ <div class="wp-mail-smtp-admin-about-section-hero-main wp-mail-smtp-admin-columns">
526
+ <div class="wp-mail-smtp-admin-column-33">
527
+ <h3 class="no-margin">
528
+ <?php esc_html_e( 'Feature', 'wp-mail-smtp' ); ?>
529
+ </h3>
530
+ </div>
531
+ <div class="wp-mail-smtp-admin-column-33">
532
+ <h3 class="no-margin">
533
+ <?php echo esc_html( ucfirst( $license ) ); ?>
534
+ </h3>
535
+ </div>
536
+ <div class="wp-mail-smtp-admin-column-33">
537
+ <h3 class="no-margin">
538
+ <?php esc_html_e( 'Pro', 'wp-mail-smtp' ); ?>
539
+ </h3>
540
+ </div>
541
+ </div>
542
+ <div class="wp-mail-smtp-admin-about-section-hero-extra no-padding wp-mail-smtp-admin-columns">
543
+
544
+ <table>
545
+ <?php
546
+ foreach ( $this->get_license_features() as $slug => $name ) {
547
+ $current = $this->get_license_data( $slug, $license );
548
+ $pro = $this->get_license_data( $slug, 'pro' );
549
+ ?>
550
+ <tr class="wp-mail-smtp-admin-columns">
551
+ <td class="wp-mail-smtp-admin-column-33">
552
+ <p><?php echo $name; ?></p>
553
+ </td>
554
+ <td class="wp-mail-smtp-admin-column-33">
555
+ <p class="features-<?php echo esc_attr( $current['status'] ); ?>">
556
+ <?php echo \implode( '<br>', $current['text'] ); ?>
557
+ </p>
558
+ </td>
559
+ <td class="wp-mail-smtp-admin-column-33">
560
+ <p class="features-full">
561
+ <?php echo \implode( '<br>', $pro['text'] ); ?>
562
+ </p>
563
+ </td>
564
+ </tr>
565
+ <?php
566
+ }
567
+ ?>
568
+ </table>
569
+
570
+ </div>
571
+
572
+ </div>
573
+
574
+ <?php if ( 'lite' === $license ) : ?>
575
+ <div class="wp-mail-smtp-admin-about-section wp-mail-smtp-admin-about-section-hero">
576
+ <div class="wp-mail-smtp-admin-about-section-hero-main no-border">
577
+ <h3 class="call-to-action centered">
578
+ <a href="<?php echo esc_url( wp_mail_smtp()->get_upgrade_link( 'lite-vs-pro' ) ); ?>" target="_blank" rel="noopener noreferrer">
579
+ <?php \esc_html_e( 'Get WP Mail SMTP Pro Today and Unlock all of these Powerful Features', 'wp-mail-smtp' ); ?>
580
+ </a>
581
+ </h3>
582
+
583
+ <p class="centered">
584
+ <?php
585
+ echo \wp_kses(
586
+ \__( 'Bonus: WP Mail SMTP Lite users get <span class="price-off">20% off regular price</span>, automatically applied at checkout.', 'wp-mail-smtp' ),
587
+ array(
588
+ 'span' => array(
589
+ 'class' => array(),
590
+ ),
591
+ )
592
+ );
593
+ ?>
594
+ </p>
595
+ </div>
596
+ </div>
597
+ <?php endif; ?>
598
+
599
+ <?php
600
+ }
601
+
602
+ /**
603
+ * Get the list of features for all licenses.
604
+ *
605
+ * @since 1.5.0
606
+ *
607
+ * @return array
608
+ */
609
+ private function get_license_features() {
610
+
611
+ return array(
612
+ 'log' => \esc_html__( 'Email Log', 'wp-mail-smtp' ),
613
+ 'control' => \esc_html__( 'Email Controls', 'wp-mail-smtp' ),
614
+ 'mailers' => \esc_html__( 'Additional Mailers', 'wp-mail-smtp' ),
615
+ 'support' => \esc_html__( 'Customer Support', 'wp-mail-smtp' ),
616
+ );
617
+ }
618
+
619
+ /**
620
+ * Get the array of data that compared the license data.
621
+ *
622
+ * @since 1.5.0
623
+ *
624
+ * @param string $feature Feature name.
625
+ * @param string $license License type to get data for.
626
+ *
627
+ * @return array|false
628
+ */
629
+ private function get_license_data( $feature, $license ) {
630
+
631
+ $data = array(
632
+ 'log' => array(
633
+ 'lite' => array(
634
+ 'status' => 'none',
635
+ 'text' => array(
636
+ '<strong>' . esc_html__( 'Emails are not logged', 'wp-mail-smtp' ) . '</strong>',
637
+ ),
638
+ ),
639
+ 'pro' => array(
640
+ 'status' => 'full',
641
+ 'text' => array(
642
+ '<strong>' . esc_html__( 'Complete Email Log management inside WordPress', 'wp-mail-smtp' ) . '</strong>',
643
+ ),
644
+ ),
645
+ ),
646
+ 'control' => array(
647
+ 'lite' => array(
648
+ 'status' => 'none',
649
+ 'text' => array(
650
+ '<strong>' . esc_html__( 'No controls over whether default WordPress emails are sent', 'wp-mail-smtp' ) . '</strong>',
651
+ ),
652
+ ),
653
+ 'pro' => array(
654
+ 'status' => 'full',
655
+ 'text' => array(
656
+ '<strong>' . esc_html__( 'Complete Email Controls management for most default WordPress emails', 'wp-mail-smtp' ) . '</strong>',
657
+ ),
658
+ ),
659
+ ),
660
+ 'mailers' => array(
661
+ 'lite' => array(
662
+ 'status' => 'none',
663
+ 'text' => array(
664
+ '<strong>' . esc_html__( 'Only default list of mailers', 'wp-mail-smtp' ) . '</strong>',
665
+ ),
666
+ ),
667
+ 'pro' => array(
668
+ 'status' => 'full',
669
+ 'text' => array(
670
+ '<strong>' . esc_html__( 'Additional mailers: Microsoft Outlook (with Office365 support) and Amazon SES', 'wp-mail-smtp' ) . '</strong>',
671
+ ),
672
+ ),
673
+ ),
674
+ 'support' => array(
675
+ 'lite' => array(
676
+ 'status' => 'none',
677
+ 'text' => array(
678
+ '<strong>' . esc_html__( 'Limited Support', 'wp-mail-smtp' ) . '</strong>',
679
+ ),
680
+ ),
681
+ 'pro' => array(
682
+ 'status' => 'full',
683
+ 'text' => array(
684
+ '<strong>' . esc_html__( 'Priority Support', 'wp-mail-smtp' ) . '</strong>',
685
+ ),
686
+ ),
687
+ ),
688
+ );
689
+
690
+ // Wrong feature?
691
+ if ( ! isset( $data[ $feature ] ) ) {
692
+ return false;
693
+ }
694
+
695
+ // Wrong license type?
696
+ if ( ! isset( $data[ $feature ][ $license ] ) ) {
697
+ return false;
698
+ }
699
+
700
+ return $data[ $feature ][ $license ];
701
+ }
702
+ }
src/Admin/Pages/SettingsTab.php CHANGED
@@ -1,537 +1,546 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Admin\Pages;
4
-
5
- use WPMailSMTP\Admin\PageAbstract;
6
- use WPMailSMTP\Debug;
7
- use WPMailSMTP\Options;
8
- use WPMailSMTP\WP;
9
-
10
- /**
11
- * Class SettingsTab is part of Area, displays general settings of the plugin.
12
- *
13
- * @since 1.0.0
14
- */
15
- class SettingsTab extends PageAbstract {
16
-
17
- /**
18
- * Settings constructor.
19
- *
20
- * @since 1.5.0
21
- */
22
- public function __construct() {
23
-
24
- add_action( 'wp_mail_smtp_admin_pages_settings_license_key', array( __CLASS__, 'display_license_key_field_content' ) );
25
- }
26
-
27
- /**
28
- * @var string Slug of a tab.
29
- */
30
- protected $slug = 'settings';
31
-
32
- /**
33
- * @inheritdoc
34
- */
35
- public function get_label() {
36
- return esc_html__( 'General', 'wp-mail-smtp' );
37
- }
38
-
39
- /**
40
- * @inheritdoc
41
- */
42
- public function get_title() {
43
- return $this->get_label();
44
- }
45
-
46
- /**
47
- * @inheritdoc
48
- */
49
- public function display() {
50
-
51
- $options = new Options();
52
- $mailer = $options->get( 'mail', 'mailer' );
53
-
54
- $disabled_email = 'gmail' === $mailer || 'outlook' === $mailer ? 'disabled' : '';
55
- $disabled_name = 'outlook' === $mailer ? 'disabled' : '';
56
- ?>
57
-
58
- <form method="POST" action="" autocomplete="off">
59
- <?php $this->wp_nonce_field(); ?>
60
-
61
- <!-- License Section Title -->
62
- <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading" id="wp-mail-smtp-setting-row-license-heading">
63
- <div class="wp-mail-smtp-setting-field">
64
- <h2><?php esc_html_e( 'License', 'wp-mail-smtp' ); ?></h2>
65
-
66
- <p class="desc">
67
- <?php esc_html_e( 'Your license key provides access to updates and support.', 'wp-mail-smtp' ); ?>
68
- </p>
69
- </div>
70
- </div>
71
-
72
- <!-- License Key -->
73
- <div id="wp-mail-smtp-setting-row-license_key" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-license_key wp-mail-smtp-clear">
74
- <div class="wp-mail-smtp-setting-label">
75
- <label for="wp-mail-smtp-setting-license_key"><?php esc_html_e( 'License Key', 'wp-mail-smtp' ); ?></label>
76
- </div>
77
- <div class="wp-mail-smtp-setting-field">
78
- <?php do_action( 'wp_mail_smtp_admin_pages_settings_license_key', $options ); ?>
79
- </div>
80
- </div>
81
-
82
- <!-- Mail Section Title -->
83
- <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading no-desc" id="wp-mail-smtp-setting-row-email-heading">
84
- <div class="wp-mail-smtp-setting-field">
85
- <h2><?php esc_html_e( 'Mail', 'wp-mail-smtp' ); ?></h2>
86
- </div>
87
- </div>
88
-
89
- <!-- From Email -->
90
- <div id="wp-mail-smtp-setting-row-from_email" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-email wp-mail-smtp-clear">
91
- <div class="wp-mail-smtp-setting-label">
92
- <label for="wp-mail-smtp-setting-from_email"><?php esc_html_e( 'From Email', 'wp-mail-smtp' ); ?></label>
93
- </div>
94
- <div class="wp-mail-smtp-setting-field">
95
- <input name="wp-mail-smtp[mail][from_email]" type="email"
96
- value="<?php echo esc_attr( $options->get( 'mail', 'from_email' ) ); ?>"
97
- <?php echo $options->is_const_defined( 'mail', 'from_email' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
98
- id="wp-mail-smtp-setting-from_email" spellcheck="false"
99
- placeholder="<?php echo esc_attr( wp_mail_smtp()->get_processor()->get_default_email() ); ?>">
100
-
101
- <?php if ( empty( $disabled_email ) ) : ?>
102
- <p class="desc">
103
- <?php esc_html_e( 'The email address which emails are sent from.', 'wp-mail-smtp' ); ?><br/>
104
- <?php esc_html_e( 'If you using an email provider (Gmail, Yahoo, Outlook.com, etc) this should be your email address for that account.', 'wp-mail-smtp' ); ?>
105
- </p>
106
- <p class="desc">
107
- <?php esc_html_e( 'Please note that other plugins can change this, to prevent this use the setting below.', 'wp-mail-smtp' ); ?>
108
- </p>
109
- <?php endif; ?>
110
-
111
- <hr class="wp-mail-smtp-setting-mid-row-sep">
112
-
113
- <input name="wp-mail-smtp[mail][from_email_force]" type="checkbox"
114
- value="true" <?php checked( true, (bool) $options->get( 'mail', 'from_email_force' ) ); ?>
115
- <?php echo $options->is_const_defined( 'mail', 'from_email_force' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
116
- id="wp-mail-smtp-setting-from_email_force">
117
-
118
- <label for="wp-mail-smtp-setting-from_email_force">
119
- <?php esc_html_e( 'Force From Email', 'wp-mail-smtp' ); ?>
120
- </label>
121
-
122
- <?php if ( ! empty( $disabled_email ) ) : ?>
123
- <p class="desc">
124
- <?php esc_html_e( 'Current provider will automatically force From Email to be the email address that you use to set up the connection below.', 'wp-mail-smtp' ); ?>
125
- </p>
126
- <?php else : ?>
127
- <p class="desc">
128
- <?php esc_html_e( 'If checked, the From Email setting above will be used for all emails, ignoring values set by other plugins.', 'wp-mail-smtp' ); ?>
129
- </p>
130
- <?php endif; ?>
131
-
132
- </div>
133
- </div>
134
-
135
- <!-- From Name -->
136
- <div id="wp-mail-smtp-setting-row-from_name" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
137
- <div class="wp-mail-smtp-setting-label">
138
- <label for="wp-mail-smtp-setting-from_name"><?php esc_html_e( 'From Name', 'wp-mail-smtp' ); ?></label>
139
- </div>
140
- <div class="wp-mail-smtp-setting-field">
141
- <input name="wp-mail-smtp[mail][from_name]" type="text"
142
- value="<?php echo esc_attr( $options->get( 'mail', 'from_name' ) ); ?>"
143
- <?php echo $options->is_const_defined( 'mail', 'from_name' ) || ! empty( $disabled_name ) ? 'disabled' : ''; ?>
144
- id="wp-mail-smtp-setting-from_name" spellcheck="false"
145
- placeholder="<?php echo esc_attr( wp_mail_smtp()->get_processor()->get_default_name() ); ?>">
146
-
147
- <?php if ( empty( $disabled_name ) ) : ?>
148
- <p class="desc">
149
- <?php esc_html_e( 'The name which emails are sent from.', 'wp-mail-smtp' ); ?>
150
- </p>
151
- <?php endif; ?>
152
-
153
- <hr class="wp-mail-smtp-setting-mid-row-sep">
154
-
155
- <input name="wp-mail-smtp[mail][from_name_force]" type="checkbox"
156
- value="true" <?php checked( true, (bool) $options->get( 'mail', 'from_name_force' ) ); ?>
157
- <?php echo $options->is_const_defined( 'mail', 'from_name_force' ) || ! empty( $disabled_name ) ? 'disabled' : ''; ?>
158
- id="wp-mail-smtp-setting-from_name_force">
159
-
160
- <label for="wp-mail-smtp-setting-from_name_force">
161
- <?php esc_html_e( 'Force From Name', 'wp-mail-smtp' ); ?>
162
- </label>
163
-
164
- <?php if ( ! empty( $disabled_name ) ) : ?>
165
- <p class="desc">
166
- <?php esc_html_e( 'Current provider doesn\'t support setting and forcing From Name. Emails will be sent on behalf of the account name used to setup the connection below.', 'wp-mail-smtp' ); ?>
167
- </p>
168
- <?php else : ?>
169
- <p class="desc">
170
- <?php esc_html_e( 'If checked, the From Name setting above will be used for all emails, ignoring values set by other plugins.', 'wp-mail-smtp' ); ?>
171
- </p>
172
- <?php endif; ?>
173
- </div>
174
- </div>
175
-
176
- <!-- Return Path -->
177
- <div id="wp-mail-smtp-setting-row-return_path" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-checkbox wp-mail-smtp-clear">
178
- <div class="wp-mail-smtp-setting-label">
179
- <label for="wp-mail-smtp-setting-return_path"><?php esc_html_e( 'Return Path', 'wp-mail-smtp' ); ?></label>
180
- </div>
181
- <div class="wp-mail-smtp-setting-field">
182
- <input name="wp-mail-smtp[mail][return_path]" type="checkbox"
183
- value="true" <?php checked( true, (bool) $options->get( 'mail', 'return_path' ) ); ?>
184
- <?php echo $options->is_const_defined( 'mail', 'return_path' ) ? 'disabled' : ''; ?>
185
- id="wp-mail-smtp-setting-return_path">
186
-
187
- <label for="wp-mail-smtp-setting-return_path">
188
- <?php esc_html_e( 'Set the return-path to match the From Email', 'wp-mail-smtp' ); ?>
189
- </label>
190
-
191
- <p class="desc">
192
- <?php esc_html_e( 'Return Path indicates where non-delivery receipts - or bounce messages - are to be sent.', 'wp-mail-smtp' ); ?><br/>
193
- <?php esc_html_e( 'If unchecked, bounce messages may be lost. Some providers may ignore this option.', 'wp-mail-smtp' ); ?>
194
- </p>
195
- </div>
196
- </div>
197
-
198
- <!-- Mailer -->
199
- <div id="wp-mail-smtp-setting-row-mailer" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-mailer wp-mail-smtp-clear">
200
- <div class="wp-mail-smtp-setting-label">
201
- <label for="wp-mail-smtp-setting-mailer"><?php esc_html_e( 'Mailer', 'wp-mail-smtp' ); ?></label>
202
- </div>
203
- <div class="wp-mail-smtp-setting-field">
204
- <div class="wp-mail-smtp-mailers">
205
-
206
- <?php foreach ( wp_mail_smtp()->get_providers()->get_options_all() as $provider ) : ?>
207
-
208
- <div class="wp-mail-smtp-mailer wp-mail-smtp-mailer-<?php echo esc_attr( $provider->get_slug() ); ?> <?php echo $mailer === $provider->get_slug() ? 'active' : ''; ?>">
209
-
210
- <div class="wp-mail-smtp-mailer-image <?php echo $provider->is_recommended() ? 'is-recommended' : ''; ?>">
211
- <img src="<?php echo esc_url( $provider->get_logo_url() ); ?>"
212
- alt="<?php echo esc_attr( $provider->get_title() ); ?>">
213
- </div>
214
-
215
- <div class="wp-mail-smtp-mailer-text">
216
- <?php if ( $provider->is_disabled() ) : ?>
217
- <input type="radio" name="wp-mail-smtp[mail][mailer]" disabled class="educate"
218
- id="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>"
219
- value="<?php echo esc_attr( $provider->get_slug() ); ?>"
220
- />
221
- <?php else : ?>
222
- <input id="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>"
223
- type="radio" name="wp-mail-smtp[mail][mailer]"
224
- value="<?php echo esc_attr( $provider->get_slug() ); ?>"
225
- <?php checked( $provider->get_slug(), $mailer ); ?>
226
- <?php echo $options->is_const_defined( 'mail', 'mailer' ) || $provider->is_disabled() ? 'disabled' : ''; ?>
227
- <?php echo $provider->is_disabled() ? 'class="educate"' : ''; ?>
228
- />
229
- <?php endif; ?>
230
- <label for="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>">
231
- <?php echo esc_html( $provider->get_title() ); ?>
232
- </label>
233
- </div>
234
- </div>
235
-
236
- <?php endforeach; ?>
237
-
238
- </div>
239
- </div>
240
- </div>
241
-
242
- <!-- Mailer Options -->
243
- <div class="wp-mail-smtp-mailer-options">
244
- <?php foreach ( wp_mail_smtp()->get_providers()->get_options_all() as $provider ) : ?>
245
-
246
- <div class="wp-mail-smtp-mailer-option wp-mail-smtp-mailer-option-<?php echo esc_attr( $provider->get_slug() ); ?> <?php echo $mailer === $provider->get_slug() ? 'active' : 'hidden'; ?>">
247
-
248
- <!-- Mailer Title/Notice/Description -->
249
- <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading <?php echo empty( $provider_desc ) ? 'no-desc' : ''; ?>" id="wp-mail-smtp-setting-row-email-heading">
250
- <div class="wp-mail-smtp-setting-field">
251
- <?php if ( $provider->is_disabled() ) : ?>
252
- <?php $provider->display_options(); ?>
253
- <?php else : ?>
254
- <h2><?php echo $provider->get_title(); ?></h2>
255
-
256
- <?php
257
- $provider_desc = $provider->get_description();
258
- $provider_edu_notice = $provider->get_notice( 'educational' );
259
- $is_dismissed = (bool) get_user_meta( get_current_user_id(), "wp_mail_smtp_notice_educational_for_{$provider->get_slug()}_dismissed", true );
260
- if ( ! empty( $provider_edu_notice ) && ! $is_dismissed ) :
261
- ?>
262
- <p class="inline-notice inline-edu-notice"
263
- data-notice="educational"
264
- data-mailer="<?php echo esc_attr( $provider->get_slug() ); ?>">
265
- <a href="#" title="<?php esc_attr_e( 'Dismiss this notice', 'wp-mail-smtp' ); ?>"
266
- class="wp-mail-smtp-mailer-notice-dismiss js-wp-mail-smtp-mailer-notice-dismiss">
267
- <span class="dashicons dashicons-dismiss"></span>
268
- </a>
269
-
270
- <?php echo $provider_edu_notice; ?>
271
- </p>
272
- <?php endif; ?>
273
-
274
- <?php if ( ! empty( $provider_desc ) ) : ?>
275
- <p class="desc"><?php echo $provider_desc; ?></p>
276
- <?php endif; ?>
277
- <?php endif; ?>
278
- </div>
279
- </div>
280
-
281
- <?php $provider->display_options(); ?>
282
- </div>
283
-
284
- <?php endforeach; ?>
285
-
286
- </div>
287
-
288
- <?php $this->display_save_btn(); ?>
289
-
290
- </form>
291
-
292
- <?php
293
- $this->display_wpforms();
294
- $this->display_pro_banner();
295
- }
296
-
297
- /**
298
- * License key text for a Lite version of the plugin.
299
- *
300
- * @since 1.5.0
301
- *
302
- * @param Options $options
303
- */
304
- public static function display_license_key_field_content( $options ) {
305
- ?>
306
-
307
- <p><?php esc_html_e( 'You\'re using WP Mail SMTP Lite - no license needed. Enjoy!', 'wp-mail-smtp' ); ?> 🙂</p>
308
-
309
- <p>
310
- <?php
311
- printf(
312
- wp_kses( /* translators: %s - WPMailSMTP.com upgrade URL. */
313
- __( 'To unlock more features consider <strong><a href="%s" target="_blank" rel="noopener noreferrer" class="wp-mail-smtp-upgrade-modal">upgrading to PRO</a></strong>.', 'wp-mail-smtp' ),
314
- array(
315
- 'a' => array(
316
- 'href' => array(),
317
- 'class' => array(),
318
- 'target' => array(),
319
- 'rel' => array(),
320
- ),
321
- 'strong' => array(),
322
- )
323
- ),
324
- esc_url( wp_mail_smtp()->get_upgrade_link( 'general-license-key' ) )
325
- );
326
- ?>
327
- </p>
328
-
329
- <p class="desc">
330
- <?php
331
- echo wp_kses(
332
- __( 'As a valued WP Mail SMTP Lite user you receive <strong>20% off</strong>, automatically applied at checkout!', 'wp-mail-smtp' ),
333
- array(
334
- 'strong' => array(),
335
- 'br' => array(),
336
- )
337
- );
338
- ?>
339
- </p>
340
-
341
- <?php
342
- }
343
-
344
- /**
345
- * Display a WPForms related message.
346
- *
347
- * @since 1.3.0
348
- * @since 1.4.0 Display only to site admins.
349
- * @since 1.5.0 Do nothing.
350
- */
351
- protected function display_wpforms() {
352
- /*
353
- * Used to have this check:
354
- *
355
- * $is_dismissed = get_user_meta( get_current_user_id(), 'wp_mail_smtp_wpforms_dismissed', true );
356
- */
357
- }
358
-
359
- /**
360
- * Display WP Mail SMTP Pro upgrade banner.
361
- *
362
- * @since 1.5.0
363
- */
364
- protected function display_pro_banner() {
365
-
366
- // Display only to site admins. Only site admins can install plugins.
367
- if ( ! is_super_admin() ) {
368
- return;
369
- }
370
-
371
- // Do not display if WP Mail SMTP Pro already installed.
372
- if ( wp_mail_smtp()->is_pro() ) {
373
- return;
374
- }
375
-
376
- $is_dismissed = get_user_meta( get_current_user_id(), 'wp_mail_smtp_pro_banner_dismissed', true );
377
-
378
- // Do not display if user dismissed.
379
- if ( (bool) $is_dismissed === true ) {
380
- return;
381
- }
382
- ?>
383
-
384
- <div id="wp-mail-smtp-pro-banner">
385
-
386
- <span class="wp-mail-smtp-pro-banner-dismiss">
387
- <button id="wp-mail-smtp-pro-banner-dismiss">
388
- <span class="dashicons dashicons-dismiss"></span>
389
- </button>
390
- </span>
391
-
392
- <h2>
393
- <?php esc_html_e( 'Get WP Mail SMTP Pro and Unlock all the Powerful Features', 'wp-mail-smtp' ); ?>
394
- </h2>
395
-
396
- <p>
397
- <?php esc_html_e( 'Thanks for being a loyal WP Mail SMTP user. Upgrade to WP Mail SMTP Pro to unlock more awesome features and experience why WP Mail SMTP is the most popular SMTP plugin.', 'wp-mail-smtp' ); ?>
398
- </p>
399
-
400
- <p>
401
- <?php esc_html_e( 'We know that you will truly love WP Mail SMTP. It\'s used by over 1,000,000 websites.', 'wp-mail-smtp' ); ?>
402
- </p>
403
-
404
- <p><strong><?php esc_html_e( 'Pro Features:', 'wp-mail-smtp' ); ?></strong></p>
405
-
406
- <div class="benefits">
407
- <ul>
408
- <li><?php esc_html_e( 'Manage Notifications - control which emails your site sends', 'wp-mail-smtp' ); ?></li>
409
- <li><?php esc_html_e( 'Email Logging - keep track of every email sent from your site', 'wp-mail-smtp' ); ?></li>
410
- <li><?php esc_html_e( 'Office 365 - send emails using your Office 365 account', 'wp-mail-smtp' ); ?></li>
411
- <li><?php esc_html_e( 'Amazon SES - harness the power of AWS', 'wp-mail-smtp' ); ?></li>
412
- <li><?php esc_html_e( 'Outlook.com - send emails using your Outlook.com account', 'wp-mail-smtp' ); ?></li>
413
- <li><?php esc_html_e( 'Access to our world class support team', 'wp-mail-smtp' ); ?></li>
414
- </ul>
415
- <ul>
416
- <li><?php esc_html_e( 'White Glove Setup - sit back and relax while we handle everything for you', 'wp-mail-smtp' ); ?></li>
417
- <li class="arrow-right"><?php esc_html_e( 'Install WP Mail SMTP Pro plugin', 'wp-mail-smtp' ); ?></li>
418
- <li class="arrow-right"><?php esc_html_e( 'Set up domain name verification (DNS)', 'wp-mail-smtp' ); ?></li>
419
- <li class="arrow-right"><?php esc_html_e( 'Configure Mailgun service', 'wp-mail-smtp' ); ?></li>
420
- <li class="arrow-right"><?php esc_html_e( 'Set up WP Mail SMTP Pro plugin', 'wp-mail-smtp' ); ?></li>
421
- <li class="arrow-right"><?php esc_html_e( 'Test and verify email delivery', 'wp-mail-smtp' ); ?></li>
422
- </ul>
423
- </div>
424
-
425
- <p>
426
- <?php
427
- printf(
428
- wp_kses( /* translators: %s - WPMailSMTP.com URL. */
429
- __( '<a href="%s" target="_blank" rel="noopener noreferrer">Get WP Mail SMTP Pro Today and Unlock all the Powerful Features &raquo;</a>', 'wp-mail-smtp' ),
430
- array(
431
- 'a' => array(
432
- 'href' => array(),
433
- 'target' => array(),
434
- 'rel' => array(),
435
- ),
436
- 'strong' => array(),
437
- )
438
- ),
439
- esc_url( wp_mail_smtp()->get_upgrade_link( 'general-cta' ) )
440
- );
441
- ?>
442
- </p>
443
-
444
- <p>
445
- <?php
446
- echo wp_kses(
447
- __( '<strong>Bonus:</strong> WP Mail SMTP users get <span class="price-off">20% off regular price</span>, automatically applied at checkout.', 'wp-mail-smtp' ),
448
- array(
449
- 'strong' => array(),
450
- 'span' => array(
451
- 'class' => array(),
452
- ),
453
- )
454
- );
455
- ?>
456
- </p>
457
-
458
- </div>
459
-
460
- <?php
461
- }
462
-
463
- /**
464
- * @inheritdoc
465
- */
466
- public function process_post( $data ) {
467
-
468
- $this->check_admin_referer();
469
-
470
- $options = new Options();
471
- $old_opt = $options->get_all();
472
-
473
- // When checkbox is unchecked - it's not submitted at all, so we need to define its default false value.
474
- if ( ! isset( $data['mail']['from_email_force'] ) ) {
475
- $data['mail']['from_email_force'] = false;
476
- }
477
- if ( ! isset( $data['mail']['from_name_force'] ) ) {
478
- $data['mail']['from_name_force'] = false;
479
- }
480
- if ( ! isset( $data['mail']['return_path'] ) ) {
481
- $data['mail']['return_path'] = false;
482
- }
483
- if ( ! isset( $data['smtp']['autotls'] ) ) {
484
- $data['smtp']['autotls'] = false;
485
- }
486
- if ( ! isset( $data['smtp']['auth'] ) ) {
487
- $data['smtp']['auth'] = false;
488
- }
489
-
490
- // Remove all debug messages when switching mailers.
491
- if (
492
- ! empty( $old_opt['mail']['mailer'] ) &&
493
- ! empty( $data['mail']['mailer'] ) &&
494
- $old_opt['mail']['mailer'] !== $data['mail']['mailer']
495
- ) {
496
- Debug::clear();
497
- }
498
-
499
- $to_redirect = false;
500
-
501
- // Old and new Gmail client id/secret values are different - we need to invalidate tokens and scroll to Auth button.
502
- if (
503
- $options->get( 'mail', 'mailer' ) === 'gmail' &&
504
- ! empty( $data['gmail']['client_id'] ) &&
505
- ! empty( $data['gmail']['client_secret'] ) &&
506
- (
507
- $options->get( 'gmail', 'client_id' ) !== $data['gmail']['client_id'] ||
508
- $options->get( 'gmail', 'client_secret' ) !== $data['gmail']['client_secret']
509
- )
510
- ) {
511
- unset( $old_opt['gmail'] );
512
-
513
- if (
514
- ! empty( $data['gmail']['client_id'] ) &&
515
- ! empty( $data['gmail']['client_secret'] )
516
- ) {
517
- $to_redirect = true;
518
- }
519
- }
520
-
521
- // New gmail clients data will be added from new $data.
522
- $to_save = Options::array_merge_recursive( $old_opt, $data );
523
-
524
- // All the sanitization is done in Options class.
525
- $options->set( $to_save );
526
-
527
- if ( $to_redirect ) {
528
- wp_redirect( $_POST['_wp_http_referer'] . '#wp-mail-smtp-setting-row-gmail-authorize' );
529
- exit;
530
- }
531
-
532
- WP::add_admin_notice(
533
- esc_html__( 'Settings were successfully saved.', 'wp-mail-smtp' ),
534
- WP::ADMIN_NOTICE_SUCCESS
535
- );
536
- }
537
- }
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Admin\Pages;
4
+
5
+ use WPMailSMTP\Admin\PageAbstract;
6
+ use WPMailSMTP\Debug;
7
+ use WPMailSMTP\Options;
8
+ use WPMailSMTP\WP;
9
+
10
+ /**
11
+ * Class SettingsTab is part of Area, displays general settings of the plugin.
12
+ *
13
+ * @since 1.0.0
14
+ */
15
+ class SettingsTab extends PageAbstract {
16
+
17
+ /**
18
+ * Settings constructor.
19
+ *
20
+ * @since 1.5.0
21
+ */
22
+ public function __construct() {
23
+
24
+ add_action( 'wp_mail_smtp_admin_pages_settings_license_key', array( __CLASS__, 'display_license_key_field_content' ) );
25
+ }
26
+
27
+ /**
28
+ * @var string Slug of a tab.
29
+ */
30
+ protected $slug = 'settings';
31
+
32
+ /**
33
+ * @inheritdoc
34
+ */
35
+ public function get_label() {
36
+ return esc_html__( 'General', 'wp-mail-smtp' );
37
+ }
38
+
39
+ /**
40
+ * @inheritdoc
41
+ */
42
+ public function get_title() {
43
+ return $this->get_label();
44
+ }
45
+
46
+ /**
47
+ * @inheritdoc
48
+ */
49
+ public function display() {
50
+
51
+ $options = new Options();
52
+ $mailer = $options->get( 'mail', 'mailer' );
53
+
54
+ $disabled_email = 'gmail' === $mailer || 'outlook' === $mailer ? 'disabled' : '';
55
+ $disabled_name = 'outlook' === $mailer ? 'disabled' : '';
56
+ ?>
57
+
58
+ <form method="POST" action="" autocomplete="off">
59
+ <?php $this->wp_nonce_field(); ?>
60
+
61
+ <!-- License Section Title -->
62
+ <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading" id="wp-mail-smtp-setting-row-license-heading">
63
+ <div class="wp-mail-smtp-setting-field">
64
+ <h2><?php esc_html_e( 'License', 'wp-mail-smtp' ); ?></h2>
65
+
66
+ <p class="desc">
67
+ <?php esc_html_e( 'Your license key provides access to updates and support.', 'wp-mail-smtp' ); ?>
68
+ </p>
69
+ </div>
70
+ </div>
71
+
72
+ <!-- License Key -->
73
+ <div id="wp-mail-smtp-setting-row-license_key" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-license_key wp-mail-smtp-clear">
74
+ <div class="wp-mail-smtp-setting-label">
75
+ <label for="wp-mail-smtp-setting-license_key"><?php esc_html_e( 'License Key', 'wp-mail-smtp' ); ?></label>
76
+ </div>
77
+ <div class="wp-mail-smtp-setting-field">
78
+ <?php do_action( 'wp_mail_smtp_admin_pages_settings_license_key', $options ); ?>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Mail Section Title -->
83
+ <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading no-desc" id="wp-mail-smtp-setting-row-email-heading">
84
+ <div class="wp-mail-smtp-setting-field">
85
+ <h2><?php esc_html_e( 'Mail', 'wp-mail-smtp' ); ?></h2>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- From Email -->
90
+ <div id="wp-mail-smtp-setting-row-from_email" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-email wp-mail-smtp-clear">
91
+ <div class="wp-mail-smtp-setting-label">
92
+ <label for="wp-mail-smtp-setting-from_email"><?php esc_html_e( 'From Email', 'wp-mail-smtp' ); ?></label>
93
+ </div>
94
+ <div class="wp-mail-smtp-setting-field">
95
+ <input name="wp-mail-smtp[mail][from_email]" type="email"
96
+ value="<?php echo esc_attr( $options->get( 'mail', 'from_email' ) ); ?>"
97
+ <?php echo $options->is_const_defined( 'mail', 'from_email' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
98
+ id="wp-mail-smtp-setting-from_email" spellcheck="false"
99
+ placeholder="<?php echo esc_attr( wp_mail_smtp()->get_processor()->get_default_email() ); ?>">
100
+
101
+ <?php if ( empty( $disabled_email ) ) : ?>
102
+ <p class="desc">
103
+ <?php esc_html_e( 'The email address which emails are sent from.', 'wp-mail-smtp' ); ?><br/>
104
+ <?php esc_html_e( 'If you using an email provider (Gmail, Yahoo, Outlook.com, etc) this should be your email address for that account.', 'wp-mail-smtp' ); ?>
105
+ </p>
106
+ <p class="desc">
107
+ <?php esc_html_e( 'Please note that other plugins can change this, to prevent this use the setting below.', 'wp-mail-smtp' ); ?>
108
+ </p>
109
+ <?php endif; ?>
110
+
111
+ <hr class="wp-mail-smtp-setting-mid-row-sep">
112
+
113
+ <input name="wp-mail-smtp[mail][from_email_force]" type="checkbox"
114
+ value="true" <?php checked( true, (bool) $options->get( 'mail', 'from_email_force' ) ); ?>
115
+ <?php echo $options->is_const_defined( 'mail', 'from_email_force' ) || ! empty( $disabled_email ) ? 'disabled' : ''; ?>
116
+ id="wp-mail-smtp-setting-from_email_force">
117
+
118
+ <label for="wp-mail-smtp-setting-from_email_force">
119
+ <?php esc_html_e( 'Force From Email', 'wp-mail-smtp' ); ?>
120
+ </label>
121
+
122
+ <?php if ( ! empty( $disabled_email ) ) : ?>
123
+ <p class="desc">
124
+ <?php esc_html_e( 'Current provider will automatically force From Email to be the email address that you use to set up the connection below.', 'wp-mail-smtp' ); ?>
125
+ </p>
126
+ <?php else : ?>
127
+ <p class="desc">
128
+ <?php esc_html_e( 'If checked, the From Email setting above will be used for all emails, ignoring values set by other plugins.', 'wp-mail-smtp' ); ?>
129
+ </p>
130
+ <?php endif; ?>
131
+
132
+ </div>
133
+ </div>
134
+
135
+ <!-- From Name -->
136
+ <div id="wp-mail-smtp-setting-row-from_name" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
137
+ <div class="wp-mail-smtp-setting-label">
138
+ <label for="wp-mail-smtp-setting-from_name"><?php esc_html_e( 'From Name', 'wp-mail-smtp' ); ?></label>
139
+ </div>
140
+ <div class="wp-mail-smtp-setting-field">
141
+ <input name="wp-mail-smtp[mail][from_name]" type="text"
142
+ value="<?php echo esc_attr( $options->get( 'mail', 'from_name' ) ); ?>"
143
+ <?php echo $options->is_const_defined( 'mail', 'from_name' ) || ! empty( $disabled_name ) ? 'disabled' : ''; ?>
144
+ id="wp-mail-smtp-setting-from_name" spellcheck="false"
145
+ placeholder="<?php echo esc_attr( wp_mail_smtp()->get_processor()->get_default_name() ); ?>">
146
+
147
+ <?php if ( empty( $disabled_name ) ) : ?>
148
+ <p class="desc">
149
+ <?php esc_html_e( 'The name which emails are sent from.', 'wp-mail-smtp' ); ?>
150
+ </p>
151
+ <?php endif; ?>
152
+
153
+ <hr class="wp-mail-smtp-setting-mid-row-sep">
154
+
155
+ <input name="wp-mail-smtp[mail][from_name_force]" type="checkbox"
156
+ value="true" <?php checked( true, (bool) $options->get( 'mail', 'from_name_force' ) ); ?>
157
+ <?php echo $options->is_const_defined( 'mail', 'from_name_force' ) || ! empty( $disabled_name ) ? 'disabled' : ''; ?>
158
+ id="wp-mail-smtp-setting-from_name_force">
159
+
160
+ <label for="wp-mail-smtp-setting-from_name_force">
161
+ <?php esc_html_e( 'Force From Name', 'wp-mail-smtp' ); ?>
162
+ </label>
163
+
164
+ <?php if ( ! empty( $disabled_name ) ) : ?>
165
+ <p class="desc">
166
+ <?php esc_html_e( 'Current provider doesn\'t support setting and forcing From Name. Emails will be sent on behalf of the account name used to setup the connection below.', 'wp-mail-smtp' ); ?>
167
+ </p>
168
+ <?php else : ?>
169
+ <p class="desc">
170
+ <?php esc_html_e( 'If checked, the From Name setting above will be used for all emails, ignoring values set by other plugins.', 'wp-mail-smtp' ); ?>
171
+ </p>
172
+ <?php endif; ?>
173
+ </div>
174
+ </div>
175
+
176
+ <!-- Return Path -->
177
+ <div id="wp-mail-smtp-setting-row-return_path" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-checkbox wp-mail-smtp-clear">
178
+ <div class="wp-mail-smtp-setting-label">
179
+ <label for="wp-mail-smtp-setting-return_path"><?php esc_html_e( 'Return Path', 'wp-mail-smtp' ); ?></label>
180
+ </div>
181
+ <div class="wp-mail-smtp-setting-field">
182
+ <input name="wp-mail-smtp[mail][return_path]" type="checkbox"
183
+ value="true" <?php checked( true, (bool) $options->get( 'mail', 'return_path' ) ); ?>
184
+ <?php echo $options->is_const_defined( 'mail', 'return_path' ) ? 'disabled' : ''; ?>
185
+ id="wp-mail-smtp-setting-return_path">
186
+
187
+ <label for="wp-mail-smtp-setting-return_path">
188
+ <?php esc_html_e( 'Set the return-path to match the From Email', 'wp-mail-smtp' ); ?>
189
+ </label>
190
+
191
+ <p class="desc">
192
+ <?php esc_html_e( 'Return Path indicates where non-delivery receipts - or bounce messages - are to be sent.', 'wp-mail-smtp' ); ?><br/>
193
+ <?php esc_html_e( 'If unchecked, bounce messages may be lost. Some providers may ignore this option.', 'wp-mail-smtp' ); ?>
194
+ </p>
195
+ </div>
196
+ </div>
197
+
198
+ <!-- Mailer -->
199
+ <div id="wp-mail-smtp-setting-row-mailer" class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-mailer wp-mail-smtp-clear">
200
+ <div class="wp-mail-smtp-setting-label">
201
+ <label for="wp-mail-smtp-setting-mailer"><?php esc_html_e( 'Mailer', 'wp-mail-smtp' ); ?></label>
202
+ </div>
203
+ <div class="wp-mail-smtp-setting-field">
204
+ <div class="wp-mail-smtp-mailers">
205
+
206
+ <?php foreach ( wp_mail_smtp()->get_providers()->get_options_all() as $provider ) : ?>
207
+
208
+ <div class="wp-mail-smtp-mailer wp-mail-smtp-mailer-<?php echo esc_attr( $provider->get_slug() ); ?> <?php echo $mailer === $provider->get_slug() ? 'active' : ''; ?>">
209
+
210
+ <div class="wp-mail-smtp-mailer-image <?php echo $provider->is_recommended() ? 'is-recommended' : ''; ?>">
211
+ <img src="<?php echo esc_url( $provider->get_logo_url() ); ?>"
212
+ alt="<?php echo esc_attr( $provider->get_title() ); ?>">
213
+ </div>
214
+
215
+ <div class="wp-mail-smtp-mailer-text">
216
+ <?php if ( $provider->is_disabled() ) : ?>
217
+ <input type="radio" name="wp-mail-smtp[mail][mailer]" disabled class="educate"
218
+ id="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>"
219
+ value="<?php echo esc_attr( $provider->get_slug() ); ?>"
220
+ />
221
+ <?php else : ?>
222
+ <input id="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>"
223
+ type="radio" name="wp-mail-smtp[mail][mailer]"
224
+ value="<?php echo esc_attr( $provider->get_slug() ); ?>"
225
+ <?php checked( $provider->get_slug(), $mailer ); ?>
226
+ <?php echo $options->is_const_defined( 'mail', 'mailer' ) || $provider->is_disabled() ? 'disabled' : ''; ?>
227
+ <?php echo $provider->is_disabled() ? 'class="educate"' : ''; ?>
228
+ />
229
+ <?php endif; ?>
230
+ <label for="wp-mail-smtp-setting-mailer-<?php echo esc_attr( $provider->get_slug() ); ?>">
231
+ <?php echo esc_html( $provider->get_title() ); ?>
232
+ </label>
233
+ </div>
234
+ </div>
235
+
236
+ <?php endforeach; ?>
237
+
238
+ <!-- Suggest a mailer -->
239
+ <div class="wp-mail-smtp-mailer suggest-new">
240
+ <a href="https://wpmailsmtp.com/suggest-a-mailer" class="wp-mail-smtp-mailer-image" target="_blank" rel="noopener noreferrer">
241
+ <?php esc_html_e( 'Suggest a Mailer', 'wp-mail-smtp' ); ?>
242
+ </a>
243
+
244
+ <div class="wp-mail-smtp-mailer-text">
245
+ <label><?php esc_html_e( 'Suggest a Mailer', 'wp-mail-smtp' ); ?></label>
246
+ </div>
247
+ </div>
248
+
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <!-- Mailer Options -->
254
+ <div class="wp-mail-smtp-mailer-options">
255
+ <?php foreach ( wp_mail_smtp()->get_providers()->get_options_all() as $provider ) : ?>
256
+ <?php $provider_desc = $provider->get_description(); ?>
257
+ <div class="wp-mail-smtp-mailer-option wp-mail-smtp-mailer-option-<?php echo esc_attr( $provider->get_slug() ); ?> <?php echo $mailer === $provider->get_slug() ? 'active' : 'hidden'; ?>">
258
+
259
+ <!-- Mailer Title/Notice/Description -->
260
+ <div class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-content wp-mail-smtp-clear section-heading <?php echo empty( $provider_desc ) ? 'no-desc' : ''; ?>" id="wp-mail-smtp-setting-row-email-heading">
261
+ <div class="wp-mail-smtp-setting-field">
262
+ <?php if ( $provider->is_disabled() ) : ?>
263
+ <?php $provider->display_options(); ?>
264
+ <?php else : ?>
265
+ <h2><?php echo $provider->get_title(); ?></h2>
266
+ <?php
267
+ $provider_edu_notice = $provider->get_notice( 'educational' );
268
+ $is_dismissed = (bool) get_user_meta( get_current_user_id(), "wp_mail_smtp_notice_educational_for_{$provider->get_slug()}_dismissed", true );
269
+ if ( ! empty( $provider_edu_notice ) && ! $is_dismissed ) :
270
+ ?>
271
+ <p class="inline-notice inline-edu-notice"
272
+ data-notice="educational"
273
+ data-mailer="<?php echo esc_attr( $provider->get_slug() ); ?>">
274
+ <a href="#" title="<?php esc_attr_e( 'Dismiss this notice', 'wp-mail-smtp' ); ?>"
275
+ class="wp-mail-smtp-mailer-notice-dismiss js-wp-mail-smtp-mailer-notice-dismiss">
276
+ <span class="dashicons dashicons-dismiss"></span>
277
+ </a>
278
+
279
+ <?php echo $provider_edu_notice; ?>
280
+ </p>
281
+ <?php endif; ?>
282
+
283
+ <?php if ( ! empty( $provider_desc ) ) : ?>
284
+ <p class="desc"><?php echo $provider_desc; ?></p>
285
+ <?php endif; ?>
286
+ <?php endif; ?>
287
+ </div>
288
+ </div>
289
+
290
+ <?php $provider->display_options(); ?>
291
+ </div>
292
+
293
+ <?php endforeach; ?>
294
+
295
+ </div>
296
+
297
+ <?php $this->display_save_btn(); ?>
298
+
299
+ </form>
300
+
301
+ <?php
302
+ $this->display_wpforms();
303
+ $this->display_pro_banner();
304
+ }
305
+
306
+ /**
307
+ * License key text for a Lite version of the plugin.
308
+ *
309
+ * @since 1.5.0
310
+ *
311
+ * @param Options $options
312
+ */
313
+ public static function display_license_key_field_content( $options ) {
314
+ ?>
315
+
316
+ <p><?php esc_html_e( 'You\'re using WP Mail SMTP Lite - no license needed. Enjoy!', 'wp-mail-smtp' ); ?> 🙂</p>
317
+
318
+ <p>
319
+ <?php
320
+ printf(
321
+ wp_kses( /* translators: %s - WPMailSMTP.com upgrade URL. */
322
+ __( 'To unlock more features consider <strong><a href="%s" target="_blank" rel="noopener noreferrer" class="wp-mail-smtp-upgrade-modal">upgrading to PRO</a></strong>.', 'wp-mail-smtp' ),
323
+ array(
324
+ 'a' => array(
325
+ 'href' => array(),
326
+ 'class' => array(),
327
+ 'target' => array(),
328
+ 'rel' => array(),
329
+ ),
330
+ 'strong' => array(),
331
+ )
332
+ ),
333
+ esc_url( wp_mail_smtp()->get_upgrade_link( 'general-license-key' ) )
334
+ );
335
+ ?>
336
+ </p>
337
+
338
+ <p class="desc">
339
+ <?php
340
+ echo wp_kses(
341
+ __( 'As a valued WP Mail SMTP Lite user you receive <strong>20% off</strong>, automatically applied at checkout!', 'wp-mail-smtp' ),
342
+ array(
343
+ 'strong' => array(),
344
+ 'br' => array(),
345
+ )
346
+ );
347
+ ?>
348
+ </p>
349
+
350
+ <?php
351
+ }
352
+
353
+ /**
354
+ * Display a WPForms related message.
355
+ *
356
+ * @since 1.3.0
357
+ * @since 1.4.0 Display only to site admins.
358
+ * @since 1.5.0 Do nothing.
359
+ */
360
+ protected function display_wpforms() {
361
+ /*
362
+ * Used to have this check:
363
+ *
364
+ * $is_dismissed = get_user_meta( get_current_user_id(), 'wp_mail_smtp_wpforms_dismissed', true );
365
+ */
366
+ }
367
+
368
+ /**
369
+ * Display WP Mail SMTP Pro upgrade banner.
370
+ *
371
+ * @since 1.5.0
372
+ */
373
+ protected function display_pro_banner() {
374
+
375
+ // Display only to site admins. Only site admins can install plugins.
376
+ if ( ! is_super_admin() ) {
377
+ return;
378
+ }
379
+
380
+ // Do not display if WP Mail SMTP Pro already installed.
381
+ if ( wp_mail_smtp()->is_pro() ) {
382
+ return;
383
+ }
384
+
385
+ $is_dismissed = get_user_meta( get_current_user_id(), 'wp_mail_smtp_pro_banner_dismissed', true );
386
+
387
+ // Do not display if user dismissed.
388
+ if ( (bool) $is_dismissed === true ) {
389
+ return;
390
+ }
391
+ ?>
392
+
393
+ <div id="wp-mail-smtp-pro-banner">
394
+
395
+ <span class="wp-mail-smtp-pro-banner-dismiss">
396
+ <button id="wp-mail-smtp-pro-banner-dismiss">
397
+ <span class="dashicons dashicons-dismiss"></span>
398
+ </button>
399
+ </span>
400
+
401
+ <h2>
402
+ <?php esc_html_e( 'Get WP Mail SMTP Pro and Unlock all the Powerful Features', 'wp-mail-smtp' ); ?>
403
+ </h2>
404
+
405
+ <p>
406
+ <?php esc_html_e( 'Thanks for being a loyal WP Mail SMTP user. Upgrade to WP Mail SMTP Pro to unlock more awesome features and experience why WP Mail SMTP is the most popular SMTP plugin.', 'wp-mail-smtp' ); ?>
407
+ </p>
408
+
409
+ <p>
410
+ <?php esc_html_e( 'We know that you will truly love WP Mail SMTP. It\'s used by over 1,000,000 websites.', 'wp-mail-smtp' ); ?>
411
+ </p>
412
+
413
+ <p><strong><?php esc_html_e( 'Pro Features:', 'wp-mail-smtp' ); ?></strong></p>
414
+
415
+ <div class="benefits">
416
+ <ul>
417
+ <li><?php esc_html_e( 'Manage Notifications - control which emails your site sends', 'wp-mail-smtp' ); ?></li>
418
+ <li><?php esc_html_e( 'Email Logging - keep track of every email sent from your site', 'wp-mail-smtp' ); ?></li>
419
+ <li><?php esc_html_e( 'Office 365 - send emails using your Office 365 account', 'wp-mail-smtp' ); ?></li>
420
+ <li><?php esc_html_e( 'Amazon SES - harness the power of AWS', 'wp-mail-smtp' ); ?></li>
421
+ <li><?php esc_html_e( 'Outlook.com - send emails using your Outlook.com account', 'wp-mail-smtp' ); ?></li>
422
+ <li><?php esc_html_e( 'Access to our world class support team', 'wp-mail-smtp' ); ?></li>
423
+ </ul>
424
+ <ul>
425
+ <li><?php esc_html_e( 'White Glove Setup - sit back and relax while we handle everything for you', 'wp-mail-smtp' ); ?></li>
426
+ <li class="arrow-right"><?php esc_html_e( 'Install WP Mail SMTP Pro plugin', 'wp-mail-smtp' ); ?></li>
427
+ <li class="arrow-right"><?php esc_html_e( 'Set up domain name verification (DNS)', 'wp-mail-smtp' ); ?></li>
428
+ <li class="arrow-right"><?php esc_html_e( 'Configure Mailgun service', 'wp-mail-smtp' ); ?></li>
429
+ <li class="arrow-right"><?php esc_html_e( 'Set up WP Mail SMTP Pro plugin', 'wp-mail-smtp' ); ?></li>
430
+ <li class="arrow-right"><?php esc_html_e( 'Test and verify email delivery', 'wp-mail-smtp' ); ?></li>
431
+ </ul>
432
+ </div>
433
+
434
+ <p>
435
+ <?php
436
+ printf(
437
+ wp_kses( /* translators: %s - WPMailSMTP.com URL. */
438
+ __( '<a href="%s" target="_blank" rel="noopener noreferrer">Get WP Mail SMTP Pro Today and Unlock all the Powerful Features &raquo;</a>', 'wp-mail-smtp' ),
439
+ array(
440
+ 'a' => array(
441
+ 'href' => array(),
442
+ 'target' => array(),
443
+ 'rel' => array(),
444
+ ),
445
+ 'strong' => array(),
446
+ )
447
+ ),
448
+ esc_url( wp_mail_smtp()->get_upgrade_link( 'general-cta' ) )
449
+ );
450
+ ?>
451
+ </p>
452
+
453
+ <p>
454
+ <?php
455
+ echo wp_kses(
456
+ __( '<strong>Bonus:</strong> WP Mail SMTP users get <span class="price-off">20% off regular price</span>, automatically applied at checkout.', 'wp-mail-smtp' ),
457
+ array(
458
+ 'strong' => array(),
459
+ 'span' => array(
460
+ 'class' => array(),
461
+ ),
462
+ )
463
+ );
464
+ ?>
465
+ </p>
466
+
467
+ </div>
468
+
469
+ <?php
470
+ }
471
+
472
+ /**
473
+ * @inheritdoc
474
+ */
475
+ public function process_post( $data ) {
476
+
477
+ $this->check_admin_referer();
478
+
479
+ $options = new Options();
480
+ $old_opt = $options->get_all();
481
+
482
+ // When checkbox is unchecked - it's not submitted at all, so we need to define its default false value.
483
+ if ( ! isset( $data['mail']['from_email_force'] ) ) {
484
+ $data['mail']['from_email_force'] = false;
485
+ }
486
+ if ( ! isset( $data['mail']['from_name_force'] ) ) {
487
+ $data['mail']['from_name_force'] = false;
488
+ }
489
+ if ( ! isset( $data['mail']['return_path'] ) ) {
490
+ $data['mail']['return_path'] = false;
491
+ }
492
+ if ( ! isset( $data['smtp']['autotls'] ) ) {
493
+ $data['smtp']['autotls'] = false;
494
+ }
495
+ if ( ! isset( $data['smtp']['auth'] ) ) {
496
+ $data['smtp']['auth'] = false;
497
+ }
498
+
499
+ // Remove all debug messages when switching mailers.
500
+ if (
501
+ ! empty( $old_opt['mail']['mailer'] ) &&
502
+ ! empty( $data['mail']['mailer'] ) &&
503
+ $old_opt['mail']['mailer'] !== $data['mail']['mailer']
504
+ ) {
505
+ Debug::clear();
506
+ }
507
+
508
+ $to_redirect = false;
509
+
510
+ // Old and new Gmail client id/secret values are different - we need to invalidate tokens and scroll to Auth button.
511
+ if (
512
+ $options->get( 'mail', 'mailer' ) === 'gmail' &&
513
+ ! empty( $data['gmail']['client_id'] ) &&
514
+ ! empty( $data['gmail']['client_secret'] ) &&
515
+ (
516
+ $options->get( 'gmail', 'client_id' ) !== $data['gmail']['client_id'] ||
517
+ $options->get( 'gmail', 'client_secret' ) !== $data['gmail']['client_secret']
518
+ )
519
+ ) {
520
+ unset( $old_opt['gmail'] );
521
+
522
+ if (
523
+ ! empty( $data['gmail']['client_id'] ) &&
524
+ ! empty( $data['gmail']['client_secret'] )
525
+ ) {
526
+ $to_redirect = true;
527
+ }
528
+ }
529
+
530
+ // New gmail clients data will be added from new $data.
531
+ $to_save = Options::array_merge_recursive( $old_opt, $data );
532
+
533
+ // All the sanitization is done in Options class.
534
+ $options->set( $to_save );
535
+
536
+ if ( $to_redirect ) {
537
+ wp_redirect( $_POST['_wp_http_referer'] . '#wp-mail-smtp-setting-row-gmail-authorize' );
538
+ exit;
539
+ }
540
+
541
+ WP::add_admin_notice(
542
+ esc_html__( 'Settings were successfully saved.', 'wp-mail-smtp' ),
543
+ WP::ADMIN_NOTICE_SUCCESS
544
+ );
545
+ }
546
+ }
src/Admin/PluginsInstallSkin.php CHANGED
@@ -1,55 +1,55 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Admin;
4
-
5
- /**
6
- * WordPress class extended for on-the-fly plugin installations.
7
- *
8
- * @since 1.5.0
9
- * @since 1.7.1 Removed feedback() method override to be compatible with PHP5.3+ and WP5.3.
10
- */
11
- class PluginsInstallSkin extends \WP_Upgrader_Skin {
12
-
13
- /**
14
- * Empty out the header of its HTML content and only check to see if it has
15
- * been performed or not.
16
- *
17
- * @since 1.5.0
18
- */
19
- public function header() {
20
- }
21
-
22
- /**
23
- * Empty out the footer of its HTML contents.
24
- *
25
- * @since 1.5.0
26
- */
27
- public function footer() {
28
- }
29
-
30
- /**
31
- * Instead of outputting HTML for errors, json_encode the errors and send them
32
- * back to the Ajax script for processing.
33
- *
34
- * @since 1.5.0
35
- *
36
- * @param array $errors Array of errors with the install process.
37
- */
38
- public function error( $errors ) {
39
-
40
- if ( ! empty( $errors ) ) {
41
- wp_send_json_error( $errors );
42
- }
43
- }
44
-
45
- /**
46
- * Empty out JavaScript output that calls function to decrement the update counts.
47
- *
48
- * @since 1.5.0
49
- *
50
- * @param string $type Type of update count to decrement.
51
- */
52
- public function decrement_update_count( $type ) {
53
- }
54
- }
55
-
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Admin;
4
+
5
+ /**
6
+ * WordPress class extended for on-the-fly plugin installations.
7
+ *
8
+ * @since 1.5.0
9
+ * @since 1.7.1 Removed feedback() method override to be compatible with PHP5.3+ and WP5.3.
10
+ */
11
+ class PluginsInstallSkin extends \WP_Upgrader_Skin {
12
+
13
+ /**
14
+ * Empty out the header of its HTML content and only check to see if it has
15
+ * been performed or not.
16
+ *
17
+ * @since 1.5.0
18
+ */
19
+ public function header() {
20
+ }
21
+
22
+ /**
23
+ * Empty out the footer of its HTML contents.
24
+ *
25
+ * @since 1.5.0
26
+ */
27
+ public function footer() {
28
+ }
29
+
30
+ /**
31
+ * Instead of outputting HTML for errors, json_encode the errors and send them
32
+ * back to the Ajax script for processing.
33
+ *
34
+ * @since 1.5.0
35
+ *
36
+ * @param array $errors Array of errors with the install process.
37
+ */
38
+ public function error( $errors ) {
39
+
40
+ if ( ! empty( $errors ) ) {
41
+ wp_send_json_error( $errors );
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Empty out JavaScript output that calls function to decrement the update counts.
47
+ *
48
+ * @since 1.5.0
49
+ *
50
+ * @param string $type Type of update count to decrement.
51
+ */
52
+ public function decrement_update_count( $type ) {
53
+ }
54
+ }
55
+
src/Options.php CHANGED
@@ -18,13 +18,14 @@ class Options {
18
  * @since 1.3.0
19
  * @since 1.4.0 Added Mailgun:region.
20
  * @since 1.5.0 Added Outlook/AmazonSES.
 
21
  *
22
  * @since
23
  *
24
  * @var array Map of all the default options of the plugin.
25
  */
26
  private static $map = array(
27
- 'mail' => array(
28
  'from_name',
29
  'from_email',
30
  'mailer',
@@ -32,7 +33,7 @@ class Options {
32
  'from_name_force',
33
  'from_email_force',
34
  ),
35
- 'smtp' => array(
36
  'host',
37
  'port',
38
  'encryption',
@@ -41,32 +42,35 @@ class Options {
41
  'user',
42
  'pass',
43
  ),
44
- 'gmail' => array(
45
  'client_id',
46
  'client_secret',
47
  ),
48
- 'outlook' => array(
49
  'client_id',
50
  'client_secret',
51
  ),
52
- 'amazonses' => array(
53
  'client_id',
54
  'client_secret',
55
  'region',
56
  'emails_pending',
57
  ),
58
- 'mailgun' => array(
59
  'api_key',
60
  'domain',
61
  'region',
62
  ),
63
- 'sendgrid' => array(
64
  'api_key',
65
  ),
66
- 'sendinblue' => array(
67
  'api_key',
68
  ),
69
- 'pepipost' => array(
 
 
 
70
  'host',
71
  'port',
72
  'encryption',
@@ -74,7 +78,7 @@ class Options {
74
  'user',
75
  'pass',
76
  ),
77
- 'license' => array(
78
  'key',
79
  ),
80
  );
@@ -340,6 +344,7 @@ class Options {
340
  * @since 1.5.0 Added Outlook/AmazonSES, license key support.
341
  * @since 1.6.0 Added Sendinblue.
342
  * @since 1.7.0 Added Do Not Send.
 
343
  *
344
  * @param string $group
345
  * @param string $key
@@ -504,6 +509,16 @@ class Options {
504
 
505
  break;
506
 
 
 
 
 
 
 
 
 
 
 
507
  case 'license':
508
  switch ( $key ) {
509
  case 'key':
@@ -556,6 +571,7 @@ class Options {
556
  * @since 1.5.0 Added a filter, Outlook/AmazonSES, license key support.
557
  * @since 1.6.0 Added Sendinblue.
558
  * @since 1.7.0 Added Do Not Send.
 
559
  *
560
  * @param string $group
561
  * @param string $key
@@ -697,6 +713,15 @@ class Options {
697
 
698
  break;
699
 
 
 
 
 
 
 
 
 
 
700
  case 'license':
701
  switch ( $key ) {
702
  case 'key':
@@ -776,7 +801,7 @@ class Options {
776
  if (
777
  ! empty( $options['mail']['mailer'] ) &&
778
  isset( $options[ $options['mail']['mailer'] ] ) &&
779
- in_array( $options['mail']['mailer'], array( 'pepipost', 'smtp', 'sendgrid', 'mailgun', 'gmail', 'outlook' ), true )
780
  ) {
781
 
782
  $mailer = $options['mail']['mailer'];
@@ -804,7 +829,7 @@ class Options {
804
  $options[ $mailer ][ $option_name ] = $this->is_const_defined( $mailer, $option_name ) ? '' : trim( (string) $option_value );
805
  break;
806
 
807
- case 'api_key': // mailgun/sendgrid/sendinblue.
808
  case 'domain': // mailgun.
809
  case 'client_id': // gmail/outlook/amazonses.
810
  case 'client_secret': // gmail/outlook/amazonses.
@@ -885,7 +910,7 @@ class Options {
885
  }
886
 
887
  /**
888
- * Check whether the site is using Pepipost or not.
889
  *
890
  * @since 1.0.0
891
  *
18
  * @since 1.3.0
19
  * @since 1.4.0 Added Mailgun:region.
20
  * @since 1.5.0 Added Outlook/AmazonSES.
21
+ * @since 1.8.0 Added Pepipost API.
22
  *
23
  * @since
24
  *
25
  * @var array Map of all the default options of the plugin.
26
  */
27
  private static $map = array(
28
+ 'mail' => array(
29
  'from_name',
30
  'from_email',
31
  'mailer',
33
  'from_name_force',
34
  'from_email_force',
35
  ),
36
+ 'smtp' => array(
37
  'host',
38
  'port',
39
  'encryption',
42
  'user',
43
  'pass',
44
  ),
45
+ 'gmail' => array(
46
  'client_id',
47
  'client_secret',
48
  ),
49
+ 'outlook' => array(
50
  'client_id',
51
  'client_secret',
52
  ),
53
+ 'amazonses' => array(
54
  'client_id',
55
  'client_secret',
56
  'region',
57
  'emails_pending',
58
  ),
59
+ 'mailgun' => array(
60
  'api_key',
61
  'domain',
62
  'region',
63
  ),
64
+ 'sendgrid' => array(
65
  'api_key',
66
  ),
67
+ 'sendinblue' => array(
68
  'api_key',
69
  ),
70
+ 'pepipostapi' => array(
71
+ 'api_key',
72
+ ),
73
+ 'pepipost' => array(
74
  'host',
75
  'port',
76
  'encryption',
78
  'user',
79
  'pass',
80
  ),
81
+ 'license' => array(
82
  'key',
83
  ),
84
  );
344
  * @since 1.5.0 Added Outlook/AmazonSES, license key support.
345
  * @since 1.6.0 Added Sendinblue.
346
  * @since 1.7.0 Added Do Not Send.
347
+ * @since 1.8.0 Added Pepipost API.
348
  *
349
  * @param string $group
350
  * @param string $key
509
 
510
  break;
511
 
512
+ case 'pepipostapi':
513
+ switch ( $key ) {
514
+ case 'api_key':
515
+ /** @noinspection PhpUndefinedConstantInspection */
516
+ $return = $this->is_const_defined( $group, $key ) ? WPMS_PEPIPOST_API_KEY : $value;
517
+ break;
518
+ }
519
+
520
+ break;
521
+
522
  case 'license':
523
  switch ( $key ) {
524
  case 'key':
571
  * @since 1.5.0 Added a filter, Outlook/AmazonSES, license key support.
572
  * @since 1.6.0 Added Sendinblue.
573
  * @since 1.7.0 Added Do Not Send.
574
+ * @since 1.8.0 Added Pepipost API.
575
  *
576
  * @param string $group
577
  * @param string $key
713
 
714
  break;
715
 
716
+ case 'pepipostapi':
717
+ switch ( $key ) {
718
+ case 'api_key':
719
+ $return = defined( 'WPMS_PEPIPOST_API_KEY' ) && WPMS_PEPIPOST_API_KEY;
720
+ break;
721
+ }
722
+
723
+ break;
724
+
725
  case 'license':
726
  switch ( $key ) {
727
  case 'key':
801
  if (
802
  ! empty( $options['mail']['mailer'] ) &&
803
  isset( $options[ $options['mail']['mailer'] ] ) &&
804
+ in_array( $options['mail']['mailer'], array( 'pepipost', 'pepipostapi', 'smtp', 'sendgrid', 'sendinblue', 'mailgun', 'gmail', 'outlook' ), true )
805
  ) {
806
 
807
  $mailer = $options['mail']['mailer'];
829
  $options[ $mailer ][ $option_name ] = $this->is_const_defined( $mailer, $option_name ) ? '' : trim( (string) $option_value );
830
  break;
831
 
832
+ case 'api_key': // mailgun/sendgrid/sendinblue/pepipostapi.
833
  case 'domain': // mailgun.
834
  case 'client_id': // gmail/outlook/amazonses.
835
  case 'client_secret': // gmail/outlook/amazonses.
910
  }
911
 
912
  /**
913
+ * Check whether the site is using Pepipost SMTP or not.
914
  *
915
  * @since 1.0.0
916
  *
src/Processor.php CHANGED
@@ -1,232 +1,232 @@
1
- <?php
2
-
3
- namespace WPMailSMTP;
4
-
5
- /**
6
- * Class Processor modifies the behaviour of wp_mail() function.
7
- *
8
- * @since 1.0.0
9
- */
10
- class Processor {
11
-
12
- /**
13
- * Processor constructor.
14
- *
15
- * @since 1.0.0
16
- */
17
- public function __construct() {
18
- $this->hooks();
19
- }
20
-
21
- /**
22
- * Assign all hooks to proper places.
23
- *
24
- * @since 1.0.0
25
- */
26
- public function hooks() {
27
-
28
- add_action( 'phpmailer_init', array( $this, 'phpmailer_init' ) );
29
-
30
- add_filter( 'wp_mail_from', array( $this, 'filter_mail_from_email' ), 1000 );
31
- add_filter( 'wp_mail_from_name', array( $this, 'filter_mail_from_name' ), 1000 );
32
- }
33
-
34
- /**
35
- * Redefine certain PHPMailer options with our custom ones.
36
- *
37
- * @since 1.0.0
38
- *
39
- * @param \PHPMailer $phpmailer It's passed by reference, so no need to return anything.
40
- */
41
- public function phpmailer_init( $phpmailer ) {
42
-
43
- $options = new Options();
44
- $mailer = $options->get( 'mail', 'mailer' );
45
-
46
- // Check that mailer is not blank, and if mailer=smtp, host is not blank.
47
- if (
48
- ! $mailer ||
49
- ( 'smtp' === $mailer && ! $options->get( 'smtp', 'host' ) )
50
- ) {
51
- return;
52
- }
53
-
54
- // If the mailer is pepipost, make sure we have a username and password.
55
- if (
56
- 'pepipost' === $mailer &&
57
- ( ! $options->get( 'pepipost', 'user' ) && ! $options->get( 'pepipost', 'pass' ) )
58
- ) {
59
- return;
60
- }
61
-
62
- // Set the mailer type as per config above, this overrides the already called isMail method.
63
- // It's basically always 'smtp'.
64
- $phpmailer->Mailer = $mailer;
65
-
66
- // Set the Sender (return-path) if required.
67
- if ( $options->get( 'mail', 'return_path' ) ) {
68
- $phpmailer->Sender = $phpmailer->From;
69
- }
70
-
71
- // Set the SMTPSecure value, if set to none, leave this blank. Possible values: 'ssl', 'tls', ''.
72
- if ( 'none' === $options->get( $mailer, 'encryption' ) ) {
73
- $phpmailer->SMTPSecure = '';
74
- } else {
75
- $phpmailer->SMTPSecure = $options->get( $mailer, 'encryption' );
76
- }
77
-
78
- // Check if user has disabled SMTPAutoTLS.
79
- if ( $options->get( $mailer, 'encryption' ) !== 'tls' && ! $options->get( $mailer, 'autotls' ) ) {
80
- $phpmailer->SMTPAutoTLS = false;
81
- }
82
-
83
- // If we're sending via SMTP, set the host.
84
- if ( 'smtp' === $mailer ) {
85
- // Set the other options.
86
- $phpmailer->Host = $options->get( $mailer, 'host' );
87
- $phpmailer->Port = $options->get( $mailer, 'port' );
88
-
89
- // If we're using smtp auth, set the username & password.
90
- if ( $options->get( $mailer, 'auth' ) ) {
91
- $phpmailer->SMTPAuth = true;
92
- $phpmailer->Username = $options->get( $mailer, 'user' );
93
- $phpmailer->Password = $options->get( $mailer, 'pass' );
94
- }
95
- } elseif ( 'pepipost' === $mailer ) {
96
- // Set the Pepipost settings for BC.
97
- $phpmailer->Mailer = 'smtp';
98
- $phpmailer->Host = 'smtp.pepipost.com';
99
- $phpmailer->Port = $options->get( $mailer, 'port' );
100
- $phpmailer->SMTPSecure = $options->get( $mailer, 'encryption' ) === 'none' ? '' : $options->get( $mailer, 'encryption' );
101
- $phpmailer->SMTPAuth = true;
102
- $phpmailer->Username = $options->get( $mailer, 'user' );
103
- $phpmailer->Password = $options->get( $mailer, 'pass' );
104
- }
105
-
106
- // You can add your own options here.
107
- // See the phpmailer documentation for more info: https://github.com/PHPMailer/PHPMailer/tree/5.2-stable.
108
- /** @noinspection PhpUnusedLocalVariableInspection It's passed by reference. */
109
- $phpmailer = apply_filters( 'wp_mail_smtp_custom_options', $phpmailer );
110
- }
111
-
112
- /**
113
- * This method will be called every time 'smtp' and 'mail' mailers will be used to send emails.
114
- *
115
- * @since 1.3.0
116
- * @since 1.5.0 Added a do_action() to be able to hook into.
117
- *
118
- * @param bool $is_sent
119
- * @param array $to
120
- * @param array $cc
121
- * @param array $bcc
122
- * @param string $subject
123
- * @param string $body
124
- * @param string $from
125
- */
126
- public static function send_callback( $is_sent, $to, $cc, $bcc, $subject, $body, $from ) {
127
-
128
- if ( ! $is_sent ) {
129
- // Add mailer to the beginning and save to display later.
130
- Debug::set(
131
- 'Mailer: ' . esc_html( wp_mail_smtp()->get_providers()->get_options( Options::init()->get( 'mail', 'mailer' ) )->get_title() ) . "\r\n" .
132
- 'PHPMailer was able to connect to SMTP server but failed while trying to send an email.'
133
- );
134
- } else {
135
- Debug::clear();
136
- }
137
-
138
- do_action( 'wp_mail_smtp_mailcatcher_smtp_send_after', $is_sent, $to, $cc, $bcc, $subject, $body, $from );
139
- }
140
-
141
- /**
142
- * Modify the email address that is used for sending emails.
143
- *
144
- * @since 1.0.0
145
- * @since 1.3.0 Forcing email rewrite if option is selected.
146
- * @since 1.7.0 Default email may be empty, so pay attention to that as well.
147
- *
148
- * @param string $wp_email
149
- *
150
- * @return string
151
- */
152
- public function filter_mail_from_email( $wp_email ) {
153
-
154
- $options = new Options();
155
- $forced = $options->get( 'mail', 'from_email_force' );
156
- $from_email = $options->get( 'mail', 'from_email' );
157
- $def_email = $this->get_default_email();
158
-
159
- // Return FROM EMAIL if forced in settings.
160
- if ( $forced & ! empty( $from_email ) ) {
161
- return $from_email;
162
- }
163
-
164
- // If the FROM EMAIL is not the default, return it unchanged.
165
- if ( ! empty( $def_email ) && $wp_email !== $def_email ) {
166
- return $wp_email;
167
- }
168
-
169
- return ! empty( $from_email ) ? $from_email : $wp_email;
170
- }
171
-
172
- /**
173
- * Modify the sender name that is used for sending emails.
174
- *
175
- * @since 1.0.0
176
- * @since 1.3.0 Forcing name rewrite if option is selected.
177
- *
178
- * @param string $name
179
- *
180
- * @return string
181
- */
182
- public function filter_mail_from_name( $name ) {
183
-
184
- $options = new Options();
185
- $force = $options->get( 'mail', 'from_name_force' );
186
-
187
- // If the FROM NAME is not the default and not forced, return it unchanged.
188
- if ( ! $force && $name !== $this->get_default_name() ) {
189
- return $name;
190
- }
191
-
192
- $name = $options->get( 'mail', 'from_name' );
193
-
194
- return $name;
195
- }
196
-
197
- /**
198
- * Get the default email address based on domain name.
199
- *
200
- * @since 1.0.0
201
- * @since 1.7.0 May return an empty string.
202
- *
203
- * @return string Empty string when we aren't able to get the site domain (CLI, misconfigured server etc).
204
- */
205
- public function get_default_email() {
206
-
207
- $server_name = Geo::get_site_domain();
208
-
209
- if ( empty( $server_name ) ) {
210
- return '';
211
- }
212
-
213
- // Get rid of www.
214
- $sitename = strtolower( $server_name );
215
- if ( substr( $sitename, 0, 4 ) === 'www.' ) {
216
- $sitename = substr( $sitename, 4 );
217
- }
218
-
219
- return 'wordpress@' . $sitename;
220
- }
221
-
222
- /**
223
- * Get the default email FROM NAME generated by WordPress.
224
- *
225
- * @since 1.3.0
226
- *
227
- * @return string
228
- */
229
- public function get_default_name() {
230
- return 'WordPress';
231
- }
232
- }
1
+ <?php
2
+
3
+ namespace WPMailSMTP;
4
+
5
+ /**
6
+ * Class Processor modifies the behaviour of wp_mail() function.
7
+ *
8
+ * @since 1.0.0
9
+ */
10
+ class Processor {
11
+
12
+ /**
13
+ * Processor constructor.
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ public function __construct() {
18
+ $this->hooks();
19
+ }
20
+
21
+ /**
22
+ * Assign all hooks to proper places.
23
+ *
24
+ * @since 1.0.0
25
+ */
26
+ public function hooks() {
27
+
28
+ add_action( 'phpmailer_init', array( $this, 'phpmailer_init' ) );
29
+
30
+ add_filter( 'wp_mail_from', array( $this, 'filter_mail_from_email' ), 1000 );
31
+ add_filter( 'wp_mail_from_name', array( $this, 'filter_mail_from_name' ), 1000 );
32
+ }
33
+
34
+ /**
35
+ * Redefine certain PHPMailer options with our custom ones.
36
+ *
37
+ * @since 1.0.0
38
+ *
39
+ * @param \PHPMailer $phpmailer It's passed by reference, so no need to return anything.
40
+ */
41
+ public function phpmailer_init( $phpmailer ) {
42
+
43
+ $options = new Options();
44
+ $mailer = $options->get( 'mail', 'mailer' );
45
+
46
+ // Check that mailer is not blank, and if mailer=smtp, host is not blank.
47
+ if (
48
+ ! $mailer ||
49
+ ( 'smtp' === $mailer && ! $options->get( 'smtp', 'host' ) )
50
+ ) {
51
+ return;
52
+ }
53
+
54
+ // If the mailer is pepipost, make sure we have a username and password.
55
+ if (
56
+ 'pepipost' === $mailer &&
57
+ ( ! $options->get( 'pepipost', 'user' ) && ! $options->get( 'pepipost', 'pass' ) )
58
+ ) {
59
+ return;
60
+ }
61
+
62
+ // Set the mailer type as per config above, this overrides the already called isMail method.
63
+ // It's basically always 'smtp'.
64
+ $phpmailer->Mailer = $mailer;
65
+
66
+ // Set the Sender (return-path) if required.
67
+ if ( $options->get( 'mail', 'return_path' ) ) {
68
+ $phpmailer->Sender = $phpmailer->From;
69
+ }
70
+
71
+ // Set the SMTPSecure value, if set to none, leave this blank. Possible values: 'ssl', 'tls', ''.
72
+ if ( 'none' === $options->get( $mailer, 'encryption' ) ) {
73
+ $phpmailer->SMTPSecure = '';
74
+ } else {
75
+ $phpmailer->SMTPSecure = $options->get( $mailer, 'encryption' );
76
+ }
77
+
78
+ // Check if user has disabled SMTPAutoTLS.
79
+ if ( $options->get( $mailer, 'encryption' ) !== 'tls' && ! $options->get( $mailer, 'autotls' ) ) {
80
+ $phpmailer->SMTPAutoTLS = false;
81
+ }
82
+
83
+ // If we're sending via SMTP, set the host.
84
+ if ( 'smtp' === $mailer ) {
85
+ // Set the other options.
86
+ $phpmailer->Host = $options->get( $mailer, 'host' );
87
+ $phpmailer->Port = $options->get( $mailer, 'port' );
88
+
89
+ // If we're using smtp auth, set the username & password.
90
+ if ( $options->get( $mailer, 'auth' ) ) {
91
+ $phpmailer->SMTPAuth = true;
92
+ $phpmailer->Username = $options->get( $mailer, 'user' );
93
+ $phpmailer->Password = $options->get( $mailer, 'pass' );
94
+ }
95
+ } elseif ( 'pepipost' === $mailer ) {
96
+ // Set the Pepipost settings for BC.
97
+ $phpmailer->Mailer = 'smtp';
98
+ $phpmailer->Host = 'smtp.pepipost.com';
99
+ $phpmailer->Port = $options->get( $mailer, 'port' );
100
+ $phpmailer->SMTPSecure = $options->get( $mailer, 'encryption' ) === 'none' ? '' : $options->get( $mailer, 'encryption' );
101
+ $phpmailer->SMTPAuth = true;
102
+ $phpmailer->Username = $options->get( $mailer, 'user' );
103
+ $phpmailer->Password = $options->get( $mailer, 'pass' );
104
+ }
105
+
106
+ // You can add your own options here.
107
+ // See the phpmailer documentation for more info: https://github.com/PHPMailer/PHPMailer/tree/5.2-stable.
108
+ /** @noinspection PhpUnusedLocalVariableInspection It's passed by reference. */
109
+ $phpmailer = apply_filters( 'wp_mail_smtp_custom_options', $phpmailer );
110
+ }
111
+
112
+ /**
113
+ * This method will be called every time 'smtp' and 'mail' mailers will be used to send emails.
114
+ *
115
+ * @since 1.3.0
116
+ * @since 1.5.0 Added a do_action() to be able to hook into.
117
+ *
118
+ * @param bool $is_sent
119
+ * @param array $to
120
+ * @param array $cc
121
+ * @param array $bcc
122
+ * @param string $subject
123
+ * @param string $body
124
+ * @param string $from
125
+ */
126
+ public static function send_callback( $is_sent, $to, $cc, $bcc, $subject, $body, $from ) {
127
+
128
+ if ( ! $is_sent ) {
129
+ // Add mailer to the beginning and save to display later.
130
+ Debug::set(
131
+ 'Mailer: ' . esc_html( wp_mail_smtp()->get_providers()->get_options( Options::init()->get( 'mail', 'mailer' ) )->get_title() ) . "\r\n" .
132
+ 'PHPMailer was able to connect to SMTP server but failed while trying to send an email.'
133
+ );
134
+ } else {
135
+ Debug::clear();
136
+ }
137
+
138
+ do_action( 'wp_mail_smtp_mailcatcher_smtp_send_after', $is_sent, $to, $cc, $bcc, $subject, $body, $from );
139
+ }
140
+
141
+ /**
142
+ * Modify the email address that is used for sending emails.
143
+ *
144
+ * @since 1.0.0
145
+ * @since 1.3.0 Forcing email rewrite if option is selected.
146
+ * @since 1.7.0 Default email may be empty, so pay attention to that as well.
147
+ *
148
+ * @param string $wp_email
149
+ *
150
+ * @return string
151
+ */
152
+ public function filter_mail_from_email( $wp_email ) {
153
+
154
+ $options = new Options();
155
+ $forced = $options->get( 'mail', 'from_email_force' );
156
+ $from_email = $options->get( 'mail', 'from_email' );
157
+ $def_email = $this->get_default_email();
158
+
159
+ // Return FROM EMAIL if forced in settings.
160
+ if ( $forced & ! empty( $from_email ) ) {
161
+ return $from_email;
162
+ }
163
+
164
+ // If the FROM EMAIL is not the default, return it unchanged.
165
+ if ( ! empty( $def_email ) && $wp_email !== $def_email ) {
166
+ return $wp_email;
167
+ }
168
+
169
+ return ! empty( $from_email ) ? $from_email : $wp_email;
170
+ }
171
+
172
+ /**
173
+ * Modify the sender name that is used for sending emails.
174
+ *
175
+ * @since 1.0.0
176
+ * @since 1.3.0 Forcing name rewrite if option is selected.
177
+ *
178
+ * @param string $name
179
+ *
180
+ * @return string
181
+ */
182
+ public function filter_mail_from_name( $name ) {
183
+
184
+ $options = new Options();
185
+ $force = $options->get( 'mail', 'from_name_force' );
186
+
187
+ // If the FROM NAME is not the default and not forced, return it unchanged.
188
+ if ( ! $force && $name !== $this->get_default_name() ) {
189
+ return $name;
190
+ }
191
+
192
+ $name = $options->get( 'mail', 'from_name' );
193
+
194
+ return $name;
195
+ }
196
+
197
+ /**
198
+ * Get the default email address based on domain name.
199
+ *
200
+ * @since 1.0.0
201
+ * @since 1.7.0 May return an empty string.
202
+ *
203
+ * @return string Empty string when we aren't able to get the site domain (CLI, misconfigured server etc).
204
+ */
205
+ public function get_default_email() {
206
+
207
+ $server_name = Geo::get_site_domain();
208
+
209
+ if ( empty( $server_name ) ) {
210
+ return '';
211
+ }
212
+
213
+ // Get rid of www.
214
+ $sitename = strtolower( $server_name );
215
+ if ( substr( $sitename, 0, 4 ) === 'www.' ) {
216
+ $sitename = substr( $sitename, 4 );
217
+ }
218
+
219
+ return 'wordpress@' . $sitename;
220
+ }
221
+
222
+ /**
223
+ * Get the default email FROM NAME generated by WordPress.
224
+ *
225
+ * @since 1.3.0
226
+ *
227
+ * @return string
228
+ */
229
+ public function get_default_name() {
230
+ return 'WordPress';
231
+ }
232
+ }
src/Providers/Loader.php CHANGED
@@ -1,204 +1,205 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Providers;
4
-
5
- use WPMailSMTP\Debug;
6
- use WPMailSMTP\MailCatcher;
7
- use WPMailSMTP\Options;
8
-
9
- /**
10
- * Class Loader.
11
- *
12
- * @since 1.0.0
13
- */
14
- class Loader {
15
-
16
- /**
17
- * Key is the mailer option, value is the path to its classes.
18
- *
19
- * @since 1.0.0
20
- * @since 1.6.0 Added Sendinblue.
21
- * @since 1.7.0 Added AmazonSES/Outlook as indication of the Pro mailers.
22
- *
23
- * @var array
24
- */
25
- protected $providers = array(
26
- 'mail' => 'WPMailSMTP\Providers\Mail\\',
27
- 'sendinblue' => 'WPMailSMTP\Providers\Sendinblue\\',
28
- 'mailgun' => 'WPMailSMTP\Providers\Mailgun\\',
29
- 'sendgrid' => 'WPMailSMTP\Providers\Sendgrid\\',
30
- 'amazonses' => 'WPMailSMTP\Providers\AmazonSES\\',
31
- 'gmail' => 'WPMailSMTP\Providers\Gmail\\',
32
- 'outlook' => 'WPMailSMTP\Providers\Outlook\\',
33
- 'smtp' => 'WPMailSMTP\Providers\SMTP\\',
34
- 'pepipost' => 'WPMailSMTP\Providers\Pepipost\\',
35
- );
36
-
37
- /**
38
- * @since 1.0.0
39
- *
40
- * @var MailCatcher
41
- */
42
- private $phpmailer;
43
-
44
- /**
45
- * Get all the supported providers.
46
- *
47
- * @since 1.0.0
48
- *
49
- * @return array
50
- */
51
- public function get_providers() {
52
-
53
- if ( ! Options::init()->is_pepipost_active() ) {
54
- unset( $this->providers['pepipost'] );
55
- }
56
-
57
- return apply_filters( 'wp_mail_smtp_providers_loader_get_providers', $this->providers );
58
- }
59
-
60
- /**
61
- * Get a single provider FQN-path based on its name.
62
- *
63
- * @since 1.0.0
64
- *
65
- * @param string $provider
66
- *
67
- * @return string|null
68
- */
69
- public function get_provider_path( $provider ) {
70
-
71
- $provider = sanitize_key( $provider );
72
-
73
- $providers = $this->get_providers();
74
-
75
- return apply_filters(
76
- 'wp_mail_smtp_providers_loader_get_provider_path',
77
- isset( $providers[ $provider ] ) ? $providers[ $provider ] : null,
78
- $provider
79
- );
80
- }
81
-
82
- /**
83
- * Get the provider options, if exists.
84
- *
85
- * @since 1.0.0
86
- *
87
- * @param string $provider
88
- *
89
- * @return OptionsAbstract|null
90
- */
91
- public function get_options( $provider ) {
92
-
93
- return $this->get_entity( $provider, 'Options' );
94
- }
95
-
96
- /**
97
- * Get all options of all providers.
98
- *
99
- * @since 1.0.0
100
- *
101
- * @return OptionsAbstract[]
102
- */
103
- public function get_options_all() {
104
-
105
- $options = array();
106
-
107
- foreach ( $this->get_providers() as $provider => $path ) {
108
-
109
- $option = $this->get_options( $provider );
110
-
111
- if ( ! $option instanceof OptionsAbstract ) {
112
- continue;
113
- }
114
-
115
- $slug = $option->get_slug();
116
- $title = $option->get_title();
117
-
118
- if ( empty( $title ) || empty( $slug ) ) {
119
- continue;
120
- }
121
-
122
- $options[] = $option;
123
- }
124
-
125
- return apply_filters( 'wp_mail_smtp_providers_loader_get_providers_all', $options );
126
- }
127
-
128
- /**
129
- * Get the provider mailer, if exists.
130
- *
131
- * @since 1.0.0
132
- *
133
- * @param string $provider
134
- * @param MailCatcher $phpmailer
135
- *
136
- * @return MailerAbstract|null
137
- */
138
- public function get_mailer( $provider, $phpmailer ) {
139
-
140
- if (
141
- $phpmailer instanceof MailCatcher ||
142
- $phpmailer instanceof \PHPMailer
143
- ) {
144
- $this->phpmailer = $phpmailer;
145
- }
146
-
147
- return $this->get_entity( $provider, 'Mailer' );
148
- }
149
-
150
- /**
151
- * Get the provider auth, if exists.
152
- *
153
- * @param string $provider
154
- *
155
- * @return AuthAbstract|null
156
- */
157
- public function get_auth( $provider ) {
158
-
159
- return $this->get_entity( $provider, 'Auth' );
160
- }
161
-
162
- /**
163
- * Get a generic entity based on the request.
164
- *
165
- * @uses \ReflectionClass
166
- *
167
- * @since 1.0.0
168
- *
169
- * @param string $provider
170
- * @param string $request
171
- *
172
- * @return OptionsAbstract|MailerAbstract|AuthAbstract|null
173
- */
174
- protected function get_entity( $provider, $request ) {
175
-
176
- $provider = sanitize_key( $provider );
177
- $request = sanitize_text_field( $request );
178
- $path = $this->get_provider_path( $provider );
179
- $entity = null;
180
-
181
- if ( empty( $path ) ) {
182
- return $entity;
183
- }
184
-
185
- try {
186
- $reflection = new \ReflectionClass( $path . $request );
187
-
188
- if ( file_exists( $reflection->getFileName() ) ) {
189
- $class = $path . $request;
190
- if ( $this->phpmailer ) {
191
- $entity = new $class( $this->phpmailer );
192
- } else {
193
- $entity = new $class();
194
- }
195
- }
196
- }
197
- catch ( \Exception $e ) {
198
- Debug::set( "There was a problem while retrieving {$request} for {$provider}: {$e->getMessage()}" );
199
- $entity = null;
200
- }
201
-
202
- return apply_filters( 'wp_mail_smtp_providers_loader_get_entity', $entity, $provider, $request );
203
- }
204
- }
 
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Providers;
4
+
5
+ use WPMailSMTP\Debug;
6
+ use WPMailSMTP\MailCatcher;
7
+ use WPMailSMTP\Options;
8
+
9
+ /**
10
+ * Class Loader.
11
+ *
12
+ * @since 1.0.0
13
+ */
14
+ class Loader {
15
+
16
+ /**
17
+ * Key is the mailer option, value is the path to its classes.
18
+ *
19
+ * @since 1.0.0
20
+ * @since 1.6.0 Added Sendinblue.
21
+ * @since 1.7.0 Added AmazonSES/Outlook as indication of the Pro mailers.
22
+ *
23
+ * @var array
24
+ */
25
+ protected $providers = array(
26
+ 'mail' => 'WPMailSMTP\Providers\Mail\\',
27
+ 'pepipostapi' => 'WPMailSMTP\Providers\PepipostAPI\\',
28
+ 'sendinblue' => 'WPMailSMTP\Providers\Sendinblue\\',
29
+ 'mailgun' => 'WPMailSMTP\Providers\Mailgun\\',
30
+ 'sendgrid' => 'WPMailSMTP\Providers\Sendgrid\\',
31
+ 'amazonses' => 'WPMailSMTP\Providers\AmazonSES\\',
32
+ 'gmail' => 'WPMailSMTP\Providers\Gmail\\',
33
+ 'outlook' => 'WPMailSMTP\Providers\Outlook\\',
34
+ 'smtp' => 'WPMailSMTP\Providers\SMTP\\',
35
+ 'pepipost' => 'WPMailSMTP\Providers\Pepipost\\',
36
+ );
37
+
38
+ /**
39
+ * @since 1.0.0
40
+ *
41
+ * @var MailCatcher
42
+ */
43
+ private $phpmailer;
44
+
45
+ /**
46
+ * Get all the supported providers.
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @return array
51
+ */
52
+ public function get_providers() {
53
+
54
+ if ( ! Options::init()->is_pepipost_active() ) {
55
+ unset( $this->providers['pepipost'] );
56
+ }
57
+
58
+ return apply_filters( 'wp_mail_smtp_providers_loader_get_providers', $this->providers );
59
+ }
60
+
61
+ /**
62
+ * Get a single provider FQN-path based on its name.
63
+ *
64
+ * @since 1.0.0
65
+ *
66
+ * @param string $provider
67
+ *
68
+ * @return string|null
69
+ */
70
+ public function get_provider_path( $provider ) {
71
+
72
+ $provider = sanitize_key( $provider );
73
+
74
+ $providers = $this->get_providers();
75
+
76
+ return apply_filters(
77
+ 'wp_mail_smtp_providers_loader_get_provider_path',
78
+ isset( $providers[ $provider ] ) ? $providers[ $provider ] : null,
79
+ $provider
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Get the provider options, if exists.
85
+ *
86
+ * @since 1.0.0
87
+ *
88
+ * @param string $provider
89
+ *
90
+ * @return OptionsAbstract|null
91
+ */
92
+ public function get_options( $provider ) {
93
+
94
+ return $this->get_entity( $provider, 'Options' );
95
+ }
96
+
97
+ /**
98
+ * Get all options of all providers.
99
+ *
100
+ * @since 1.0.0
101
+ *
102
+ * @return OptionsAbstract[]
103
+ */
104
+ public function get_options_all() {
105
+
106
+ $options = array();
107
+
108
+ foreach ( $this->get_providers() as $provider => $path ) {
109
+
110
+ $option = $this->get_options( $provider );
111
+
112
+ if ( ! $option instanceof OptionsAbstract ) {
113
+ continue;
114
+ }
115
+
116
+ $slug = $option->get_slug();
117
+ $title = $option->get_title();
118
+
119
+ if ( empty( $title ) || empty( $slug ) ) {
120
+ continue;
121
+ }
122
+
123
+ $options[] = $option;
124
+ }
125
+
126
+ return apply_filters( 'wp_mail_smtp_providers_loader_get_providers_all', $options );
127
+ }
128
+
129
+ /**
130
+ * Get the provider mailer, if exists.
131
+ *
132
+ * @since 1.0.0
133
+ *
134
+ * @param string $provider
135
+ * @param MailCatcher $phpmailer
136
+ *
137
+ * @return MailerAbstract|null
138
+ */
139
+ public function get_mailer( $provider, $phpmailer ) {
140
+
141
+ if (
142
+ $phpmailer instanceof MailCatcher ||
143
+ $phpmailer instanceof \PHPMailer
144
+ ) {
145
+ $this->phpmailer = $phpmailer;
146
+ }
147
+
148
+ return $this->get_entity( $provider, 'Mailer' );
149
+ }
150
+
151
+ /**
152
+ * Get the provider auth, if exists.
153
+ *
154
+ * @param string $provider
155
+ *
156
+ * @return AuthAbstract|null
157
+ */
158
+ public function get_auth( $provider ) {
159
+
160
+ return $this->get_entity( $provider, 'Auth' );
161
+ }
162
+
163
+ /**
164
+ * Get a generic entity based on the request.
165
+ *
166
+ * @uses \ReflectionClass
167
+ *
168
+ * @since 1.0.0
169
+ *
170
+ * @param string $provider
171
+ * @param string $request
172
+ *
173
+ * @return OptionsAbstract|MailerAbstract|AuthAbstract|null
174
+ */
175
+ protected function get_entity( $provider, $request ) {
176
+
177
+ $provider = sanitize_key( $provider );
178
+ $request = sanitize_text_field( $request );
179
+ $path = $this->get_provider_path( $provider );
180
+ $entity = null;
181
+
182
+ if ( empty( $path ) ) {
183
+ return $entity;
184
+ }
185
+
186
+ try {
187
+ $reflection = new \ReflectionClass( $path . $request );
188
+
189
+ if ( file_exists( $reflection->getFileName() ) ) {
190
+ $class = $path . $request;
191
+ if ( $this->phpmailer ) {
192
+ $entity = new $class( $this->phpmailer );
193
+ } else {
194
+ $entity = new $class();
195
+ }
196
+ }
197
+ }
198
+ catch ( \Exception $e ) {
199
+ Debug::set( "There was a problem while retrieving {$request} for {$provider}: {$e->getMessage()}" );
200
+ $entity = null;
201
+ }
202
+
203
+ return apply_filters( 'wp_mail_smtp_providers_loader_get_entity', $entity, $provider, $request );
204
+ }
205
+ }
src/Providers/MailerAbstract.php CHANGED
@@ -223,7 +223,11 @@ abstract class MailerAbstract implements MailerInterface {
223
  }
224
 
225
  /**
226
- * @inheritdoc
 
 
 
 
227
  */
228
  public function get_headers() {
229
 
@@ -231,15 +235,21 @@ abstract class MailerAbstract implements MailerInterface {
231
  }
232
 
233
  /**
234
- * @inheritdoc
 
 
 
235
  */
236
  public function send() {
237
 
 
 
238
  $params = Options::array_merge_recursive(
239
  $this->get_default_params(),
240
  array(
241
  'headers' => $this->get_headers(),
242
  'body' => $this->get_body(),
 
243
  )
244
  );
245
 
@@ -296,7 +306,13 @@ abstract class MailerAbstract implements MailerInterface {
296
  }
297
 
298
  /**
299
- * @inheritdoc
 
 
 
 
 
 
300
  */
301
  public function is_email_sent() {
302
 
@@ -341,7 +357,11 @@ abstract class MailerAbstract implements MailerInterface {
341
  }
342
 
343
  /**
344
- * @inheritdoc
 
 
 
 
345
  */
346
  public function is_php_compatible() {
347
 
223
  }
224
 
225
  /**
226
+ * Get the email headers.
227
+ *
228
+ * @since 1.0.0
229
+ *
230
+ * @return array
231
  */
232
  public function get_headers() {
233
 
235
  }
236
 
237
  /**
238
+ * Send the email.
239
+ *
240
+ * @since 1.0.0
241
+ * @since 1.8.0 Added timeout for requests, same as max_execution_time.
242
  */
243
  public function send() {
244
 
245
+ $timeout = (int) ini_get( 'max_execution_time' );
246
+
247
  $params = Options::array_merge_recursive(
248
  $this->get_default_params(),
249
  array(
250
  'headers' => $this->get_headers(),
251
  'body' => $this->get_body(),
252
+ 'timeout' => $timeout ? $timeout : 30,
253
  )
254
  );
255
 
306
  }
307
 
308
  /**
309
+ * Whether the email is sent or not.
310
+ * We basically check the response code from a request to provider.
311
+ * Might not be 100% correct, not guarantees that email is delivered.
312
+ *
313
+ * @since 1.0.0
314
+ *
315
+ * @return bool
316
  */
317
  public function is_email_sent() {
318
 
357
  }
358
 
359
  /**
360
+ * Whether the mailer supports the current PHP version or not.
361
+ *
362
+ * @since 1.0.0
363
+ *
364
+ * @return bool
365
  */
366
  public function is_php_compatible() {
367
 
src/Providers/Pepipost/Options.php CHANGED
@@ -20,9 +20,9 @@ class Options extends OptionsAbstract {
20
 
21
  parent::__construct(
22
  array(
23
- 'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/pepipost.png',
24
  'slug' => 'pepipost',
25
- 'title' => esc_html__( 'Pepipost', 'wp-mail-smtp' ),
26
  )
27
  );
28
  }
20
 
21
  parent::__construct(
22
  array(
23
+ 'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/pepipost-smtp.png',
24
  'slug' => 'pepipost',
25
+ 'title' => esc_html__( 'Pepipost SMTP', 'wp-mail-smtp' ),
26
  )
27
  );
28
  }
src/Providers/PepipostAPI/Mailer.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Providers\PepipostAPI;
4
+
5
+ use WPMailSMTP\Providers\MailerAbstract;
6
+ use WPMailSMTP\WP;
7
+
8
+ /**
9
+ * Class Mailer is basically a Sendgrid copy-paste, as Pepipost support SG migration.
10
+ * In the future we may rewrite the class to use the native Pepipost API.
11
+ *
12
+ * @since 1.8.0
13
+ */
14
+ class Mailer extends MailerAbstract {
15
+
16
+ /**
17
+ * Which response code from HTTP provider is considered to be successful?
18
+ *
19
+ * @since 1.8.0
20
+ *
21
+ * @var int
22
+ */
23
+ protected $email_sent_code = 202;
24
+
25
+ /**
26
+ * URL to make an API request to.
27
+ *
28
+ * @since 1.8.0
29
+ *
30
+ * @var string
31
+ */
32
+ protected $url = 'https://sgapi.pepipost.com/v3/mail/send';
33
+
34
+ /**
35
+ * Mailer constructor.
36
+ *
37
+ * @since 1.8.0
38
+ *
39
+ * @param \WPMailSMTP\MailCatcher $phpmailer
40
+ */
41
+ public function __construct( $phpmailer ) {
42
+
43
+ // We want to prefill everything from \WPMailSMTP\MailCatcher class, which extends \PHPMailer.
44
+ parent::__construct( $phpmailer );
45
+
46
+ $this->set_header( 'Authorization', 'Bearer ' . $this->options->get( $this->mailer, 'api_key' ) );
47
+ $this->set_header( 'content-type', 'application/json' );
48
+ }
49
+
50
+ /**
51
+ * Redefine the way email body is returned.
52
+ * By default we are sending an array of data.
53
+ * Pepipost requires a JSON, so we encode the body.
54
+ *
55
+ * @since 1.8.0
56
+ *
57
+ * @return string
58
+ */
59
+ public function get_body() {
60
+
61
+ $body = parent::get_body();
62
+
63
+ return wp_json_encode( $body );
64
+ }
65
+
66
+ /**
67
+ * Set the FROM header of the email.
68
+ *
69
+ * @since 1.8.0
70
+ *
71
+ * @param string $email From mail.
72
+ * @param string $name From name.
73
+ */
74
+ public function set_from( $email, $name = '' ) {
75
+
76
+ if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
77
+ return;
78
+ }
79
+
80
+ $from['email'] = $email;
81
+
82
+ if ( ! empty( $name ) ) {
83
+ $from['name'] = $name;
84
+ }
85
+
86
+ $this->set_body_param(
87
+ array(
88
+ 'from' => $from,
89
+ )
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Set the names/emails of people who will receive the email.
95
+ *
96
+ * @since 1.8.0
97
+ *
98
+ * @param array $recipients List of recipients: cc/bcc/to.
99
+ */
100
+ public function set_recipients( $recipients ) {
101
+
102
+ if ( empty( $recipients ) ) {
103
+ return;
104
+ }
105
+
106
+ // Allow for now only these recipient types.
107
+ $default = array( 'to', 'cc', 'bcc' );
108
+ $data = array();
109
+
110
+ foreach ( $recipients as $type => $emails ) {
111
+ if (
112
+ ! in_array( $type, $default, true ) ||
113
+ empty( $emails ) ||
114
+ ! is_array( $emails )
115
+ ) {
116
+ continue;
117
+ }
118
+
119
+ $data[ $type ] = array();
120
+
121
+ // Iterate over all emails for each type.
122
+ // There might be multiple cc/to/bcc emails.
123
+ foreach ( $emails as $email ) {
124
+ $holder = array();
125
+ $addr = isset( $email[0] ) ? $email[0] : false;
126
+ $name = isset( $email[1] ) ? $email[1] : false;
127
+
128
+ if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
129
+ continue;
130
+ }
131
+
132
+ $holder['email'] = $addr;
133
+ if ( ! empty( $name ) ) {
134
+ $holder['name'] = $name;
135
+ }
136
+
137
+ array_push( $data[ $type ], $holder );
138
+ }
139
+ }
140
+
141
+ if ( ! empty( $data ) ) {
142
+ $this->set_body_param(
143
+ array(
144
+ 'personalizations' => array( $data ),
145
+ )
146
+ );
147
+
148
+ if ( ! empty( $data['bcc'] ) ) {
149
+ // Only the 1st BCC email address, ignore the rest - is not supported by Pepipost.
150
+ $bcc['mail_settings']['bcc']['email'] = $data['bcc'][0]['email'];
151
+ $this->set_body_param(
152
+ $bcc
153
+ );
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Set the email content.
160
+ *
161
+ * @since 1.8.0
162
+ *
163
+ * @param array|string $content Email content.
164
+ */
165
+ public function set_content( $content ) {
166
+
167
+ if ( empty( $content ) ) {
168
+ return;
169
+ }
170
+
171
+ if ( is_array( $content ) ) {
172
+
173
+ $default = array( 'text', 'html' );
174
+ $data = array();
175
+
176
+ foreach ( $content as $type => $body ) {
177
+ if (
178
+ ! in_array( $type, $default, true ) ||
179
+ empty( $body )
180
+ ) {
181
+ continue;
182
+ }
183
+
184
+ $content_type = 'text/plain';
185
+ $content_value = $body;
186
+
187
+ if ( $type === 'html' ) {
188
+ $content_type = 'text/html';
189
+ }
190
+
191
+ $data[] = array(
192
+ 'type' => $content_type,
193
+ 'value' => $content_value,
194
+ );
195
+ }
196
+
197
+ $this->set_body_param(
198
+ array(
199
+ 'content' => $data,
200
+ )
201
+ );
202
+ } else {
203
+ $data['type'] = 'text/html';
204
+ $data['value'] = $content;
205
+
206
+ if ( $this->phpmailer->ContentType === 'text/plain' ) {
207
+ $data['type'] = 'text/plain';
208
+ }
209
+
210
+ $this->set_body_param(
211
+ array(
212
+ 'content' => array( $data ),
213
+ )
214
+ );
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Redefine the way custom headers are processed for this mailer - they should be in body.
220
+ *
221
+ * @since 1.8.0
222
+ *
223
+ * @param array $headers
224
+ */
225
+ public function set_headers( $headers ) {
226
+
227
+ foreach ( $headers as $header ) {
228
+ $name = isset( $header[0] ) ? $header[0] : false;
229
+ $value = isset( $header[1] ) ? $header[1] : false;
230
+
231
+ $this->set_body_header( $name, $value );
232
+ }
233
+
234
+ // Add custom PHPMailer-specific header.
235
+ $this->set_body_header( 'X-Mailer', 'WPMailSMTP/Mailer/' . $this->mailer . ' ' . WPMS_PLUGIN_VER );
236
+ }
237
+
238
+ /**
239
+ * This mailer supports email-related custom headers inside a body of the message.
240
+ *
241
+ * @since 1.8.0
242
+ *
243
+ * @param string $name
244
+ * @param string $value
245
+ */
246
+ public function set_body_header( $name, $value ) {
247
+
248
+ $name = sanitize_text_field( $name );
249
+ if ( empty( $name ) ) {
250
+ return;
251
+ }
252
+
253
+ $headers = isset( $this->body['headers'] ) ? (array) $this->body['headers'] : array();
254
+
255
+ $headers[ $name ] = WP::sanitize_value( $value );
256
+
257
+ $this->set_body_param(
258
+ array(
259
+ 'headers' => $headers,
260
+ )
261
+ );
262
+ }
263
+
264
+ /**
265
+ * Pepipost accepts an array of files content in body, so we will include all files and send.
266
+ * Doesn't handle exceeding the limits etc, as this is done and reported by SendGrid API.
267
+ *
268
+ * @since 1.8.0
269
+ *
270
+ * @param array $attachments
271
+ */
272
+ public function set_attachments( $attachments ) {
273
+
274
+ if ( empty( $attachments ) ) {
275
+ return;
276
+ }
277
+
278
+ $data = array();
279
+
280
+ foreach ( $attachments as $attachment ) {
281
+ $file = false;
282
+
283
+ /*
284
+ * We are not using WP_Filesystem API as we can't reliably work with it.
285
+ * It is not always available, same as credentials for FTP.
286
+ */
287
+ try {
288
+ if ( is_file( $attachment[0] ) && is_readable( $attachment[0] ) ) {
289
+ $file = file_get_contents( $attachment[0] ); // phpcs:ignore
290
+ }
291
+ }
292
+ catch ( \Exception $e ) {
293
+ $file = false;
294
+ }
295
+
296
+ if ( $file === false ) {
297
+ continue;
298
+ }
299
+
300
+ $data[] = array(
301
+ 'content' => base64_encode( $file ),
302
+ 'type' => $attachment[4],
303
+ 'filename' => $attachment[2],
304
+ 'disposition' => $attachment[6],
305
+ );
306
+ }
307
+
308
+ if ( ! empty( $data ) ) {
309
+ $this->set_body_param(
310
+ array(
311
+ 'attachments' => $data,
312
+ )
313
+ );
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Set the reply-to property of the email.
319
+ *
320
+ * @since 1.8.0
321
+ *
322
+ * @param array $reply_to Name/email for reply-to feature.
323
+ */
324
+ public function set_reply_to( $reply_to ) {
325
+
326
+ if ( empty( $reply_to ) ) {
327
+ return;
328
+ }
329
+
330
+ $data = array();
331
+
332
+ foreach ( $reply_to as $key => $emails ) {
333
+ if (
334
+ empty( $emails ) ||
335
+ ! is_array( $emails )
336
+ ) {
337
+ continue;
338
+ }
339
+
340
+ $addr = isset( $emails[0] ) ? $emails[0] : false;
341
+ $name = isset( $emails[1] ) ? $emails[1] : false;
342
+
343
+ if ( ! filter_var( $addr, FILTER_VALIDATE_EMAIL ) ) {
344
+ continue;
345
+ }
346
+
347
+ $data['email'] = $addr;
348
+ if ( ! empty( $name ) ) {
349
+ $data['name'] = $name;
350
+ }
351
+ }
352
+
353
+ if ( ! empty( $data ) ) {
354
+ $this->set_body_param(
355
+ array(
356
+ 'reply_to' => $data,
357
+ )
358
+ );
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Pepipost doesn't support sender or return_path params.
364
+ * So we do nothing.
365
+ *
366
+ * @since 1.8.0
367
+ *
368
+ * @param string $from_email
369
+ */
370
+ public function set_return_path( $from_email ) {}
371
+
372
+ /**
373
+ * Get a Pepipost-specific response with a helpful error.
374
+ *
375
+ * @see https://developers.pepipost.com/migration-api/new-subpage/errorcodes
376
+ *
377
+ * @since 1.8.0
378
+ *
379
+ * @return string
380
+ */
381
+ protected function get_response_error() {
382
+
383
+ $body = (array) wp_remote_retrieve_body( $this->response );
384
+
385
+ $error_text = array();
386
+
387
+ if ( ! empty( $body['errors'] ) ) {
388
+ foreach ( $body['errors'] as $error ) {
389
+ if ( property_exists( $error, 'message' ) ) {
390
+ // Prepare additional information from SendGrid API.
391
+ $extra = '';
392
+ if ( property_exists( $error, 'field' ) && ! empty( $error->field ) ) {
393
+ $extra .= $error->field . '; ';
394
+ }
395
+ if ( property_exists( $error, 'help' ) && ! empty( $error->help ) ) {
396
+ $extra .= $error->help;
397
+ }
398
+
399
+ // Assign both the main message and perhaps extra information, if exists.
400
+ $error_text[] = $error->message . ( ! empty( $extra ) ? ' - ' . $extra : '' );
401
+ }
402
+ }
403
+ }
404
+
405
+ return implode( '<br>', array_map( 'esc_textarea', $error_text ) );
406
+ }
407
+
408
+ /**
409
+ * Get mailer debug information, that is helpful during support.
410
+ *
411
+ * @since 1.8.0
412
+ *
413
+ * @return string
414
+ */
415
+ public function get_debug_info() {
416
+
417
+ $sendgrid_text[] = '<strong>Api Key:</strong> ' . ( $this->is_mailer_complete() ? 'Yes' : 'No' );
418
+
419
+ return implode( '<br>', $sendgrid_text );
420
+ }
421
+
422
+ /**
423
+ * Whether the mailer has all its settings correctly set up and saved.
424
+ *
425
+ * @since 1.8.0
426
+ *
427
+ * @return bool
428
+ */
429
+ public function is_mailer_complete() {
430
+
431
+ $options = $this->options->get_group( $this->mailer );
432
+
433
+ // API key is the only required option.
434
+ if ( ! empty( $options['api_key'] ) ) {
435
+ return true;
436
+ }
437
+
438
+ return false;
439
+ }
440
+ }
src/Providers/PepipostAPI/Options.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Providers\PepipostAPI;
4
+
5
+ use WPMailSMTP\Providers\OptionsAbstract;
6
+ use WPMailSMTP\Options as PluginOptions;
7
+
8
+ /**
9
+ * Class Options.
10
+ *
11
+ * @since 1.8.0
12
+ */
13
+ class Options extends OptionsAbstract {
14
+
15
+ /**
16
+ * Mailer slug.
17
+ *
18
+ * @since 1.8.0
19
+ */
20
+ const SLUG = 'pepipostapi';
21
+
22
+ /**
23
+ * Options constructor.
24
+ *
25
+ * @since 1.8.0
26
+ */
27
+ public function __construct() {
28
+
29
+ $description = sprintf(
30
+ wp_kses( /* translators: %1$s - URL to pepipost.com site. */
31
+ __( '<strong><a href="%1$s" target="_blank" rel="noopener noreferrer">Pepipost</a> is a recommended transactional email service.</strong> Every month Pepipost delivers over 8 billion emails from 20,000+ customers. Their mission is to reliably send emails in the most efficient way and at the most disruptive pricing ever. Pepipost provides users 30,000 free emails the first 30 days, then 100 emails per day.', 'wp-mail-smtp' ) .
32
+ '<br><br>' .
33
+ /* translators: %1$s - URL to wpmailsmtp.com doc. */
34
+ __( 'Read our <a href="%2$s" target="_blank" rel="noopener noreferrer">Pepipost documentation</a> to learn how to configure Pepipost and improve your email deliverability.', 'wp-mail-smtp' ),
35
+ array(
36
+ 'br' => true,
37
+ 'strong' => true,
38
+ 'a' => array(
39
+ 'href' => true,
40
+ 'rel' => true,
41
+ 'target' => true,
42
+ ),
43
+ )
44
+ ),
45
+ 'https://wpmailsmtp.com/go/pepipost/',
46
+ 'https://wpmailsmtp.com/docs/how-to-set-up-the-pepipost-mailer-in-wp-mail-smtp'
47
+ );
48
+
49
+ $api_key = PluginOptions::init()->get( self::SLUG, 'api_key' );
50
+
51
+ if ( empty( $api_key ) ) {
52
+ $description .= '</p><p class="buttonned"><a href="https://wpmailsmtp.com/go/pepipost/" target="_blank" rel="noopener noreferrer" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-blueish">' .
53
+ esc_html__( 'Get Pepipost Now (Free)', 'wp-mail-smtp' ) .
54
+ '</a></p>';
55
+ }
56
+
57
+ parent::__construct(
58
+ array(
59
+ 'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/pepipost.png',
60
+ 'slug' => self::SLUG,
61
+ 'title' => esc_html__( 'Pepipost', 'wp-mail-smtp' ),
62
+ 'description' => $description,
63
+ 'recommended' => true,
64
+ 'php' => '5.3',
65
+ )
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Output the mailer provider options.
71
+ *
72
+ * @since 1.8.0
73
+ */
74
+ public function display_options() {
75
+
76
+ // Do not display options if PHP version is not correct.
77
+ if ( ! $this->is_php_correct() ) {
78
+ $this->display_php_warning();
79
+
80
+ return;
81
+ }
82
+ ?>
83
+
84
+ <!-- API Key -->
85
+ <div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"
86
+ class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
87
+ <div class="wp-mail-smtp-setting-label">
88
+ <label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"><?php esc_html_e( 'API Key', 'wp-mail-smtp' ); ?></label>
89
+ </div>
90
+ <div class="wp-mail-smtp-setting-field">
91
+ <?php if ( $this->options->is_const_defined( $this->get_slug(), 'api_key' ) ) : ?>
92
+ <input type="text" disabled value="****************************************"
93
+ id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
94
+ />
95
+ <?php $this->display_const_set_message( 'WPMS_PEPIPOST_API_KEY' ); ?>
96
+ <?php else : ?>
97
+ <input type="password" spellcheck="false"
98
+ name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][api_key]"
99
+ value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'api_key' ) ); ?>"
100
+ id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
101
+ />
102
+ <?php endif; ?>
103
+
104
+ <p class="desc">
105
+ <?php
106
+ printf( /* translators: %s - pepipost.com link to get an API Key. */
107
+ esc_html__( 'Follow this link to get an API Key: %s.', 'wp-mail-smtp' ),
108
+ '<a href="https://app.pepipost.com/app/settings/integration" target="_blank" rel="noopener noreferrer">' .
109
+ esc_html__( 'Get the API Key', 'wp-mail-smtp' ) .
110
+ '</a>'
111
+ );
112
+ ?>
113
+ </p>
114
+ </div>
115
+ </div>
116
+
117
+ <?php
118
+ }
119
+ }
src/Providers/Sendgrid/Mailer.php CHANGED
@@ -278,6 +278,7 @@ class Mailer extends MailerAbstract {
278
  'type' => $attachment[4],
279
  'filename' => $attachment[2],
280
  'disposition' => $attachment[6],
 
281
  );
282
  }
283
 
278
  'type' => $attachment[4],
279
  'filename' => $attachment[2],
280
  'disposition' => $attachment[6],
281
+ 'content_id' => $attachment[7],
282
  );
283
  }
284
 
src/Providers/Sendinblue/Options.php CHANGED
@@ -1,119 +1,119 @@
1
- <?php
2
-
3
- namespace WPMailSMTP\Providers\Sendinblue;
4
-
5
- use WPMailSMTP\Providers\OptionsAbstract;
6
- use WPMailSMTP\Options as PluginOptions;
7
-
8
- /**
9
- * Class Option.
10
- *
11
- * @since 1.6.0
12
- */
13
- class Options extends OptionsAbstract {
14
-
15
- /**
16
- * Mailer slug.
17
- *
18
- * @since 1.6.0
19
- */
20
- const SLUG = 'sendinblue';
21
-
22
- /**
23
- * Options constructor.
24
- *
25
- * @since 1.6.0
26
- */
27
- public function __construct() {
28
-
29
- $description = sprintf(
30
- wp_kses( /* translators: %1$s - URL to sendinblue.com site. */
31
- __( '<strong><a href="%1$s" target="_blank" rel="noopener noreferrer">Sendinblue</a> is our recommended transactional email service.</strong> Founded in 2012, they serve 80,000+ growing companies around the world and send over 30 million emails each day. They understand that transactional emails are the heart of your customer relationships. Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day.', 'wp-mail-smtp' ) .
32
- '<br><br>' .
33
- /* translators: %2$s - URL to wpmailsmtp.com doc. */
34
- __( 'Read our <a href="%2$s" target="_blank" rel="noopener noreferrer">Sendinblue documentation</a> to learn how to configure Sendinblue and improve your email deliverability.', 'wp-mail-smtp' ),
35
- array(
36
- 'br' => true,
37
- 'strong' => true,
38
- 'a' => array(
39
- 'href' => true,
40
- 'rel' => true,
41
- 'target' => true,
42
- ),
43
- )
44
- ),
45
- 'https://wpmailsmtp.com/go/sendinblue/',
46
- 'https://wpmailsmtp.com/docs/how-to-set-up-the-sendinblue-mailer-in-wp-mail-smtp'
47
- );
48
-
49
- $api_key = PluginOptions::init()->get( self::SLUG, 'api_key' );
50
-
51
- if ( empty( $api_key ) ) {
52
- $description .= '</p><p class="buttonned"><a href="https://wpmailsmtp.com/go/sendinblue/" target="_blank" rel="noopener noreferrer" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-blueish">' .
53
- esc_html__( 'Get Sendinblue Now (Free)', 'wp-mail-smtp' ) .
54
- '</a></p>';
55
- }
56
-
57
- parent::__construct(
58
- array(
59
- 'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/sendinblue.svg',
60
- 'slug' => self::SLUG,
61
- 'title' => esc_html__( 'Sendinblue', 'wp-mail-smtp' ),
62
- 'description' => $description,
63
- 'recommended' => true,
64
- 'php' => '5.6',
65
- )
66
- );
67
- }
68
-
69
- /**
70
- * Output the mailer provider options.
71
- *
72
- * @since 1.6.0
73
- */
74
- public function display_options() {
75
-
76
- // Do not display options if PHP version is not correct.
77
- if ( ! $this->is_php_correct() ) {
78
- $this->display_php_warning();
79
-
80
- return;
81
- }
82
- ?>
83
-
84
- <!-- API Key -->
85
- <div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"
86
- class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
87
- <div class="wp-mail-smtp-setting-label">
88
- <label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"><?php esc_html_e( 'API Key', 'wp-mail-smtp' ); ?></label>
89
- </div>
90
- <div class="wp-mail-smtp-setting-field">
91
- <?php if ( $this->options->is_const_defined( $this->get_slug(), 'api_key' ) ) : ?>
92
- <input type="text" disabled value="****************************************"
93
- id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
94
- />
95
- <?php $this->display_const_set_message( 'WPMS_SENDINBLUE_API_KEY' ); ?>
96
- <?php else : ?>
97
- <input type="password" spellcheck="false"
98
- name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][api_key]"
99
- value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'api_key' ) ); ?>"
100
- id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
101
- />
102
- <?php endif; ?>
103
-
104
- <p class="desc">
105
- <?php
106
- printf( /* translators: %s - sendinblue.com link to get an API Key. */
107
- esc_html__( 'Follow this link to get an API Key: %s.', 'wp-mail-smtp' ),
108
- '<a href="https://account.sendinblue.com/advanced/api" target="_blank" rel="noopener noreferrer">' .
109
- esc_html__( 'Get v3 API Key', 'wp-mail-smtp' ) .
110
- '</a>'
111
- );
112
- ?>
113
- </p>
114
- </div>
115
- </div>
116
-
117
- <?php
118
- }
119
- }
1
+ <?php
2
+
3
+ namespace WPMailSMTP\Providers\Sendinblue;
4
+
5
+ use WPMailSMTP\Providers\OptionsAbstract;
6
+ use WPMailSMTP\Options as PluginOptions;
7
+
8
+ /**
9
+ * Class Options.
10
+ *
11
+ * @since 1.6.0
12
+ */
13
+ class Options extends OptionsAbstract {
14
+
15
+ /**
16
+ * Mailer slug.
17
+ *
18
+ * @since 1.6.0
19
+ */
20
+ const SLUG = 'sendinblue';
21
+
22
+ /**
23
+ * Options constructor.
24
+ *
25
+ * @since 1.6.0
26
+ */
27
+ public function __construct() {
28
+
29
+ $description = sprintf(
30
+ wp_kses( /* translators: %1$s - URL to sendinblue.com site. */
31
+ __( '<strong><a href="%1$s" target="_blank" rel="noopener noreferrer">Sendinblue</a> is a recommended transactional email service.</strong> Founded in 2012, they serve 80,000+ growing companies around the world and send over 30 million emails each day. They understand that transactional emails are the heart of your customer relationships. Their email deliverability experts are constantly at work optimizing the reliability and speed of their SMTP infrastructure. Sendinblue provides users 300 free emails per day.', 'wp-mail-smtp' ) .
32
+ '<br><br>' .
33
+ /* translators: %2$s - URL to wpmailsmtp.com doc. */
34
+ __( 'Read our <a href="%2$s" target="_blank" rel="noopener noreferrer">Sendinblue documentation</a> to learn how to configure Sendinblue and improve your email deliverability.', 'wp-mail-smtp' ),
35
+ array(
36
+ 'br' => true,
37
+ 'strong' => true,
38
+ 'a' => array(
39
+ 'href' => true,
40
+ 'rel' => true,
41
+ 'target' => true,
42
+ ),
43
+ )
44
+ ),
45
+ 'https://wpmailsmtp.com/go/sendinblue/',
46
+ 'https://wpmailsmtp.com/docs/how-to-set-up-the-sendinblue-mailer-in-wp-mail-smtp'
47
+ );
48
+
49
+ $api_key = PluginOptions::init()->get( self::SLUG, 'api_key' );
50
+
51
+ if ( empty( $api_key ) ) {
52
+ $description .= '</p><p class="buttonned"><a href="https://wpmailsmtp.com/go/sendinblue/" target="_blank" rel="noopener noreferrer" class="wp-mail-smtp-btn wp-mail-smtp-btn-md wp-mail-smtp-btn-blueish">' .
53
+ esc_html__( 'Get Sendinblue Now (Free)', 'wp-mail-smtp' ) .
54
+ '</a></p>';
55
+ }
56
+
57
+ parent::__construct(
58
+ array(
59
+ 'logo_url' => wp_mail_smtp()->assets_url . '/images/providers/sendinblue.svg',
60
+ 'slug' => self::SLUG,
61
+ 'title' => esc_html__( 'Sendinblue', 'wp-mail-smtp' ),
62
+ 'description' => $description,
63
+ 'recommended' => true,
64
+ 'php' => '5.6',
65
+ )
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Output the mailer provider options.
71
+ *
72
+ * @since 1.6.0
73
+ */
74
+ public function display_options() {
75
+
76
+ // Do not display options if PHP version is not correct.
77
+ if ( ! $this->is_php_correct() ) {
78
+ $this->display_php_warning();
79
+
80
+ return;
81
+ }
82
+ ?>
83
+
84
+ <!-- API Key -->
85
+ <div id="wp-mail-smtp-setting-row-<?php echo esc_attr( $this->get_slug() ); ?>-client_id"
86
+ class="wp-mail-smtp-setting-row wp-mail-smtp-setting-row-text wp-mail-smtp-clear">
87
+ <div class="wp-mail-smtp-setting-label">
88
+ <label for="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"><?php esc_html_e( 'API Key', 'wp-mail-smtp' ); ?></label>
89
+ </div>
90
+ <div class="wp-mail-smtp-setting-field">
91
+ <?php if ( $this->options->is_const_defined( $this->get_slug(), 'api_key' ) ) : ?>
92
+ <input type="text" disabled value="****************************************"
93
+ id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
94
+ />
95
+ <?php $this->display_const_set_message( 'WPMS_SENDINBLUE_API_KEY' ); ?>
96
+ <?php else : ?>
97
+ <input type="password" spellcheck="false"
98
+ name="wp-mail-smtp[<?php echo esc_attr( $this->get_slug() ); ?>][api_key]"
99
+ value="<?php echo esc_attr( $this->options->get( $this->get_slug(), 'api_key' ) ); ?>"
100
+ id="wp-mail-smtp-setting-<?php echo esc_attr( $this->get_slug() ); ?>-api_key"
101
+ />
102
+ <?php endif; ?>
103
+
104
+ <p class="desc">
105
+ <?php
106
+ printf( /* translators: %s - sendinblue.com link to get an API Key. */
107
+ esc_html__( 'Follow this link to get an API Key: %s.', 'wp-mail-smtp' ),
108
+ '<a href="https://account.sendinblue.com/advanced/api" target="_blank" rel="noopener noreferrer">' .
109
+ esc_html__( 'Get v3 API Key', 'wp-mail-smtp' ) .
110
+ '</a>'
111
+ );
112
+ ?>
113
+ </p>
114
+ </div>
115
+ </div>
116
+
117
+ <?php
118
+ }
119
+ }
vendor/guzzlehttp/guzzle/phpstan-baseline.neon ADDED
@@ -0,0 +1,1412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ parameters:
2
+ ignoreErrors:
3
+ -
4
+ message: "#^Property GuzzleHttp\\\\Client\\:\\:\\$config type has no value type specified in iterable type array\\.$#"
5
+ count: 1
6
+ path: src/Client.php
7
+
8
+ -
9
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
10
+ count: 1
11
+ path: src/Client.php
12
+
13
+ -
14
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:__call\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#"
15
+ count: 1
16
+ path: src/Client.php
17
+
18
+ -
19
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:__call\\(\\) should return GuzzleHttp\\\\Promise\\\\PromiseInterface but returns GuzzleHttp\\\\PromiseInterface\\|Psr\\\\Http\\\\Message\\\\ResponseInterface\\.$#"
20
+ count: 1
21
+ path: src/Client.php
22
+
23
+ -
24
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:sendAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
25
+ count: 1
26
+ path: src/Client.php
27
+
28
+ -
29
+ message: "#^Return typehint of method GuzzleHttp\\\\Client\\:\\:sendAsync\\(\\) has invalid type GuzzleHttp\\\\PromiseInterface\\.$#"
30
+ count: 1
31
+ path: src/Client.php
32
+
33
+ -
34
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:sendAsync\\(\\) should return GuzzleHttp\\\\PromiseInterface but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\.$#"
35
+ count: 1
36
+ path: src/Client.php
37
+
38
+ -
39
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:send\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
40
+ count: 1
41
+ path: src/Client.php
42
+
43
+ -
44
+ message: "#^PHPDoc tag @throws with type GuzzleHttp\\\\GuzzleException is not subtype of Throwable$#"
45
+ count: 2
46
+ path: src/Client.php
47
+
48
+ -
49
+ message: "#^Call to method wait\\(\\) on an unknown class GuzzleHttp\\\\PromiseInterface\\.$#"
50
+ count: 2
51
+ path: src/Client.php
52
+
53
+ -
54
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:requestAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
55
+ count: 1
56
+ path: src/Client.php
57
+
58
+ -
59
+ message: "#^Return typehint of method GuzzleHttp\\\\Client\\:\\:requestAsync\\(\\) has invalid type GuzzleHttp\\\\PromiseInterface\\.$#"
60
+ count: 1
61
+ path: src/Client.php
62
+
63
+ -
64
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:requestAsync\\(\\) should return GuzzleHttp\\\\PromiseInterface but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\.$#"
65
+ count: 1
66
+ path: src/Client.php
67
+
68
+ -
69
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
70
+ count: 1
71
+ path: src/Client.php
72
+
73
+ -
74
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:buildUri\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
75
+ count: 1
76
+ path: src/Client.php
77
+
78
+ -
79
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:configureDefaults\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
80
+ count: 1
81
+ path: src/Client.php
82
+
83
+ -
84
+ message: "#^Parameter \\#1 \\$str of function strtolower expects string, int\\|string given\\.$#"
85
+ count: 1
86
+ path: src/Client.php
87
+
88
+ -
89
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:prepareDefaults\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
90
+ count: 1
91
+ path: src/Client.php
92
+
93
+ -
94
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:prepareDefaults\\(\\) return type has no value type specified in iterable type array\\.$#"
95
+ count: 1
96
+ path: src/Client.php
97
+
98
+ -
99
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:transfer\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
100
+ count: 1
101
+ path: src/Client.php
102
+
103
+ -
104
+ message: "#^Method GuzzleHttp\\\\Client\\:\\:applyOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
105
+ count: 1
106
+ path: src/Client.php
107
+
108
+ -
109
+ message: "#^Parameter \\#2 \\$prefix of function http_build_query expects string, null given\\.$#"
110
+ count: 1
111
+ path: src/Client.php
112
+
113
+ -
114
+ message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:send\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
115
+ count: 1
116
+ path: src/ClientInterface.php
117
+
118
+ -
119
+ message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:sendAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
120
+ count: 1
121
+ path: src/ClientInterface.php
122
+
123
+ -
124
+ message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
125
+ count: 1
126
+ path: src/ClientInterface.php
127
+
128
+ -
129
+ message: "#^Method GuzzleHttp\\\\ClientInterface\\:\\:requestAsync\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
130
+ count: 1
131
+ path: src/ClientInterface.php
132
+
133
+ -
134
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:__construct\\(\\) has parameter \\$cookieArray with no value type specified in iterable type array\\.$#"
135
+ count: 1
136
+ path: src/Cookie/CookieJar.php
137
+
138
+ -
139
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:fromArray\\(\\) has parameter \\$cookies with no value type specified in iterable type array\\.$#"
140
+ count: 1
141
+ path: src/Cookie/CookieJar.php
142
+
143
+ -
144
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:fromArray\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJar\\.$#"
145
+ count: 1
146
+ path: src/Cookie/CookieJar.php
147
+
148
+ -
149
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getCookieValue\\(\\) has no return typehint specified\\.$#"
150
+ count: 1
151
+ path: src/Cookie/CookieJar.php
152
+
153
+ -
154
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getCookieValue\\(\\) has parameter \\$value with no typehint specified\\.$#"
155
+ count: 1
156
+ path: src/Cookie/CookieJar.php
157
+
158
+ -
159
+ message: "#^Result of \\|\\| is always false\\.$#"
160
+ count: 1
161
+ path: src/Cookie/CookieJar.php
162
+
163
+ -
164
+ message: "#^Strict comparison using \\=\\=\\= between string and null will always evaluate to false\\.$#"
165
+ count: 2
166
+ path: src/Cookie/CookieJar.php
167
+
168
+ -
169
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#"
170
+ count: 1
171
+ path: src/Cookie/CookieJar.php
172
+
173
+ -
174
+ message: "#^Call to an undefined method Traversable\\<mixed, mixed\\>\\:\\:getArrayCopy\\(\\)\\.$#"
175
+ count: 1
176
+ path: src/Cookie/CookieJar.php
177
+
178
+ -
179
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\.$#"
180
+ count: 1
181
+ path: src/Cookie/CookieJar.php
182
+
183
+ -
184
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) should return GuzzleHttp\\\\Cookie\\\\CookieJarInterface but return statement is missing\\.$#"
185
+ count: 1
186
+ path: src/Cookie/CookieJar.php
187
+
188
+ -
189
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clear\\(\\) should return GuzzleHttp\\\\Cookie\\\\CookieJarInterface but empty return statement found\\.$#"
190
+ count: 1
191
+ path: src/Cookie/CookieJar.php
192
+
193
+ -
194
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:clearSessionCookies\\(\\) has no return typehint specified\\.$#"
195
+ count: 1
196
+ path: src/Cookie/CookieJar.php
197
+
198
+ -
199
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:count\\(\\) has no return typehint specified\\.$#"
200
+ count: 1
201
+ path: src/Cookie/CookieJar.php
202
+
203
+ -
204
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:getIterator\\(\\) return type has no value type specified in iterable type Traversable\\<mixed, mixed\\>\\.$#"
205
+ count: 1
206
+ path: src/Cookie/CookieJar.php
207
+
208
+ -
209
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:extractCookies\\(\\) has no return typehint specified\\.$#"
210
+ count: 1
211
+ path: src/Cookie/CookieJar.php
212
+
213
+ -
214
+ message: "#^Parameter \\#3 \\$length of function substr expects int, int\\|false given\\.$#"
215
+ count: 1
216
+ path: src/Cookie/CookieJar.php
217
+
218
+ -
219
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJar\\:\\:removeCookieIfEmpty\\(\\) has no return typehint specified\\.$#"
220
+ count: 1
221
+ path: src/Cookie/CookieJar.php
222
+
223
+ -
224
+ message: "#^Interface GuzzleHttp\\\\Cookie\\\\CookieJarInterface extends generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#"
225
+ count: 1
226
+ path: src/Cookie/CookieJarInterface.php
227
+
228
+ -
229
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:extractCookies\\(\\) has no return typehint specified\\.$#"
230
+ count: 1
231
+ path: src/Cookie/CookieJarInterface.php
232
+
233
+ -
234
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:clear\\(\\) return type has no value type specified in iterable type GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\.$#"
235
+ count: 1
236
+ path: src/Cookie/CookieJarInterface.php
237
+
238
+ -
239
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:clearSessionCookies\\(\\) has no return typehint specified\\.$#"
240
+ count: 1
241
+ path: src/Cookie/CookieJarInterface.php
242
+
243
+ -
244
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\CookieJarInterface\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#"
245
+ count: 1
246
+ path: src/Cookie/CookieJarInterface.php
247
+
248
+ -
249
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\FileCookieJar\\:\\:save\\(\\) has no return typehint specified\\.$#"
250
+ count: 1
251
+ path: src/Cookie/FileCookieJar.php
252
+
253
+ -
254
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\FileCookieJar\\:\\:load\\(\\) has no return typehint specified\\.$#"
255
+ count: 1
256
+ path: src/Cookie/FileCookieJar.php
257
+
258
+ -
259
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SessionCookieJar\\:\\:save\\(\\) has no return typehint specified\\.$#"
260
+ count: 1
261
+ path: src/Cookie/SessionCookieJar.php
262
+
263
+ -
264
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SessionCookieJar\\:\\:load\\(\\) has no return typehint specified\\.$#"
265
+ count: 1
266
+ path: src/Cookie/SessionCookieJar.php
267
+
268
+ -
269
+ message: "#^Property GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:\\$defaults type has no value type specified in iterable type array\\.$#"
270
+ count: 1
271
+ path: src/Cookie/SetCookie.php
272
+
273
+ -
274
+ message: "#^Property GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:\\$data type has no value type specified in iterable type array\\.$#"
275
+ count: 1
276
+ path: src/Cookie/SetCookie.php
277
+
278
+ -
279
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#"
280
+ count: 1
281
+ path: src/Cookie/SetCookie.php
282
+
283
+ -
284
+ message: "#^Property GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:\\$data \\(array\\) does not accept array\\|null\\.$#"
285
+ count: 1
286
+ path: src/Cookie/SetCookie.php
287
+
288
+ -
289
+ message: "#^Parameter \\#1 \\$timestamp of method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setExpires\\(\\) expects int, mixed given\\.$#"
290
+ count: 1
291
+ path: src/Cookie/SetCookie.php
292
+
293
+ -
294
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:toArray\\(\\) has no return typehint specified\\.$#"
295
+ count: 1
296
+ path: src/Cookie/SetCookie.php
297
+
298
+ -
299
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setName\\(\\) has no return typehint specified\\.$#"
300
+ count: 1
301
+ path: src/Cookie/SetCookie.php
302
+
303
+ -
304
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setValue\\(\\) has no return typehint specified\\.$#"
305
+ count: 1
306
+ path: src/Cookie/SetCookie.php
307
+
308
+ -
309
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setDomain\\(\\) has no return typehint specified\\.$#"
310
+ count: 1
311
+ path: src/Cookie/SetCookie.php
312
+
313
+ -
314
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setPath\\(\\) has no return typehint specified\\.$#"
315
+ count: 1
316
+ path: src/Cookie/SetCookie.php
317
+
318
+ -
319
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setMaxAge\\(\\) has no return typehint specified\\.$#"
320
+ count: 1
321
+ path: src/Cookie/SetCookie.php
322
+
323
+ -
324
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setExpires\\(\\) has no return typehint specified\\.$#"
325
+ count: 1
326
+ path: src/Cookie/SetCookie.php
327
+
328
+ -
329
+ message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
330
+ count: 1
331
+ path: src/Cookie/SetCookie.php
332
+
333
+ -
334
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setSecure\\(\\) has no return typehint specified\\.$#"
335
+ count: 1
336
+ path: src/Cookie/SetCookie.php
337
+
338
+ -
339
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setDiscard\\(\\) has no return typehint specified\\.$#"
340
+ count: 1
341
+ path: src/Cookie/SetCookie.php
342
+
343
+ -
344
+ message: "#^Method GuzzleHttp\\\\Cookie\\\\SetCookie\\:\\:setHttpOnly\\(\\) has no return typehint specified\\.$#"
345
+ count: 1
346
+ path: src/Cookie/SetCookie.php
347
+
348
+ -
349
+ message: "#^Parameter \\#1 \\$str of function ltrim expects string, string\\|null given\\.$#"
350
+ count: 1
351
+ path: src/Cookie/SetCookie.php
352
+
353
+ -
354
+ message: "#^Method GuzzleHttp\\\\Exception\\\\BadResponseException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#"
355
+ count: 1
356
+ path: src/Exception/BadResponseException.php
357
+
358
+ -
359
+ message: "#^Method GuzzleHttp\\\\Exception\\\\BadResponseException\\:\\:__construct\\(\\) has parameter \\$message with no typehint specified\\.$#"
360
+ count: 1
361
+ path: src/Exception/BadResponseException.php
362
+
363
+ -
364
+ message: "#^Method GuzzleHttp\\\\Exception\\\\ConnectException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#"
365
+ count: 1
366
+ path: src/Exception/ConnectException.php
367
+
368
+ -
369
+ message: "#^Method GuzzleHttp\\\\Exception\\\\ConnectException\\:\\:__construct\\(\\) has parameter \\$message with no typehint specified\\.$#"
370
+ count: 1
371
+ path: src/Exception/ConnectException.php
372
+
373
+ -
374
+ message: "#^Property GuzzleHttp\\\\Exception\\\\RequestException\\:\\:\\$handlerContext type has no value type specified in iterable type array\\.$#"
375
+ count: 1
376
+ path: src/Exception/RequestException.php
377
+
378
+ -
379
+ message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:__construct\\(\\) has parameter \\$handlerContext with no value type specified in iterable type array\\.$#"
380
+ count: 1
381
+ path: src/Exception/RequestException.php
382
+
383
+ -
384
+ message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:__construct\\(\\) has parameter \\$message with no typehint specified\\.$#"
385
+ count: 1
386
+ path: src/Exception/RequestException.php
387
+
388
+ -
389
+ message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:create\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#"
390
+ count: 1
391
+ path: src/Exception/RequestException.php
392
+
393
+ -
394
+ message: "#^Method GuzzleHttp\\\\Exception\\\\RequestException\\:\\:getHandlerContext\\(\\) return type has no value type specified in iterable type array\\.$#"
395
+ count: 1
396
+ path: src/Exception/RequestException.php
397
+
398
+ -
399
+ message: "#^Property GuzzleHttp\\\\Exception\\\\SeekException\\:\\:\\$stream has no typehint specified\\.$#"
400
+ count: 1
401
+ path: src/Exception/SeekException.php
402
+
403
+ -
404
+ message: "#^Method GuzzleHttp\\\\Exception\\\\SeekException\\:\\:__construct\\(\\) has parameter \\$msg with no typehint specified\\.$#"
405
+ count: 1
406
+ path: src/Exception/SeekException.php
407
+
408
+ -
409
+ message: "#^Method GuzzleHttp\\\\Exception\\\\SeekException\\:\\:__construct\\(\\) has parameter \\$pos with no typehint specified\\.$#"
410
+ count: 1
411
+ path: src/Exception/SeekException.php
412
+
413
+ -
414
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:\\$handles type has no value type specified in iterable type array\\.$#"
415
+ count: 1
416
+ path: src/Handler/CurlFactory.php
417
+
418
+ -
419
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:create\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
420
+ count: 1
421
+ path: src/Handler/CurlFactory.php
422
+
423
+ -
424
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:release\\(\\) has no return typehint specified\\.$#"
425
+ count: 1
426
+ path: src/Handler/CurlFactory.php
427
+
428
+ -
429
+ message: "#^Negated boolean expression is always false\\.$#"
430
+ count: 1
431
+ path: src/Handler/CurlFactory.php
432
+
433
+ -
434
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#"
435
+ count: 1
436
+ path: src/Handler/CurlFactory.php
437
+
438
+ -
439
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:finishError\\(\\) has no return typehint specified\\.$#"
440
+ count: 1
441
+ path: src/Handler/CurlFactory.php
442
+
443
+ -
444
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createRejection\\(\\) has no return typehint specified\\.$#"
445
+ count: 1
446
+ path: src/Handler/CurlFactory.php
447
+
448
+ -
449
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createRejection\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#"
450
+ count: 1
451
+ path: src/Handler/CurlFactory.php
452
+
453
+ -
454
+ message: "#^If condition is always true\\.$#"
455
+ count: 1
456
+ path: src/Handler/CurlFactory.php
457
+
458
+ -
459
+ message: "#^Unreachable statement \\- code above always terminates\\.$#"
460
+ count: 1
461
+ path: src/Handler/CurlFactory.php
462
+
463
+ -
464
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:getDefaultConf\\(\\) has no return typehint specified\\.$#"
465
+ count: 1
466
+ path: src/Handler/CurlFactory.php
467
+
468
+ -
469
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyMethod\\(\\) has no return typehint specified\\.$#"
470
+ count: 1
471
+ path: src/Handler/CurlFactory.php
472
+
473
+ -
474
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyMethod\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#"
475
+ count: 1
476
+ path: src/Handler/CurlFactory.php
477
+
478
+ -
479
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has no return typehint specified\\.$#"
480
+ count: 1
481
+ path: src/Handler/CurlFactory.php
482
+
483
+ -
484
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#"
485
+ count: 1
486
+ path: src/Handler/CurlFactory.php
487
+
488
+ -
489
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyBody\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
490
+ count: 1
491
+ path: src/Handler/CurlFactory.php
492
+
493
+ -
494
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHeaders\\(\\) has no return typehint specified\\.$#"
495
+ count: 1
496
+ path: src/Handler/CurlFactory.php
497
+
498
+ -
499
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHeaders\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#"
500
+ count: 1
501
+ path: src/Handler/CurlFactory.php
502
+
503
+ -
504
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:removeHeader\\(\\) has no return typehint specified\\.$#"
505
+ count: 1
506
+ path: src/Handler/CurlFactory.php
507
+
508
+ -
509
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:removeHeader\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
510
+ count: 1
511
+ path: src/Handler/CurlFactory.php
512
+
513
+ -
514
+ message: "#^Parameter \\#1 \\$str1 of function strcasecmp expects string, int\\|string given\\.$#"
515
+ count: 1
516
+ path: src/Handler/CurlFactory.php
517
+
518
+ -
519
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHandlerOptions\\(\\) has no return typehint specified\\.$#"
520
+ count: 1
521
+ path: src/Handler/CurlFactory.php
522
+
523
+ -
524
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:applyHandlerOptions\\(\\) has parameter \\$conf with no value type specified in iterable type array\\.$#"
525
+ count: 1
526
+ path: src/Handler/CurlFactory.php
527
+
528
+ -
529
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:retryFailedRewind\\(\\) has no return typehint specified\\.$#"
530
+ count: 1
531
+ path: src/Handler/CurlFactory.php
532
+
533
+ -
534
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:retryFailedRewind\\(\\) has parameter \\$ctx with no value type specified in iterable type array\\.$#"
535
+ count: 1
536
+ path: src/Handler/CurlFactory.php
537
+
538
+ -
539
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactory\\:\\:createHeaderFn\\(\\) has no return typehint specified\\.$#"
540
+ count: 1
541
+ path: src/Handler/CurlFactory.php
542
+
543
+ -
544
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactoryInterface\\:\\:create\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
545
+ count: 1
546
+ path: src/Handler/CurlFactoryInterface.php
547
+
548
+ -
549
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlFactoryInterface\\:\\:release\\(\\) has no return typehint specified\\.$#"
550
+ count: 1
551
+ path: src/Handler/CurlFactoryInterface.php
552
+
553
+ -
554
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
555
+ count: 1
556
+ path: src/Handler/CurlHandler.php
557
+
558
+ -
559
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#"
560
+ count: 1
561
+ path: src/Handler/CurlHandler.php
562
+
563
+ -
564
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
565
+ count: 1
566
+ path: src/Handler/CurlHandler.php
567
+
568
+ -
569
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$selectTimeout has no typehint specified\\.$#"
570
+ count: 1
571
+ path: src/Handler/CurlMultiHandler.php
572
+
573
+ -
574
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$active has no typehint specified\\.$#"
575
+ count: 1
576
+ path: src/Handler/CurlMultiHandler.php
577
+
578
+ -
579
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$handles has no typehint specified\\.$#"
580
+ count: 1
581
+ path: src/Handler/CurlMultiHandler.php
582
+
583
+ -
584
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$delays has no typehint specified\\.$#"
585
+ count: 1
586
+ path: src/Handler/CurlMultiHandler.php
587
+
588
+ -
589
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$options has no typehint specified\\.$#"
590
+ count: 1
591
+ path: src/Handler/CurlMultiHandler.php
592
+
593
+ -
594
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
595
+ count: 1
596
+ path: src/Handler/CurlMultiHandler.php
597
+
598
+ -
599
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__get\\(\\) has no return typehint specified\\.$#"
600
+ count: 1
601
+ path: src/Handler/CurlMultiHandler.php
602
+
603
+ -
604
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__get\\(\\) has parameter \\$name with no typehint specified\\.$#"
605
+ count: 1
606
+ path: src/Handler/CurlMultiHandler.php
607
+
608
+ -
609
+ message: "#^Property GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:\\$_mh \\(resource\\) does not accept resource\\|false\\.$#"
610
+ count: 1
611
+ path: src/Handler/CurlMultiHandler.php
612
+
613
+ -
614
+ message: "#^Parameter \\#1 \\$mh of function curl_multi_setopt expects resource, resource\\|false given\\.$#"
615
+ count: 1
616
+ path: src/Handler/CurlMultiHandler.php
617
+
618
+ -
619
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#"
620
+ count: 1
621
+ path: src/Handler/CurlMultiHandler.php
622
+
623
+ -
624
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
625
+ count: 1
626
+ path: src/Handler/CurlMultiHandler.php
627
+
628
+ -
629
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:tick\\(\\) has no return typehint specified\\.$#"
630
+ count: 1
631
+ path: src/Handler/CurlMultiHandler.php
632
+
633
+ -
634
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:execute\\(\\) has no return typehint specified\\.$#"
635
+ count: 1
636
+ path: src/Handler/CurlMultiHandler.php
637
+
638
+ -
639
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:addRequest\\(\\) has no return typehint specified\\.$#"
640
+ count: 1
641
+ path: src/Handler/CurlMultiHandler.php
642
+
643
+ -
644
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:addRequest\\(\\) has parameter \\$entry with no value type specified in iterable type array\\.$#"
645
+ count: 1
646
+ path: src/Handler/CurlMultiHandler.php
647
+
648
+ -
649
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:processMessages\\(\\) has no return typehint specified\\.$#"
650
+ count: 1
651
+ path: src/Handler/CurlMultiHandler.php
652
+
653
+ -
654
+ message: "#^Method GuzzleHttp\\\\Handler\\\\CurlMultiHandler\\:\\:timeToNext\\(\\) has no return typehint specified\\.$#"
655
+ count: 1
656
+ path: src/Handler/CurlMultiHandler.php
657
+
658
+ -
659
+ message: "#^Property GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:\\$headers type has no value type specified in iterable type array\\.$#"
660
+ count: 1
661
+ path: src/Handler/EasyHandle.php
662
+
663
+ -
664
+ message: "#^Property GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:\\$options type has no value type specified in iterable type array\\.$#"
665
+ count: 1
666
+ path: src/Handler/EasyHandle.php
667
+
668
+ -
669
+ message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:createResponse\\(\\) has no return typehint specified\\.$#"
670
+ count: 1
671
+ path: src/Handler/EasyHandle.php
672
+
673
+ -
674
+ message: "#^Parameter \\#1 \\$status of class GuzzleHttp\\\\Psr7\\\\Response constructor expects int, string given\\.$#"
675
+ count: 1
676
+ path: src/Handler/EasyHandle.php
677
+
678
+ -
679
+ message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:__get\\(\\) has no return typehint specified\\.$#"
680
+ count: 1
681
+ path: src/Handler/EasyHandle.php
682
+
683
+ -
684
+ message: "#^Method GuzzleHttp\\\\Handler\\\\EasyHandle\\:\\:__get\\(\\) has parameter \\$name with no typehint specified\\.$#"
685
+ count: 1
686
+ path: src/Handler/EasyHandle.php
687
+
688
+ -
689
+ message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$queue has no typehint specified\\.$#"
690
+ count: 1
691
+ path: src/Handler/MockHandler.php
692
+
693
+ -
694
+ message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$lastRequest has no typehint specified\\.$#"
695
+ count: 1
696
+ path: src/Handler/MockHandler.php
697
+
698
+ -
699
+ message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$lastOptions has no typehint specified\\.$#"
700
+ count: 1
701
+ path: src/Handler/MockHandler.php
702
+
703
+ -
704
+ message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$onFulfilled has no typehint specified\\.$#"
705
+ count: 1
706
+ path: src/Handler/MockHandler.php
707
+
708
+ -
709
+ message: "#^Property GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:\\$onRejected has no typehint specified\\.$#"
710
+ count: 1
711
+ path: src/Handler/MockHandler.php
712
+
713
+ -
714
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:createWithMiddleware\\(\\) has parameter \\$queue with no value type specified in iterable type array\\.$#"
715
+ count: 1
716
+ path: src/Handler/MockHandler.php
717
+
718
+ -
719
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__construct\\(\\) has parameter \\$queue with no value type specified in iterable type array\\.$#"
720
+ count: 1
721
+ path: src/Handler/MockHandler.php
722
+
723
+ -
724
+ message: "#^Parameter \\#2 \\$parameters of function call_user_func_array expects array\\<int, mixed\\>, array given\\.$#"
725
+ count: 1
726
+ path: src/Handler/MockHandler.php
727
+
728
+ -
729
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__invoke\\(\\) has no return typehint specified\\.$#"
730
+ count: 1
731
+ path: src/Handler/MockHandler.php
732
+
733
+ -
734
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
735
+ count: 1
736
+ path: src/Handler/MockHandler.php
737
+
738
+ -
739
+ message: "#^Binary operation \"\\*\" between float\\|int\\|string and 1000 results in an error\\.$#"
740
+ count: 1
741
+ path: src/Handler/MockHandler.php
742
+
743
+ -
744
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:append\\(\\) has no return typehint specified\\.$#"
745
+ count: 1
746
+ path: src/Handler/MockHandler.php
747
+
748
+ -
749
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:getLastOptions\\(\\) return type has no value type specified in iterable type array\\.$#"
750
+ count: 1
751
+ path: src/Handler/MockHandler.php
752
+
753
+ -
754
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:reset\\(\\) has no return typehint specified\\.$#"
755
+ count: 1
756
+ path: src/Handler/MockHandler.php
757
+
758
+ -
759
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#"
760
+ count: 1
761
+ path: src/Handler/MockHandler.php
762
+
763
+ -
764
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
765
+ count: 1
766
+ path: src/Handler/MockHandler.php
767
+
768
+ -
769
+ message: "#^Method GuzzleHttp\\\\Handler\\\\MockHandler\\:\\:invokeStats\\(\\) has parameter \\$reason with no typehint specified\\.$#"
770
+ count: 1
771
+ path: src/Handler/MockHandler.php
772
+
773
+ -
774
+ message: "#^Property GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:\\$lastHeaders has no typehint specified\\.$#"
775
+ count: 1
776
+ path: src/Handler/StreamHandler.php
777
+
778
+ -
779
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
780
+ count: 1
781
+ path: src/Handler/StreamHandler.php
782
+
783
+ -
784
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has no return typehint specified\\.$#"
785
+ count: 1
786
+ path: src/Handler/StreamHandler.php
787
+
788
+ -
789
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$error with no typehint specified\\.$#"
790
+ count: 1
791
+ path: src/Handler/StreamHandler.php
792
+
793
+ -
794
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
795
+ count: 1
796
+ path: src/Handler/StreamHandler.php
797
+
798
+ -
799
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:invokeStats\\(\\) has parameter \\$startTime with no typehint specified\\.$#"
800
+ count: 1
801
+ path: src/Handler/StreamHandler.php
802
+
803
+ -
804
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has no return typehint specified\\.$#"
805
+ count: 1
806
+ path: src/Handler/StreamHandler.php
807
+
808
+ -
809
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
810
+ count: 1
811
+ path: src/Handler/StreamHandler.php
812
+
813
+ -
814
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$startTime with no typehint specified\\.$#"
815
+ count: 1
816
+ path: src/Handler/StreamHandler.php
817
+
818
+ -
819
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createResponse\\(\\) has parameter \\$stream with no typehint specified\\.$#"
820
+ count: 1
821
+ path: src/Handler/StreamHandler.php
822
+
823
+ -
824
+ message: "#^Parameter \\#1 \\$status of class GuzzleHttp\\\\Psr7\\\\Response constructor expects int, string given\\.$#"
825
+ count: 1
826
+ path: src/Handler/StreamHandler.php
827
+
828
+ -
829
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createSink\\(\\) has no return typehint specified\\.$#"
830
+ count: 1
831
+ path: src/Handler/StreamHandler.php
832
+
833
+ -
834
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createSink\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
835
+ count: 1
836
+ path: src/Handler/StreamHandler.php
837
+
838
+ -
839
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has no return typehint specified\\.$#"
840
+ count: 1
841
+ path: src/Handler/StreamHandler.php
842
+
843
+ -
844
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#"
845
+ count: 1
846
+ path: src/Handler/StreamHandler.php
847
+
848
+ -
849
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
850
+ count: 1
851
+ path: src/Handler/StreamHandler.php
852
+
853
+ -
854
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:checkDecode\\(\\) has parameter \\$stream with no typehint specified\\.$#"
855
+ count: 1
856
+ path: src/Handler/StreamHandler.php
857
+
858
+ -
859
+ message: "#^Argument of an invalid type array\\<int, array\\<string, int\\|string\\>\\>\\|null supplied for foreach, only iterables are supported\\.$#"
860
+ count: 1
861
+ path: src/Handler/StreamHandler.php
862
+
863
+ -
864
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createStream\\(\\) has no return typehint specified\\.$#"
865
+ count: 1
866
+ path: src/Handler/StreamHandler.php
867
+
868
+ -
869
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:createStream\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
870
+ count: 1
871
+ path: src/Handler/StreamHandler.php
872
+
873
+ -
874
+ message: "#^Parameter \\#3 \\$use_include_path of function fopen expects bool, null given\\.$#"
875
+ count: 1
876
+ path: src/Handler/StreamHandler.php
877
+
878
+ -
879
+ message: "#^Parameter \\#1 \\$stream of function stream_set_timeout expects resource, resource\\|false given\\.$#"
880
+ count: 1
881
+ path: src/Handler/StreamHandler.php
882
+
883
+ -
884
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:resolveHost\\(\\) has no return typehint specified\\.$#"
885
+ count: 1
886
+ path: src/Handler/StreamHandler.php
887
+
888
+ -
889
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:resolveHost\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
890
+ count: 1
891
+ path: src/Handler/StreamHandler.php
892
+
893
+ -
894
+ message: "#^Cannot access offset 0 on array\\|false\\.$#"
895
+ count: 2
896
+ path: src/Handler/StreamHandler.php
897
+
898
+ -
899
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:getDefaultContext\\(\\) has no return typehint specified\\.$#"
900
+ count: 1
901
+ path: src/Handler/StreamHandler.php
902
+
903
+ -
904
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has no return typehint specified\\.$#"
905
+ count: 1
906
+ path: src/Handler/StreamHandler.php
907
+
908
+ -
909
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$options with no typehint specified\\.$#"
910
+ count: 1
911
+ path: src/Handler/StreamHandler.php
912
+
913
+ -
914
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$params with no typehint specified\\.$#"
915
+ count: 1
916
+ path: src/Handler/StreamHandler.php
917
+
918
+ -
919
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_proxy\\(\\) has parameter \\$value with no typehint specified\\.$#"
920
+ count: 1
921
+ path: src/Handler/StreamHandler.php
922
+
923
+ -
924
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has no return typehint specified\\.$#"
925
+ count: 1
926
+ path: src/Handler/StreamHandler.php
927
+
928
+ -
929
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$options with no typehint specified\\.$#"
930
+ count: 1
931
+ path: src/Handler/StreamHandler.php
932
+
933
+ -
934
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$params with no typehint specified\\.$#"
935
+ count: 1
936
+ path: src/Handler/StreamHandler.php
937
+
938
+ -
939
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_timeout\\(\\) has parameter \\$value with no typehint specified\\.$#"
940
+ count: 1
941
+ path: src/Handler/StreamHandler.php
942
+
943
+ -
944
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has no return typehint specified\\.$#"
945
+ count: 1
946
+ path: src/Handler/StreamHandler.php
947
+
948
+ -
949
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$options with no typehint specified\\.$#"
950
+ count: 1
951
+ path: src/Handler/StreamHandler.php
952
+
953
+ -
954
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$params with no typehint specified\\.$#"
955
+ count: 1
956
+ path: src/Handler/StreamHandler.php
957
+
958
+ -
959
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_verify\\(\\) has parameter \\$value with no typehint specified\\.$#"
960
+ count: 1
961
+ path: src/Handler/StreamHandler.php
962
+
963
+ -
964
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has no return typehint specified\\.$#"
965
+ count: 1
966
+ path: src/Handler/StreamHandler.php
967
+
968
+ -
969
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$options with no typehint specified\\.$#"
970
+ count: 1
971
+ path: src/Handler/StreamHandler.php
972
+
973
+ -
974
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$params with no typehint specified\\.$#"
975
+ count: 1
976
+ path: src/Handler/StreamHandler.php
977
+
978
+ -
979
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_cert\\(\\) has parameter \\$value with no typehint specified\\.$#"
980
+ count: 1
981
+ path: src/Handler/StreamHandler.php
982
+
983
+ -
984
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has no return typehint specified\\.$#"
985
+ count: 1
986
+ path: src/Handler/StreamHandler.php
987
+
988
+ -
989
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$options with no typehint specified\\.$#"
990
+ count: 1
991
+ path: src/Handler/StreamHandler.php
992
+
993
+ -
994
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$params with no typehint specified\\.$#"
995
+ count: 1
996
+ path: src/Handler/StreamHandler.php
997
+
998
+ -
999
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_progress\\(\\) has parameter \\$value with no typehint specified\\.$#"
1000
+ count: 1
1001
+ path: src/Handler/StreamHandler.php
1002
+
1003
+ -
1004
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has no return typehint specified\\.$#"
1005
+ count: 1
1006
+ path: src/Handler/StreamHandler.php
1007
+
1008
+ -
1009
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$options with no typehint specified\\.$#"
1010
+ count: 1
1011
+ path: src/Handler/StreamHandler.php
1012
+
1013
+ -
1014
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$params with no typehint specified\\.$#"
1015
+ count: 1
1016
+ path: src/Handler/StreamHandler.php
1017
+
1018
+ -
1019
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:add_debug\\(\\) has parameter \\$value with no typehint specified\\.$#"
1020
+ count: 1
1021
+ path: src/Handler/StreamHandler.php
1022
+
1023
+ -
1024
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:addNotification\\(\\) has no return typehint specified\\.$#"
1025
+ count: 1
1026
+ path: src/Handler/StreamHandler.php
1027
+
1028
+ -
1029
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:addNotification\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#"
1030
+ count: 1
1031
+ path: src/Handler/StreamHandler.php
1032
+
1033
+ -
1034
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:callArray\\(\\) has no return typehint specified\\.$#"
1035
+ count: 1
1036
+ path: src/Handler/StreamHandler.php
1037
+
1038
+ -
1039
+ message: "#^Method GuzzleHttp\\\\Handler\\\\StreamHandler\\:\\:callArray\\(\\) has parameter \\$functions with no value type specified in iterable type array\\.$#"
1040
+ count: 1
1041
+ path: src/Handler/StreamHandler.php
1042
+
1043
+ -
1044
+ message: "#^Property GuzzleHttp\\\\HandlerStack\\:\\:\\$stack type has no value type specified in iterable type array\\.$#"
1045
+ count: 1
1046
+ path: src/HandlerStack.php
1047
+
1048
+ -
1049
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1050
+ count: 1
1051
+ path: src/HandlerStack.php
1052
+
1053
+ -
1054
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:setHandler\\(\\) has no return typehint specified\\.$#"
1055
+ count: 1
1056
+ path: src/HandlerStack.php
1057
+
1058
+ -
1059
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:unshift\\(\\) has no return typehint specified\\.$#"
1060
+ count: 1
1061
+ path: src/HandlerStack.php
1062
+
1063
+ -
1064
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:push\\(\\) has no return typehint specified\\.$#"
1065
+ count: 1
1066
+ path: src/HandlerStack.php
1067
+
1068
+ -
1069
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:before\\(\\) has no return typehint specified\\.$#"
1070
+ count: 1
1071
+ path: src/HandlerStack.php
1072
+
1073
+ -
1074
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:after\\(\\) has no return typehint specified\\.$#"
1075
+ count: 1
1076
+ path: src/HandlerStack.php
1077
+
1078
+ -
1079
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:remove\\(\\) has no return typehint specified\\.$#"
1080
+ count: 1
1081
+ path: src/HandlerStack.php
1082
+
1083
+ -
1084
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:splice\\(\\) has no return typehint specified\\.$#"
1085
+ count: 1
1086
+ path: src/HandlerStack.php
1087
+
1088
+ -
1089
+ message: "#^Method GuzzleHttp\\\\HandlerStack\\:\\:debugCallable\\(\\) has parameter \\$fn with no value type specified in iterable type array\\.$#"
1090
+ count: 1
1091
+ path: src/HandlerStack.php
1092
+
1093
+ -
1094
+ message: "#^Parameter \\#1 \\$obj of function spl_object_hash expects object, callable given\\.$#"
1095
+ count: 1
1096
+ path: src/HandlerStack.php
1097
+
1098
+ -
1099
+ message: "#^Method GuzzleHttp\\\\MessageFormatter\\:\\:format\\(\\) should return string but returns string\\|null\\.$#"
1100
+ count: 1
1101
+ path: src/MessageFormatter.php
1102
+
1103
+ -
1104
+ message: "#^Method GuzzleHttp\\\\Middleware\\:\\:history\\(\\) has parameter \\$container with generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#"
1105
+ count: 1
1106
+ path: src/Middleware.php
1107
+
1108
+ -
1109
+ message: "#^Method GuzzleHttp\\\\Middleware\\:\\:history\\(\\) has parameter \\$container with no value type specified in iterable type array\\.$#"
1110
+ count: 1
1111
+ path: src/Middleware.php
1112
+
1113
+ -
1114
+ message: "#^Result of && is always false\\.$#"
1115
+ count: 1
1116
+ path: src/Middleware.php
1117
+
1118
+ -
1119
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
1120
+ count: 1
1121
+ path: src/Pool.php
1122
+
1123
+ -
1124
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:__construct\\(\\) has parameter \\$requests with no value type specified in iterable type array\\|Iterator\\.$#"
1125
+ count: 1
1126
+ path: src/Pool.php
1127
+
1128
+ -
1129
+ message: "#^Return typehint of method GuzzleHttp\\\\Pool\\:\\:promise\\(\\) has invalid type GuzzleHttp\\\\GuzzleHttp\\\\Promise\\\\Promise\\.$#"
1130
+ count: 1
1131
+ path: src/Pool.php
1132
+
1133
+ -
1134
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:promise\\(\\) should return GuzzleHttp\\\\GuzzleHttp\\\\Promise\\\\Promise but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\.$#"
1135
+ count: 1
1136
+ path: src/Pool.php
1137
+
1138
+ -
1139
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1140
+ count: 1
1141
+ path: src/Pool.php
1142
+
1143
+ -
1144
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) has parameter \\$requests with no value type specified in iterable type array\\|Iterator\\.$#"
1145
+ count: 1
1146
+ path: src/Pool.php
1147
+
1148
+ -
1149
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:batch\\(\\) return type has no value type specified in iterable type array\\.$#"
1150
+ count: 1
1151
+ path: src/Pool.php
1152
+
1153
+ -
1154
+ message: "#^Unsafe usage of new static\\(\\)\\.$#"
1155
+ count: 1
1156
+ path: src/Pool.php
1157
+
1158
+ -
1159
+ message: "#^Call to method wait\\(\\) on an unknown class GuzzleHttp\\\\GuzzleHttp\\\\Promise\\\\Promise\\.$#"
1160
+ count: 1
1161
+ path: src/Pool.php
1162
+
1163
+ -
1164
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$name with no typehint specified\\.$#"
1165
+ count: 1
1166
+ path: src/Pool.php
1167
+
1168
+ -
1169
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1170
+ count: 1
1171
+ path: src/Pool.php
1172
+
1173
+ -
1174
+ message: "#^Method GuzzleHttp\\\\Pool\\:\\:cmpCallback\\(\\) has parameter \\$results with no value type specified in iterable type array\\.$#"
1175
+ count: 1
1176
+ path: src/Pool.php
1177
+
1178
+ -
1179
+ message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1180
+ count: 1
1181
+ path: src/PrepareBodyMiddleware.php
1182
+
1183
+ -
1184
+ message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:addExpectHeader\\(\\) has parameter \\$modify with no value type specified in iterable type array\\.$#"
1185
+ count: 1
1186
+ path: src/PrepareBodyMiddleware.php
1187
+
1188
+ -
1189
+ message: "#^Method GuzzleHttp\\\\PrepareBodyMiddleware\\:\\:addExpectHeader\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1190
+ count: 1
1191
+ path: src/PrepareBodyMiddleware.php
1192
+
1193
+ -
1194
+ message: "#^Property GuzzleHttp\\\\RedirectMiddleware\\:\\:\\$defaultSettings has no typehint specified\\.$#"
1195
+ count: 1
1196
+ path: src/RedirectMiddleware.php
1197
+
1198
+ -
1199
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1200
+ count: 1
1201
+ path: src/RedirectMiddleware.php
1202
+
1203
+ -
1204
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:checkRedirect\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1205
+ count: 1
1206
+ path: src/RedirectMiddleware.php
1207
+
1208
+ -
1209
+ message: "#^Parameter \\#1 \\$str of function substr expects string, int given\\.$#"
1210
+ count: 1
1211
+ path: src/RedirectMiddleware.php
1212
+
1213
+ -
1214
+ message: "#^Parameter \\#1 \\$promise of method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) expects GuzzleHttp\\\\Promise\\\\PromiseInterface, GuzzleHttp\\\\Promise\\\\PromiseInterface\\|Psr\\\\Http\\\\Message\\\\ResponseInterface given\\.$#"
1215
+ count: 1
1216
+ path: src/RedirectMiddleware.php
1217
+
1218
+ -
1219
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) has parameter \\$statusCode with no typehint specified\\.$#"
1220
+ count: 1
1221
+ path: src/RedirectMiddleware.php
1222
+
1223
+ -
1224
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:withTracking\\(\\) has parameter \\$uri with no typehint specified\\.$#"
1225
+ count: 1
1226
+ path: src/RedirectMiddleware.php
1227
+
1228
+ -
1229
+ message: "#^Parameter \\#2 \\$value of method Psr\\\\Http\\\\Message\\\\MessageInterface\\:\\:withHeader\\(\\) expects array\\<string\\>\\|string, array given\\.$#"
1230
+ count: 2
1231
+ path: src/RedirectMiddleware.php
1232
+
1233
+ -
1234
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:guardMax\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1235
+ count: 1
1236
+ path: src/RedirectMiddleware.php
1237
+
1238
+ -
1239
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:modifyRequest\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1240
+ count: 1
1241
+ path: src/RedirectMiddleware.php
1242
+
1243
+ -
1244
+ message: "#^Method GuzzleHttp\\\\RedirectMiddleware\\:\\:redirectUri\\(\\) has parameter \\$protocols with no value type specified in iterable type array\\.$#"
1245
+ count: 1
1246
+ path: src/RedirectMiddleware.php
1247
+
1248
+ -
1249
+ message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:__invoke\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1250
+ count: 1
1251
+ path: src/RetryMiddleware.php
1252
+
1253
+ -
1254
+ message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:onFulfilled\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1255
+ count: 1
1256
+ path: src/RetryMiddleware.php
1257
+
1258
+ -
1259
+ message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:onRejected\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1260
+ count: 1
1261
+ path: src/RetryMiddleware.php
1262
+
1263
+ -
1264
+ message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:doRetry\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
1265
+ count: 1
1266
+ path: src/RetryMiddleware.php
1267
+
1268
+ -
1269
+ message: "#^Method GuzzleHttp\\\\RetryMiddleware\\:\\:doRetry\\(\\) should return GuzzleHttp\\\\RetryMiddleware but returns GuzzleHttp\\\\Promise\\\\PromiseInterface\\.$#"
1270
+ count: 1
1271
+ path: src/RetryMiddleware.php
1272
+
1273
+ -
1274
+ message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$request has no typehint specified\\.$#"
1275
+ count: 1
1276
+ path: src/TransferStats.php
1277
+
1278
+ -
1279
+ message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$response has no typehint specified\\.$#"
1280
+ count: 1
1281
+ path: src/TransferStats.php
1282
+
1283
+ -
1284
+ message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$transferTime has no typehint specified\\.$#"
1285
+ count: 1
1286
+ path: src/TransferStats.php
1287
+
1288
+ -
1289
+ message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$handlerStats has no typehint specified\\.$#"
1290
+ count: 1
1291
+ path: src/TransferStats.php
1292
+
1293
+ -
1294
+ message: "#^Property GuzzleHttp\\\\TransferStats\\:\\:\\$handlerErrorData has no typehint specified\\.$#"
1295
+ count: 1
1296
+ path: src/TransferStats.php
1297
+
1298
+ -
1299
+ message: "#^Method GuzzleHttp\\\\TransferStats\\:\\:__construct\\(\\) has parameter \\$handlerStats with no value type specified in iterable type array\\.$#"
1300
+ count: 1
1301
+ path: src/TransferStats.php
1302
+
1303
+ -
1304
+ message: "#^Method GuzzleHttp\\\\TransferStats\\:\\:getHandlerStats\\(\\) return type has no value type specified in iterable type array\\.$#"
1305
+ count: 1
1306
+ path: src/TransferStats.php
1307
+
1308
+ -
1309
+ message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$variables type has no value type specified in iterable type array\\.$#"
1310
+ count: 1
1311
+ path: src/UriTemplate.php
1312
+
1313
+ -
1314
+ message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$operatorHash type has no value type specified in iterable type array\\.$#"
1315
+ count: 1
1316
+ path: src/UriTemplate.php
1317
+
1318
+ -
1319
+ message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$delims type has no value type specified in iterable type array\\.$#"
1320
+ count: 1
1321
+ path: src/UriTemplate.php
1322
+
1323
+ -
1324
+ message: "#^Property GuzzleHttp\\\\UriTemplate\\:\\:\\$delimsPct type has no value type specified in iterable type array\\.$#"
1325
+ count: 1
1326
+ path: src/UriTemplate.php
1327
+
1328
+ -
1329
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has no return typehint specified\\.$#"
1330
+ count: 1
1331
+ path: src/UriTemplate.php
1332
+
1333
+ -
1334
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has parameter \\$template with no typehint specified\\.$#"
1335
+ count: 1
1336
+ path: src/UriTemplate.php
1337
+
1338
+ -
1339
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expand\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#"
1340
+ count: 1
1341
+ path: src/UriTemplate.php
1342
+
1343
+ -
1344
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:parseExpression\\(\\) return type has no value type specified in iterable type array\\.$#"
1345
+ count: 1
1346
+ path: src/UriTemplate.php
1347
+
1348
+ -
1349
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:expandMatch\\(\\) has parameter \\$matches with no value type specified in iterable type array\\.$#"
1350
+ count: 1
1351
+ path: src/UriTemplate.php
1352
+
1353
+ -
1354
+ message: "#^Method GuzzleHttp\\\\UriTemplate\\:\\:isAssoc\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#"
1355
+ count: 1
1356
+ path: src/UriTemplate.php
1357
+
1358
+ -
1359
+ message: "#^Function GuzzleHttp\\\\uri_template\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#"
1360
+ count: 1
1361
+ path: src/functions.php
1362
+
1363
+ -
1364
+ message: "#^Function uri_template not found\\.$#"
1365
+ count: 1
1366
+ path: src/functions.php
1367
+
1368
+ -
1369
+ message: "#^Parameter \\#1 \\$str of function rtrim expects string, string\\|false given\\.$#"
1370
+ count: 1
1371
+ path: src/functions.php
1372
+
1373
+ -
1374
+ message: "#^Function GuzzleHttp\\\\headers_from_lines\\(\\) has parameter \\$lines with no value type specified in iterable type iterable\\.$#"
1375
+ count: 1
1376
+ path: src/functions.php
1377
+
1378
+ -
1379
+ message: "#^Function GuzzleHttp\\\\headers_from_lines\\(\\) return type has no value type specified in iterable type array\\.$#"
1380
+ count: 1
1381
+ path: src/functions.php
1382
+
1383
+ -
1384
+ message: "#^Function GuzzleHttp\\\\debug_resource\\(\\) should return resource but returns resource\\|false\\.$#"
1385
+ count: 1
1386
+ path: src/functions.php
1387
+
1388
+ -
1389
+ message: "#^Function GuzzleHttp\\\\normalize_header_keys\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#"
1390
+ count: 1
1391
+ path: src/functions.php
1392
+
1393
+ -
1394
+ message: "#^Function GuzzleHttp\\\\normalize_header_keys\\(\\) return type has no value type specified in iterable type array\\.$#"
1395
+ count: 1
1396
+ path: src/functions.php
1397
+
1398
+ -
1399
+ message: "#^Function GuzzleHttp\\\\is_host_in_noproxy\\(\\) has parameter \\$noProxyArray with no value type specified in iterable type array\\.$#"
1400
+ count: 1
1401
+ path: src/functions.php
1402
+
1403
+ -
1404
+ message: "#^Cannot access offset 0 on array\\<int, string\\>\\|false\\.$#"
1405
+ count: 1
1406
+ path: src/functions.php
1407
+
1408
+ -
1409
+ message: "#^Function GuzzleHttp\\\\json_encode\\(\\) should return string but returns string\\|false\\.$#"
1410
+ count: 1
1411
+ path: src/functions.php
1412
+
vendor/guzzlehttp/guzzle/src/Client.php CHANGED
@@ -1,422 +1,531 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Cookie\CookieJar;
5
- use GuzzleHttp\Promise;
6
- use GuzzleHttp\Psr7;
7
- use Psr\Http\Message\UriInterface;
8
- use Psr\Http\Message\RequestInterface;
9
- use Psr\Http\Message\ResponseInterface;
10
-
11
- /**
12
- * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
13
- * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
14
- * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
15
- * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
16
- * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
17
- * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
18
- * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
19
- * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
20
- * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
21
- * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
22
- * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
23
- * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
24
- */
25
- class Client implements ClientInterface
26
- {
27
- /** @var array Default request options */
28
- private $config;
29
-
30
- /**
31
- * Clients accept an array of constructor parameters.
32
- *
33
- * Here's an example of creating a client using a base_uri and an array of
34
- * default request options to apply to each request:
35
- *
36
- * $client = new Client([
37
- * 'base_uri' => 'http://www.foo.com/1.0/',
38
- * 'timeout' => 0,
39
- * 'allow_redirects' => false,
40
- * 'proxy' => '192.168.16.1:10'
41
- * ]);
42
- *
43
- * Client configuration settings include the following options:
44
- *
45
- * - handler: (callable) Function that transfers HTTP requests over the
46
- * wire. The function is called with a Psr7\Http\Message\RequestInterface
47
- * and array of transfer options, and must return a
48
- * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
49
- * Psr7\Http\Message\ResponseInterface on success. "handler" is a
50
- * constructor only option that cannot be overridden in per/request
51
- * options. If no handler is provided, a default handler will be created
52
- * that enables all of the request options below by attaching all of the
53
- * default middleware to the handler.
54
- * - base_uri: (string|UriInterface) Base URI of the client that is merged
55
- * into relative URIs. Can be a string or instance of UriInterface.
56
- * - **: any request option
57
- *
58
- * @param array $config Client configuration settings.
59
- *
60
- * @see \GuzzleHttp\RequestOptions for a list of available request options.
61
- */
62
- public function __construct(array $config = [])
63
- {
64
- if (!isset($config['handler'])) {
65
- $config['handler'] = HandlerStack::create();
66
- } elseif (!is_callable($config['handler'])) {
67
- throw new \InvalidArgumentException('handler must be a callable');
68
- }
69
-
70
- // Convert the base_uri to a UriInterface
71
- if (isset($config['base_uri'])) {
72
- $config['base_uri'] = Psr7\uri_for($config['base_uri']);
73
- }
74
-
75
- $this->configureDefaults($config);
76
- }
77
-
78
- public function __call($method, $args)
79
- {
80
- if (count($args) < 1) {
81
- throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
82
- }
83
-
84
- $uri = $args[0];
85
- $opts = isset($args[1]) ? $args[1] : [];
86
-
87
- return substr($method, -5) === 'Async'
88
- ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
89
- : $this->request($method, $uri, $opts);
90
- }
91
-
92
- public function sendAsync(RequestInterface $request, array $options = [])
93
- {
94
- // Merge the base URI into the request URI if needed.
95
- $options = $this->prepareDefaults($options);
96
-
97
- return $this->transfer(
98
- $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
99
- $options
100
- );
101
- }
102
-
103
- public function send(RequestInterface $request, array $options = [])
104
- {
105
- $options[RequestOptions::SYNCHRONOUS] = true;
106
- return $this->sendAsync($request, $options)->wait();
107
- }
108
-
109
- public function requestAsync($method, $uri = '', array $options = [])
110
- {
111
- $options = $this->prepareDefaults($options);
112
- // Remove request modifying parameter because it can be done up-front.
113
- $headers = isset($options['headers']) ? $options['headers'] : [];
114
- $body = isset($options['body']) ? $options['body'] : null;
115
- $version = isset($options['version']) ? $options['version'] : '1.1';
116
- // Merge the URI into the base URI.
117
- $uri = $this->buildUri($uri, $options);
118
- if (is_array($body)) {
119
- $this->invalidBody();
120
- }
121
- $request = new Psr7\Request($method, $uri, $headers, $body, $version);
122
- // Remove the option so that they are not doubly-applied.
123
- unset($options['headers'], $options['body'], $options['version']);
124
-
125
- return $this->transfer($request, $options);
126
- }
127
-
128
- public function request($method, $uri = '', array $options = [])
129
- {
130
- $options[RequestOptions::SYNCHRONOUS] = true;
131
- return $this->requestAsync($method, $uri, $options)->wait();
132
- }
133
-
134
- public function getConfig($option = null)
135
- {
136
- return $option === null
137
- ? $this->config
138
- : (isset($this->config[$option]) ? $this->config[$option] : null);
139
- }
140
-
141
- private function buildUri($uri, array $config)
142
- {
143
- // for BC we accept null which would otherwise fail in uri_for
144
- $uri = Psr7\uri_for($uri === null ? '' : $uri);
145
-
146
- if (isset($config['base_uri'])) {
147
- $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
148
- }
149
-
150
- return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
151
- }
152
-
153
- /**
154
- * Configures the default options for a client.
155
- *
156
- * @param array $config
157
- */
158
- private function configureDefaults(array $config)
159
- {
160
- $defaults = [
161
- 'allow_redirects' => RedirectMiddleware::$defaultSettings,
162
- 'http_errors' => true,
163
- 'decode_content' => true,
164
- 'verify' => true,
165
- 'cookies' => false
166
- ];
167
-
168
- // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
169
-
170
- // We can only trust the HTTP_PROXY environment variable in a CLI
171
- // process due to the fact that PHP has no reliable mechanism to
172
- // get environment variables that start with "HTTP_".
173
- if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
174
- $defaults['proxy']['http'] = getenv('HTTP_PROXY');
175
- }
176
-
177
- if ($proxy = getenv('HTTPS_PROXY')) {
178
- $defaults['proxy']['https'] = $proxy;
179
- }
180
-
181
- if ($noProxy = getenv('NO_PROXY')) {
182
- $cleanedNoProxy = str_replace(' ', '', $noProxy);
183
- $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
184
- }
185
-
186
- $this->config = $config + $defaults;
187
-
188
- if (!empty($config['cookies']) && $config['cookies'] === true) {
189
- $this->config['cookies'] = new CookieJar();
190
- }
191
-
192
- // Add the default user-agent header.
193
- if (!isset($this->config['headers'])) {
194
- $this->config['headers'] = ['User-Agent' => default_user_agent()];
195
- } else {
196
- // Add the User-Agent header if one was not already set.
197
- foreach (array_keys($this->config['headers']) as $name) {
198
- if (strtolower($name) === 'user-agent') {
199
- return;
200
- }
201
- }
202
- $this->config['headers']['User-Agent'] = default_user_agent();
203
- }
204
- }
205
-
206
- /**
207
- * Merges default options into the array.
208
- *
209
- * @param array $options Options to modify by reference
210
- *
211
- * @return array
212
- */
213
- private function prepareDefaults(array $options)
214
- {
215
- $defaults = $this->config;
216
-
217
- if (!empty($defaults['headers'])) {
218
- // Default headers are only added if they are not present.
219
- $defaults['_conditional'] = $defaults['headers'];
220
- unset($defaults['headers']);
221
- }
222
-
223
- // Special handling for headers is required as they are added as
224
- // conditional headers and as headers passed to a request ctor.
225
- if (array_key_exists('headers', $options)) {
226
- // Allows default headers to be unset.
227
- if ($options['headers'] === null) {
228
- $defaults['_conditional'] = null;
229
- unset($options['headers']);
230
- } elseif (!is_array($options['headers'])) {
231
- throw new \InvalidArgumentException('headers must be an array');
232
- }
233
- }
234
-
235
- // Shallow merge defaults underneath options.
236
- $result = $options + $defaults;
237
-
238
- // Remove null values.
239
- foreach ($result as $k => $v) {
240
- if ($v === null) {
241
- unset($result[$k]);
242
- }
243
- }
244
-
245
- return $result;
246
- }
247
-
248
- /**
249
- * Transfers the given request and applies request options.
250
- *
251
- * The URI of the request is not modified and the request options are used
252
- * as-is without merging in default options.
253
- *
254
- * @param RequestInterface $request
255
- * @param array $options
256
- *
257
- * @return Promise\PromiseInterface
258
- */
259
- private function transfer(RequestInterface $request, array $options)
260
- {
261
- // save_to -> sink
262
- if (isset($options['save_to'])) {
263
- $options['sink'] = $options['save_to'];
264
- unset($options['save_to']);
265
- }
266
-
267
- // exceptions -> http_errors
268
- if (isset($options['exceptions'])) {
269
- $options['http_errors'] = $options['exceptions'];
270
- unset($options['exceptions']);
271
- }
272
-
273
- $request = $this->applyOptions($request, $options);
274
- $handler = $options['handler'];
275
-
276
- try {
277
- return Promise\promise_for($handler($request, $options));
278
- } catch (\Exception $e) {
279
- return Promise\rejection_for($e);
280
- }
281
- }
282
-
283
- /**
284
- * Applies the array of request options to a request.
285
- *
286
- * @param RequestInterface $request
287
- * @param array $options
288
- *
289
- * @return RequestInterface
290
- */
291
- private function applyOptions(RequestInterface $request, array &$options)
292
- {
293
- $modify = [
294
- 'set_headers' => [],
295
- ];
296
-
297
- if (isset($options['headers'])) {
298
- $modify['set_headers'] = $options['headers'];
299
- unset($options['headers']);
300
- }
301
-
302
- if (isset($options['form_params'])) {
303
- if (isset($options['multipart'])) {
304
- throw new \InvalidArgumentException('You cannot use '
305
- . 'form_params and multipart at the same time. Use the '
306
- . 'form_params option if you want to send application/'
307
- . 'x-www-form-urlencoded requests, and the multipart '
308
- . 'option to send multipart/form-data requests.');
309
- }
310
- $options['body'] = http_build_query($options['form_params'], '', '&');
311
- unset($options['form_params']);
312
- // Ensure that we don't have the header in different case and set the new value.
313
- $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
314
- $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
315
- }
316
-
317
- if (isset($options['multipart'])) {
318
- $options['body'] = new Psr7\MultipartStream($options['multipart']);
319
- unset($options['multipart']);
320
- }
321
-
322
- if (isset($options['json'])) {
323
- $options['body'] = \GuzzleHttp\json_encode($options['json']);
324
- unset($options['json']);
325
- // Ensure that we don't have the header in different case and set the new value.
326
- $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
327
- $options['_conditional']['Content-Type'] = 'application/json';
328
- }
329
-
330
- if (!empty($options['decode_content'])
331
- && $options['decode_content'] !== true
332
- ) {
333
- // Ensure that we don't have the header in different case and set the new value.
334
- $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
335
- $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
336
- }
337
-
338
- if (isset($options['body'])) {
339
- if (is_array($options['body'])) {
340
- $this->invalidBody();
341
- }
342
- $modify['body'] = Psr7\stream_for($options['body']);
343
- unset($options['body']);
344
- }
345
-
346
- if (!empty($options['auth']) && is_array($options['auth'])) {
347
- $value = $options['auth'];
348
- $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
349
- switch ($type) {
350
- case 'basic':
351
- // Ensure that we don't have the header in different case and set the new value.
352
- $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
353
- $modify['set_headers']['Authorization'] = 'Basic '
354
- . base64_encode("$value[0]:$value[1]");
355
- break;
356
- case 'digest':
357
- // @todo: Do not rely on curl
358
- $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
359
- $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
360
- break;
361
- case 'ntlm':
362
- $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
363
- $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
364
- break;
365
- }
366
- }
367
-
368
- if (isset($options['query'])) {
369
- $value = $options['query'];
370
- if (is_array($value)) {
371
- $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
372
- }
373
- if (!is_string($value)) {
374
- throw new \InvalidArgumentException('query must be a string or array');
375
- }
376
- $modify['query'] = $value;
377
- unset($options['query']);
378
- }
379
-
380
- // Ensure that sink is not an invalid value.
381
- if (isset($options['sink'])) {
382
- // TODO: Add more sink validation?
383
- if (is_bool($options['sink'])) {
384
- throw new \InvalidArgumentException('sink must not be a boolean');
385
- }
386
- }
387
-
388
- $request = Psr7\modify_request($request, $modify);
389
- if ($request->getBody() instanceof Psr7\MultipartStream) {
390
- // Use a multipart/form-data POST if a Content-Type is not set.
391
- // Ensure that we don't have the header in different case and set the new value.
392
- $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
393
- $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
394
- . $request->getBody()->getBoundary();
395
- }
396
-
397
- // Merge in conditional headers if they are not present.
398
- if (isset($options['_conditional'])) {
399
- // Build up the changes so it's in a single clone of the message.
400
- $modify = [];
401
- foreach ($options['_conditional'] as $k => $v) {
402
- if (!$request->hasHeader($k)) {
403
- $modify['set_headers'][$k] = $v;
404
- }
405
- }
406
- $request = Psr7\modify_request($request, $modify);
407
- // Don't pass this internal value along to middleware/handlers.
408
- unset($options['_conditional']);
409
- }
410
-
411
- return $request;
412
- }
413
-
414
- private function invalidBody()
415
- {
416
- throw new \InvalidArgumentException('Passing in the "body" request '
417
- . 'option as an array to send a POST request has been deprecated. '
418
- . 'Please use the "form_params" request option to send a '
419
- . 'application/x-www-form-urlencoded request, or the "multipart" '
420
- . 'request option to send a multipart/form-data request.');
421
- }
422
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Cookie\CookieJar;
5
+ use GuzzleHttp\Exception\InvalidArgumentException;
6
+ use GuzzleHttp\Promise;
7
+ use GuzzleHttp\Psr7;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ /**
13
+ * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
14
+ * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
15
+ * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
16
+ * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
17
+ * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
18
+ * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
19
+ * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
20
+ * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
21
+ * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
22
+ * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
23
+ * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
24
+ * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
25
+ */
26
+ class Client implements ClientInterface
27
+ {
28
+ /** @var array Default request options */
29
+ private $config;
30
+
31
+ /**
32
+ * Clients accept an array of constructor parameters.
33
+ *
34
+ * Here's an example of creating a client using a base_uri and an array of
35
+ * default request options to apply to each request:
36
+ *
37
+ * $client = new Client([
38
+ * 'base_uri' => 'http://www.foo.com/1.0/',
39
+ * 'timeout' => 0,
40
+ * 'allow_redirects' => false,
41
+ * 'proxy' => '192.168.16.1:10'
42
+ * ]);
43
+ *
44
+ * Client configuration settings include the following options:
45
+ *
46
+ * - handler: (callable) Function that transfers HTTP requests over the
47
+ * wire. The function is called with a Psr7\Http\Message\RequestInterface
48
+ * and array of transfer options, and must return a
49
+ * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
50
+ * Psr7\Http\Message\ResponseInterface on success. "handler" is a
51
+ * constructor only option that cannot be overridden in per/request
52
+ * options. If no handler is provided, a default handler will be created
53
+ * that enables all of the request options below by attaching all of the
54
+ * default middleware to the handler.
55
+ * - base_uri: (string|UriInterface) Base URI of the client that is merged
56
+ * into relative URIs. Can be a string or instance of UriInterface.
57
+ * - **: any request option
58
+ *
59
+ * @param array $config Client configuration settings.
60
+ *
61
+ * @see \GuzzleHttp\RequestOptions for a list of available request options.
62
+ */
63
+ public function __construct(array $config = [])
64
+ {
65
+ if (!isset($config['handler'])) {
66
+ $config['handler'] = HandlerStack::create();
67
+ } elseif (!is_callable($config['handler'])) {
68
+ throw new \InvalidArgumentException('handler must be a callable');
69
+ }
70
+
71
+ // Convert the base_uri to a UriInterface
72
+ if (isset($config['base_uri'])) {
73
+ $config['base_uri'] = Psr7\uri_for($config['base_uri']);
74
+ }
75
+
76
+ $this->configureDefaults($config);
77
+ }
78
+
79
+ /**
80
+ * @param string $method
81
+ * @param array $args
82
+ *
83
+ * @return Promise\PromiseInterface
84
+ */
85
+ public function __call($method, $args)
86
+ {
87
+ if (count($args) < 1) {
88
+ throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
89
+ }
90
+
91
+ $uri = $args[0];
92
+ $opts = isset($args[1]) ? $args[1] : [];
93
+
94
+ return substr($method, -5) === 'Async'
95
+ ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
96
+ : $this->request($method, $uri, $opts);
97
+ }
98
+
99
+ /**
100
+ * Asynchronously send an HTTP request.
101
+ *
102
+ * @param array $options Request options to apply to the given
103
+ * request and to the transfer. See \GuzzleHttp\RequestOptions.
104
+ *
105
+ * @return PromiseInterface
106
+ */
107
+ public function sendAsync(RequestInterface $request, array $options = [])
108
+ {
109
+ // Merge the base URI into the request URI if needed.
110
+ $options = $this->prepareDefaults($options);
111
+
112
+ return $this->transfer(
113
+ $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
114
+ $options
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Send an HTTP request.
120
+ *
121
+ * @param array $options Request options to apply to the given
122
+ * request and to the transfer. See \GuzzleHttp\RequestOptions.
123
+ *
124
+ * @return ResponseInterface
125
+ * @throws GuzzleException
126
+ */
127
+ public function send(RequestInterface $request, array $options = [])
128
+ {
129
+ $options[RequestOptions::SYNCHRONOUS] = true;
130
+ return $this->sendAsync($request, $options)->wait();
131
+ }
132
+
133
+ /**
134
+ * Create and send an asynchronous HTTP request.
135
+ *
136
+ * Use an absolute path to override the base path of the client, or a
137
+ * relative path to append to the base path of the client. The URL can
138
+ * contain the query string as well. Use an array to provide a URL
139
+ * template and additional variables to use in the URL template expansion.
140
+ *
141
+ * @param string $method HTTP method
142
+ * @param string|UriInterface $uri URI object or string.
143
+ * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
144
+ *
145
+ * @return PromiseInterface
146
+ */
147
+ public function requestAsync($method, $uri = '', array $options = [])
148
+ {
149
+ $options = $this->prepareDefaults($options);
150
+ // Remove request modifying parameter because it can be done up-front.
151
+ $headers = isset($options['headers']) ? $options['headers'] : [];
152
+ $body = isset($options['body']) ? $options['body'] : null;
153
+ $version = isset($options['version']) ? $options['version'] : '1.1';
154
+ // Merge the URI into the base URI.
155
+ $uri = $this->buildUri($uri, $options);
156
+ if (is_array($body)) {
157
+ $this->invalidBody();
158
+ }
159
+ $request = new Psr7\Request($method, $uri, $headers, $body, $version);
160
+ // Remove the option so that they are not doubly-applied.
161
+ unset($options['headers'], $options['body'], $options['version']);
162
+
163
+ return $this->transfer($request, $options);
164
+ }
165
+
166
+ /**
167
+ * Create and send an HTTP request.
168
+ *
169
+ * Use an absolute path to override the base path of the client, or a
170
+ * relative path to append to the base path of the client. The URL can
171
+ * contain the query string as well.
172
+ *
173
+ * @param string $method HTTP method.
174
+ * @param string|UriInterface $uri URI object or string.
175
+ * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
176
+ *
177
+ * @return ResponseInterface
178
+ * @throws GuzzleException
179
+ */
180
+ public function request($method, $uri = '', array $options = [])
181
+ {
182
+ $options[RequestOptions::SYNCHRONOUS] = true;
183
+ return $this->requestAsync($method, $uri, $options)->wait();
184
+ }
185
+
186
+ /**
187
+ * Get a client configuration option.
188
+ *
189
+ * These options include default request options of the client, a "handler"
190
+ * (if utilized by the concrete client), and a "base_uri" if utilized by
191
+ * the concrete client.
192
+ *
193
+ * @param string|null $option The config option to retrieve.
194
+ *
195
+ * @return mixed
196
+ */
197
+ public function getConfig($option = null)
198
+ {
199
+ return $option === null
200
+ ? $this->config
201
+ : (isset($this->config[$option]) ? $this->config[$option] : null);
202
+ }
203
+
204
+ /**
205
+ * @param string|null $uri
206
+ *
207
+ * @return UriInterface
208
+ */
209
+ private function buildUri($uri, array $config)
210
+ {
211
+ // for BC we accept null which would otherwise fail in uri_for
212
+ $uri = Psr7\uri_for($uri === null ? '' : $uri);
213
+
214
+ if (isset($config['base_uri'])) {
215
+ $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
216
+ }
217
+
218
+ if ($uri->getHost() && isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) {
219
+ $idnOptions = ($config['idn_conversion'] === true) ? IDNA_DEFAULT : $config['idn_conversion'];
220
+
221
+ $asciiHost = idn_to_ascii($uri->getHost(), $idnOptions, INTL_IDNA_VARIANT_UTS46, $info);
222
+ if ($asciiHost === false) {
223
+ $errorBitSet = isset($info['errors']) ? $info['errors'] : 0;
224
+
225
+ $errorConstants = array_filter(array_keys(get_defined_constants()), function ($name) {
226
+ return substr($name, 0, 11) === 'IDNA_ERROR_';
227
+ });
228
+
229
+ $errors = [];
230
+ foreach ($errorConstants as $errorConstant) {
231
+ if ($errorBitSet & constant($errorConstant)) {
232
+ $errors[] = $errorConstant;
233
+ }
234
+ }
235
+
236
+ $errorMessage = 'IDN conversion failed';
237
+ if ($errors) {
238
+ $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')';
239
+ }
240
+
241
+ throw new InvalidArgumentException($errorMessage);
242
+ } else {
243
+ if ($uri->getHost() !== $asciiHost) {
244
+ // Replace URI only if the ASCII version is different
245
+ $uri = $uri->withHost($asciiHost);
246
+ }
247
+ }
248
+ }
249
+
250
+ return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
251
+ }
252
+
253
+ /**
254
+ * Configures the default options for a client.
255
+ *
256
+ * @param array $config
257
+ * @return void
258
+ */
259
+ private function configureDefaults(array $config)
260
+ {
261
+ $defaults = [
262
+ 'allow_redirects' => RedirectMiddleware::$defaultSettings,
263
+ 'http_errors' => true,
264
+ 'decode_content' => true,
265
+ 'verify' => true,
266
+ 'cookies' => false
267
+ ];
268
+
269
+ // idn_to_ascii() is a part of ext-intl and might be not available
270
+ $defaults['idn_conversion'] = function_exists('idn_to_ascii');
271
+
272
+ // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
273
+
274
+ // We can only trust the HTTP_PROXY environment variable in a CLI
275
+ // process due to the fact that PHP has no reliable mechanism to
276
+ // get environment variables that start with "HTTP_".
277
+ if (php_sapi_name() === 'cli' && getenv('HTTP_PROXY')) {
278
+ $defaults['proxy']['http'] = getenv('HTTP_PROXY');
279
+ }
280
+
281
+ if ($proxy = getenv('HTTPS_PROXY')) {
282
+ $defaults['proxy']['https'] = $proxy;
283
+ }
284
+
285
+ if ($noProxy = getenv('NO_PROXY')) {
286
+ $cleanedNoProxy = str_replace(' ', '', $noProxy);
287
+ $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
288
+ }
289
+
290
+ $this->config = $config + $defaults;
291
+
292
+ if (!empty($config['cookies']) && $config['cookies'] === true) {
293
+ $this->config['cookies'] = new CookieJar();
294
+ }
295
+
296
+ // Add the default user-agent header.
297
+ if (!isset($this->config['headers'])) {
298
+ $this->config['headers'] = ['User-Agent' => default_user_agent()];
299
+ } else {
300
+ // Add the User-Agent header if one was not already set.
301
+ foreach (array_keys($this->config['headers']) as $name) {
302
+ if (strtolower($name) === 'user-agent') {
303
+ return;
304
+ }
305
+ }
306
+ $this->config['headers']['User-Agent'] = default_user_agent();
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Merges default options into the array.
312
+ *
313
+ * @param array $options Options to modify by reference
314
+ *
315
+ * @return array
316
+ */
317
+ private function prepareDefaults(array $options)
318
+ {
319
+ $defaults = $this->config;
320
+
321
+ if (!empty($defaults['headers'])) {
322
+ // Default headers are only added if they are not present.
323
+ $defaults['_conditional'] = $defaults['headers'];
324
+ unset($defaults['headers']);
325
+ }
326
+
327
+ // Special handling for headers is required as they are added as
328
+ // conditional headers and as headers passed to a request ctor.
329
+ if (array_key_exists('headers', $options)) {
330
+ // Allows default headers to be unset.
331
+ if ($options['headers'] === null) {
332
+ $defaults['_conditional'] = [];
333
+ unset($options['headers']);
334
+ } elseif (!is_array($options['headers'])) {
335
+ throw new \InvalidArgumentException('headers must be an array');
336
+ }
337
+ }
338
+
339
+ // Shallow merge defaults underneath options.
340
+ $result = $options + $defaults;
341
+
342
+ // Remove null values.
343
+ foreach ($result as $k => $v) {
344
+ if ($v === null) {
345
+ unset($result[$k]);
346
+ }
347
+ }
348
+
349
+ return $result;
350
+ }
351
+
352
+ /**
353
+ * Transfers the given request and applies request options.
354
+ *
355
+ * The URI of the request is not modified and the request options are used
356
+ * as-is without merging in default options.
357
+ *
358
+ * @param array $options See \GuzzleHttp\RequestOptions.
359
+ *
360
+ * @return Promise\PromiseInterface
361
+ */
362
+ private function transfer(RequestInterface $request, array $options)
363
+ {
364
+ // save_to -> sink
365
+ if (isset($options['save_to'])) {
366
+ $options['sink'] = $options['save_to'];
367
+ unset($options['save_to']);
368
+ }
369
+
370
+ // exceptions -> http_errors
371
+ if (isset($options['exceptions'])) {
372
+ $options['http_errors'] = $options['exceptions'];
373
+ unset($options['exceptions']);
374
+ }
375
+
376
+ $request = $this->applyOptions($request, $options);
377
+ /** @var HandlerStack $handler */
378
+ $handler = $options['handler'];
379
+
380
+ try {
381
+ return Promise\promise_for($handler($request, $options));
382
+ } catch (\Exception $e) {
383
+ return Promise\rejection_for($e);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Applies the array of request options to a request.
389
+ *
390
+ * @param RequestInterface $request
391
+ * @param array $options
392
+ *
393
+ * @return RequestInterface
394
+ */
395
+ private function applyOptions(RequestInterface $request, array &$options)
396
+ {
397
+ $modify = [
398
+ 'set_headers' => [],
399
+ ];
400
+
401
+ if (isset($options['headers'])) {
402
+ $modify['set_headers'] = $options['headers'];
403
+ unset($options['headers']);
404
+ }
405
+
406
+ if (isset($options['form_params'])) {
407
+ if (isset($options['multipart'])) {
408
+ throw new \InvalidArgumentException('You cannot use '
409
+ . 'form_params and multipart at the same time. Use the '
410
+ . 'form_params option if you want to send application/'
411
+ . 'x-www-form-urlencoded requests, and the multipart '
412
+ . 'option to send multipart/form-data requests.');
413
+ }
414
+ $options['body'] = http_build_query($options['form_params'], '', '&');
415
+ unset($options['form_params']);
416
+ // Ensure that we don't have the header in different case and set the new value.
417
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
418
+ $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
419
+ }
420
+
421
+ if (isset($options['multipart'])) {
422
+ $options['body'] = new Psr7\MultipartStream($options['multipart']);
423
+ unset($options['multipart']);
424
+ }
425
+
426
+ if (isset($options['json'])) {
427
+ $options['body'] = \GuzzleHttp\json_encode($options['json']);
428
+ unset($options['json']);
429
+ // Ensure that we don't have the header in different case and set the new value.
430
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
431
+ $options['_conditional']['Content-Type'] = 'application/json';
432
+ }
433
+
434
+ if (!empty($options['decode_content'])
435
+ && $options['decode_content'] !== true
436
+ ) {
437
+ // Ensure that we don't have the header in different case and set the new value.
438
+ $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
439
+ $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
440
+ }
441
+
442
+ if (isset($options['body'])) {
443
+ if (is_array($options['body'])) {
444
+ $this->invalidBody();
445
+ }
446
+ $modify['body'] = Psr7\stream_for($options['body']);
447
+ unset($options['body']);
448
+ }
449
+
450
+ if (!empty($options['auth']) && is_array($options['auth'])) {
451
+ $value = $options['auth'];
452
+ $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
453
+ switch ($type) {
454
+ case 'basic':
455
+ // Ensure that we don't have the header in different case and set the new value.
456
+ $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
457
+ $modify['set_headers']['Authorization'] = 'Basic '
458
+ . base64_encode("$value[0]:$value[1]");
459
+ break;
460
+ case 'digest':
461
+ // @todo: Do not rely on curl
462
+ $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
463
+ $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
464
+ break;
465
+ case 'ntlm':
466
+ $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
467
+ $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
468
+ break;
469
+ }
470
+ }
471
+
472
+ if (isset($options['query'])) {
473
+ $value = $options['query'];
474
+ if (is_array($value)) {
475
+ $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
476
+ }
477
+ if (!is_string($value)) {
478
+ throw new \InvalidArgumentException('query must be a string or array');
479
+ }
480
+ $modify['query'] = $value;
481
+ unset($options['query']);
482
+ }
483
+
484
+ // Ensure that sink is not an invalid value.
485
+ if (isset($options['sink'])) {
486
+ // TODO: Add more sink validation?
487
+ if (is_bool($options['sink'])) {
488
+ throw new \InvalidArgumentException('sink must not be a boolean');
489
+ }
490
+ }
491
+
492
+ $request = Psr7\modify_request($request, $modify);
493
+ if ($request->getBody() instanceof Psr7\MultipartStream) {
494
+ // Use a multipart/form-data POST if a Content-Type is not set.
495
+ // Ensure that we don't have the header in different case and set the new value.
496
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
497
+ $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
498
+ . $request->getBody()->getBoundary();
499
+ }
500
+
501
+ // Merge in conditional headers if they are not present.
502
+ if (isset($options['_conditional'])) {
503
+ // Build up the changes so it's in a single clone of the message.
504
+ $modify = [];
505
+ foreach ($options['_conditional'] as $k => $v) {
506
+ if (!$request->hasHeader($k)) {
507
+ $modify['set_headers'][$k] = $v;
508
+ }
509
+ }
510
+ $request = Psr7\modify_request($request, $modify);
511
+ // Don't pass this internal value along to middleware/handlers.
512
+ unset($options['_conditional']);
513
+ }
514
+
515
+ return $request;
516
+ }
517
+
518
+ /**
519
+ * Throw Exception with pre-set message.
520
+ * @return void
521
+ * @throws InvalidArgumentException Invalid body.
522
+ */
523
+ private function invalidBody()
524
+ {
525
+ throw new \InvalidArgumentException('Passing in the "body" request '
526
+ . 'option as an array to send a POST request has been deprecated. '
527
+ . 'Please use the "form_params" request option to send a '
528
+ . 'application/x-www-form-urlencoded request, or the "multipart" '
529
+ . 'request option to send a multipart/form-data request.');
530
+ }
531
+ }
vendor/guzzlehttp/guzzle/src/ClientInterface.php CHANGED
@@ -1,84 +1,87 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Promise\PromiseInterface;
5
- use GuzzleHttp\Exception\GuzzleException;
6
- use Psr\Http\Message\RequestInterface;
7
- use Psr\Http\Message\ResponseInterface;
8
- use Psr\Http\Message\UriInterface;
9
-
10
- /**
11
- * Client interface for sending HTTP requests.
12
- */
13
- interface ClientInterface
14
- {
15
- const VERSION = '6.4.1';
16
-
17
- /**
18
- * Send an HTTP request.
19
- *
20
- * @param RequestInterface $request Request to send
21
- * @param array $options Request options to apply to the given
22
- * request and to the transfer.
23
- *
24
- * @return ResponseInterface
25
- * @throws GuzzleException
26
- */
27
- public function send(RequestInterface $request, array $options = []);
28
-
29
- /**
30
- * Asynchronously send an HTTP request.
31
- *
32
- * @param RequestInterface $request Request to send
33
- * @param array $options Request options to apply to the given
34
- * request and to the transfer.
35
- *
36
- * @return PromiseInterface
37
- */
38
- public function sendAsync(RequestInterface $request, array $options = []);
39
-
40
- /**
41
- * Create and send an HTTP request.
42
- *
43
- * Use an absolute path to override the base path of the client, or a
44
- * relative path to append to the base path of the client. The URL can
45
- * contain the query string as well.
46
- *
47
- * @param string $method HTTP method.
48
- * @param string|UriInterface $uri URI object or string.
49
- * @param array $options Request options to apply.
50
- *
51
- * @return ResponseInterface
52
- * @throws GuzzleException
53
- */
54
- public function request($method, $uri, array $options = []);
55
-
56
- /**
57
- * Create and send an asynchronous HTTP request.
58
- *
59
- * Use an absolute path to override the base path of the client, or a
60
- * relative path to append to the base path of the client. The URL can
61
- * contain the query string as well. Use an array to provide a URL
62
- * template and additional variables to use in the URL template expansion.
63
- *
64
- * @param string $method HTTP method
65
- * @param string|UriInterface $uri URI object or string.
66
- * @param array $options Request options to apply.
67
- *
68
- * @return PromiseInterface
69
- */
70
- public function requestAsync($method, $uri, array $options = []);
71
-
72
- /**
73
- * Get a client configuration option.
74
- *
75
- * These options include default request options of the client, a "handler"
76
- * (if utilized by the concrete client), and a "base_uri" if utilized by
77
- * the concrete client.
78
- *
79
- * @param string|null $option The config option to retrieve.
80
- *
81
- * @return mixed
82
- */
83
- public function getConfig($option = null);
84
- }
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Exception\GuzzleException;
5
+ use GuzzleHttp\Promise\PromiseInterface;
6
+ use Psr\Http\Message\RequestInterface;
7
+ use Psr\Http\Message\ResponseInterface;
8
+ use Psr\Http\Message\UriInterface;
9
+
10
+ /**
11
+ * Client interface for sending HTTP requests.
12
+ */
13
+ interface ClientInterface
14
+ {
15
+ /**
16
+ * @deprecated Will be removed in Guzzle 7.0.0
17
+ */
18
+ const VERSION = '6.5.0';
19
+
20
+ /**
21
+ * Send an HTTP request.
22
+ *
23
+ * @param RequestInterface $request Request to send
24
+ * @param array $options Request options to apply to the given
25
+ * request and to the transfer.
26
+ *
27
+ * @return ResponseInterface
28
+ * @throws GuzzleException
29
+ */
30
+ public function send(RequestInterface $request, array $options = []);
31
+
32
+ /**
33
+ * Asynchronously send an HTTP request.
34
+ *
35
+ * @param RequestInterface $request Request to send
36
+ * @param array $options Request options to apply to the given
37
+ * request and to the transfer.
38
+ *
39
+ * @return PromiseInterface
40
+ */
41
+ public function sendAsync(RequestInterface $request, array $options = []);
42
+
43
+ /**
44
+ * Create and send an HTTP request.
45
+ *
46
+ * Use an absolute path to override the base path of the client, or a
47
+ * relative path to append to the base path of the client. The URL can
48
+ * contain the query string as well.
49
+ *
50
+ * @param string $method HTTP method.
51
+ * @param string|UriInterface $uri URI object or string.
52
+ * @param array $options Request options to apply.
53
+ *
54
+ * @return ResponseInterface
55
+ * @throws GuzzleException
56
+ */
57
+ public function request($method, $uri, array $options = []);
58
+
59
+ /**
60
+ * Create and send an asynchronous HTTP request.
61
+ *
62
+ * Use an absolute path to override the base path of the client, or a
63
+ * relative path to append to the base path of the client. The URL can
64
+ * contain the query string as well. Use an array to provide a URL
65
+ * template and additional variables to use in the URL template expansion.
66
+ *
67
+ * @param string $method HTTP method
68
+ * @param string|UriInterface $uri URI object or string.
69
+ * @param array $options Request options to apply.
70
+ *
71
+ * @return PromiseInterface
72
+ */
73
+ public function requestAsync($method, $uri, array $options = []);
74
+
75
+ /**
76
+ * Get a client configuration option.
77
+ *
78
+ * These options include default request options of the client, a "handler"
79
+ * (if utilized by the concrete client), and a "base_uri" if utilized by
80
+ * the concrete client.
81
+ *
82
+ * @param string|null $option The config option to retrieve.
83
+ *
84
+ * @return mixed
85
+ */
86
+ public function getConfig($option = null);
87
+ }
vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php CHANGED
@@ -1,314 +1,316 @@
1
- <?php
2
- namespace GuzzleHttp\Cookie;
3
-
4
- use Psr\Http\Message\RequestInterface;
5
- use Psr\Http\Message\ResponseInterface;
6
-
7
- /**
8
- * Cookie jar that stores cookies as an array
9
- */
10
- class CookieJar implements CookieJarInterface
11
- {
12
- /** @var SetCookie[] Loaded cookie data */
13
- private $cookies = [];
14
-
15
- /** @var bool */
16
- private $strictMode;
17
-
18
- /**
19
- * @param bool $strictMode Set to true to throw exceptions when invalid
20
- * cookies are added to the cookie jar.
21
- * @param array $cookieArray Array of SetCookie objects or a hash of
22
- * arrays that can be used with the SetCookie
23
- * constructor
24
- */
25
- public function __construct($strictMode = false, $cookieArray = [])
26
- {
27
- $this->strictMode = $strictMode;
28
-
29
- foreach ($cookieArray as $cookie) {
30
- if (!($cookie instanceof SetCookie)) {
31
- $cookie = new SetCookie($cookie);
32
- }
33
- $this->setCookie($cookie);
34
- }
35
- }
36
-
37
- /**
38
- * Create a new Cookie jar from an associative array and domain.
39
- *
40
- * @param array $cookies Cookies to create the jar from
41
- * @param string $domain Domain to set the cookies to
42
- *
43
- * @return self
44
- */
45
- public static function fromArray(array $cookies, $domain)
46
- {
47
- $cookieJar = new self();
48
- foreach ($cookies as $name => $value) {
49
- $cookieJar->setCookie(new SetCookie([
50
- 'Domain' => $domain,
51
- 'Name' => $name,
52
- 'Value' => $value,
53
- 'Discard' => true
54
- ]));
55
- }
56
-
57
- return $cookieJar;
58
- }
59
-
60
- /**
61
- * @deprecated
62
- */
63
- public static function getCookieValue($value)
64
- {
65
- return $value;
66
- }
67
-
68
- /**
69
- * Evaluate if this cookie should be persisted to storage
70
- * that survives between requests.
71
- *
72
- * @param SetCookie $cookie Being evaluated.
73
- * @param bool $allowSessionCookies If we should persist session cookies
74
- * @return bool
75
- */
76
- public static function shouldPersist(
77
- SetCookie $cookie,
78
- $allowSessionCookies = false
79
- ) {
80
- if ($cookie->getExpires() || $allowSessionCookies) {
81
- if (!$cookie->getDiscard()) {
82
- return true;
83
- }
84
- }
85
-
86
- return false;
87
- }
88
-
89
- /**
90
- * Finds and returns the cookie based on the name
91
- *
92
- * @param string $name cookie name to search for
93
- * @return SetCookie|null cookie that was found or null if not found
94
- */
95
- public function getCookieByName($name)
96
- {
97
- // don't allow a null name
98
- if ($name === null) {
99
- return null;
100
- }
101
- foreach ($this->cookies as $cookie) {
102
- if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
103
- return $cookie;
104
- }
105
- }
106
- }
107
-
108
- public function toArray()
109
- {
110
- return array_map(function (SetCookie $cookie) {
111
- return $cookie->toArray();
112
- }, $this->getIterator()->getArrayCopy());
113
- }
114
-
115
- public function clear($domain = null, $path = null, $name = null)
116
- {
117
- if (!$domain) {
118
- $this->cookies = [];
119
- return;
120
- } elseif (!$path) {
121
- $this->cookies = array_filter(
122
- $this->cookies,
123
- function (SetCookie $cookie) use ($domain) {
124
- return !$cookie->matchesDomain($domain);
125
- }
126
- );
127
- } elseif (!$name) {
128
- $this->cookies = array_filter(
129
- $this->cookies,
130
- function (SetCookie $cookie) use ($path, $domain) {
131
- return !($cookie->matchesPath($path) &&
132
- $cookie->matchesDomain($domain));
133
- }
134
- );
135
- } else {
136
- $this->cookies = array_filter(
137
- $this->cookies,
138
- function (SetCookie $cookie) use ($path, $domain, $name) {
139
- return !($cookie->getName() == $name &&
140
- $cookie->matchesPath($path) &&
141
- $cookie->matchesDomain($domain));
142
- }
143
- );
144
- }
145
- }
146
-
147
- public function clearSessionCookies()
148
- {
149
- $this->cookies = array_filter(
150
- $this->cookies,
151
- function (SetCookie $cookie) {
152
- return !$cookie->getDiscard() && $cookie->getExpires();
153
- }
154
- );
155
- }
156
-
157
- public function setCookie(SetCookie $cookie)
158
- {
159
- // If the name string is empty (but not 0), ignore the set-cookie
160
- // string entirely.
161
- $name = $cookie->getName();
162
- if (!$name && $name !== '0') {
163
- return false;
164
- }
165
-
166
- // Only allow cookies with set and valid domain, name, value
167
- $result = $cookie->validate();
168
- if ($result !== true) {
169
- if ($this->strictMode) {
170
- throw new \RuntimeException('Invalid cookie: ' . $result);
171
- } else {
172
- $this->removeCookieIfEmpty($cookie);
173
- return false;
174
- }
175
- }
176
-
177
- // Resolve conflicts with previously set cookies
178
- foreach ($this->cookies as $i => $c) {
179
-
180
- // Two cookies are identical, when their path, and domain are
181
- // identical.
182
- if ($c->getPath() != $cookie->getPath() ||
183
- $c->getDomain() != $cookie->getDomain() ||
184
- $c->getName() != $cookie->getName()
185
- ) {
186
- continue;
187
- }
188
-
189
- // The previously set cookie is a discard cookie and this one is
190
- // not so allow the new cookie to be set
191
- if (!$cookie->getDiscard() && $c->getDiscard()) {
192
- unset($this->cookies[$i]);
193
- continue;
194
- }
195
-
196
- // If the new cookie's expiration is further into the future, then
197
- // replace the old cookie
198
- if ($cookie->getExpires() > $c->getExpires()) {
199
- unset($this->cookies[$i]);
200
- continue;
201
- }
202
-
203
- // If the value has changed, we better change it
204
- if ($cookie->getValue() !== $c->getValue()) {
205
- unset($this->cookies[$i]);
206
- continue;
207
- }
208
-
209
- // The cookie exists, so no need to continue
210
- return false;
211
- }
212
-
213
- $this->cookies[] = $cookie;
214
-
215
- return true;
216
- }
217
-
218
- public function count()
219
- {
220
- return count($this->cookies);
221
- }
222
-
223
- public function getIterator()
224
- {
225
- return new \ArrayIterator(array_values($this->cookies));
226
- }
227
-
228
- public function extractCookies(
229
- RequestInterface $request,
230
- ResponseInterface $response
231
- ) {
232
- if ($cookieHeader = $response->getHeader('Set-Cookie')) {
233
- foreach ($cookieHeader as $cookie) {
234
- $sc = SetCookie::fromString($cookie);
235
- if (!$sc->getDomain()) {
236
- $sc->setDomain($request->getUri()->getHost());
237
- }
238
- if (0 !== strpos($sc->getPath(), '/')) {
239
- $sc->setPath($this->getCookiePathFromRequest($request));
240
- }
241
- $this->setCookie($sc);
242
- }
243
- }
244
- }
245
-
246
- /**
247
- * Computes cookie path following RFC 6265 section 5.1.4
248
- *
249
- * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
250
- *
251
- * @param RequestInterface $request
252
- * @return string
253
- */
254
- private function getCookiePathFromRequest(RequestInterface $request)
255
- {
256
- $uriPath = $request->getUri()->getPath();
257
- if ('' === $uriPath) {
258
- return '/';
259
- }
260
- if (0 !== strpos($uriPath, '/')) {
261
- return '/';
262
- }
263
- if ('/' === $uriPath) {
264
- return '/';
265
- }
266
- if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
267
- return '/';
268
- }
269
-
270
- return substr($uriPath, 0, $lastSlashPos);
271
- }
272
-
273
- public function withCookieHeader(RequestInterface $request)
274
- {
275
- $values = [];
276
- $uri = $request->getUri();
277
- $scheme = $uri->getScheme();
278
- $host = $uri->getHost();
279
- $path = $uri->getPath() ?: '/';
280
-
281
- foreach ($this->cookies as $cookie) {
282
- if ($cookie->matchesPath($path) &&
283
- $cookie->matchesDomain($host) &&
284
- !$cookie->isExpired() &&
285
- (!$cookie->getSecure() || $scheme === 'https')
286
- ) {
287
- $values[] = $cookie->getName() . '='
288
- . $cookie->getValue();
289
- }
290
- }
291
-
292
- return $values
293
- ? $request->withHeader('Cookie', implode('; ', $values))
294
- : $request;
295
- }
296
-
297
- /**
298
- * If a cookie already exists and the server asks to set it again with a
299
- * null value, the cookie must be deleted.
300
- *
301
- * @param SetCookie $cookie
302
- */
303
- private function removeCookieIfEmpty(SetCookie $cookie)
304
- {
305
- $cookieValue = $cookie->getValue();
306
- if ($cookieValue === null || $cookieValue === '') {
307
- $this->clear(
308
- $cookie->getDomain(),
309
- $cookie->getPath(),
310
- $cookie->getName()
311
- );
312
- }
313
- }
314
- }
 
 
1
+ <?php
2
+ namespace GuzzleHttp\Cookie;
3
+
4
+ use Psr\Http\Message\RequestInterface;
5
+ use Psr\Http\Message\ResponseInterface;
6
+
7
+ /**
8
+ * Cookie jar that stores cookies as an array
9
+ */
10
+ class CookieJar implements CookieJarInterface
11
+ {
12
+ /** @var SetCookie[] Loaded cookie data */
13
+ private $cookies = [];
14
+
15
+ /** @var bool */
16
+ private $strictMode;
17
+
18
+ /**
19
+ * @param bool $strictMode Set to true to throw exceptions when invalid
20
+ * cookies are added to the cookie jar.
21
+ * @param array $cookieArray Array of SetCookie objects or a hash of
22
+ * arrays that can be used with the SetCookie
23
+ * constructor
24
+ */
25
+ public function __construct($strictMode = false, $cookieArray = [])
26
+ {
27
+ $this->strictMode = $strictMode;
28
+
29
+ foreach ($cookieArray as $cookie) {
30
+ if (!($cookie instanceof SetCookie)) {
31
+ $cookie = new SetCookie($cookie);
32
+ }
33
+ $this->setCookie($cookie);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Create a new Cookie jar from an associative array and domain.
39
+ *
40
+ * @param array $cookies Cookies to create the jar from
41
+ * @param string $domain Domain to set the cookies to
42
+ *
43
+ * @return self
44
+ */
45
+ public static function fromArray(array $cookies, $domain)
46
+ {
47
+ $cookieJar = new self();
48
+ foreach ($cookies as $name => $value) {
49
+ $cookieJar->setCookie(new SetCookie([
50
+ 'Domain' => $domain,
51
+ 'Name' => $name,
52
+ 'Value' => $value,
53
+ 'Discard' => true
54
+ ]));
55
+ }
56
+
57
+ return $cookieJar;
58
+ }
59
+
60
+ /**
61
+ * @deprecated
62
+ */
63
+ public static function getCookieValue($value)
64
+ {
65
+ return $value;
66
+ }
67
+
68
+ /**
69
+ * Evaluate if this cookie should be persisted to storage
70
+ * that survives between requests.
71
+ *
72
+ * @param SetCookie $cookie Being evaluated.
73
+ * @param bool $allowSessionCookies If we should persist session cookies
74
+ * @return bool
75
+ */
76
+ public static function shouldPersist(
77
+ SetCookie $cookie,
78
+ $allowSessionCookies = false
79
+ ) {
80
+ if ($cookie->getExpires() || $allowSessionCookies) {
81
+ if (!$cookie->getDiscard()) {
82
+ return true;
83
+ }
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Finds and returns the cookie based on the name
91
+ *
92
+ * @param string $name cookie name to search for
93
+ * @return SetCookie|null cookie that was found or null if not found
94
+ */
95
+ public function getCookieByName($name)
96
+ {
97
+ // don't allow a non string name
98
+ if ($name === null || !is_scalar($name)) {
99
+ return null;
100
+ }
101
+ foreach ($this->cookies as $cookie) {
102
+ if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
103
+ return $cookie;
104
+ }
105
+ }
106
+
107
+ return null;
108
+ }
109
+
110
+ public function toArray()
111
+ {
112
+ return array_map(function (SetCookie $cookie) {
113
+ return $cookie->toArray();
114
+ }, $this->getIterator()->getArrayCopy());
115
+ }
116
+
117
+ public function clear($domain = null, $path = null, $name = null)
118
+ {
119
+ if (!$domain) {
120
+ $this->cookies = [];
121
+ return;
122
+ } elseif (!$path) {
123
+ $this->cookies = array_filter(
124
+ $this->cookies,
125
+ function (SetCookie $cookie) use ($domain) {
126
+ return !$cookie->matchesDomain($domain);
127
+ }
128
+ );
129
+ } elseif (!$name) {
130
+ $this->cookies = array_filter(
131
+ $this->cookies,
132
+ function (SetCookie $cookie) use ($path, $domain) {
133
+ return !($cookie->matchesPath($path) &&
134
+ $cookie->matchesDomain($domain));
135
+ }
136
+ );
137
+ } else {
138
+ $this->cookies = array_filter(
139
+ $this->cookies,
140
+ function (SetCookie $cookie) use ($path, $domain, $name) {
141
+ return !($cookie->getName() == $name &&
142
+ $cookie->matchesPath($path) &&
143
+ $cookie->matchesDomain($domain));
144
+ }
145
+ );
146
+ }
147
+ }
148
+
149
+ public function clearSessionCookies()
150
+ {
151
+ $this->cookies = array_filter(
152
+ $this->cookies,
153
+ function (SetCookie $cookie) {
154
+ return !$cookie->getDiscard() && $cookie->getExpires();
155
+ }
156
+ );
157
+ }
158
+
159
+ public function setCookie(SetCookie $cookie)
160
+ {
161
+ // If the name string is empty (but not 0), ignore the set-cookie
162
+ // string entirely.
163
+ $name = $cookie->getName();
164
+ if (!$name && $name !== '0') {
165
+ return false;
166
+ }
167
+
168
+ // Only allow cookies with set and valid domain, name, value
169
+ $result = $cookie->validate();
170
+ if ($result !== true) {
171
+ if ($this->strictMode) {
172
+ throw new \RuntimeException('Invalid cookie: ' . $result);
173
+ } else {
174
+ $this->removeCookieIfEmpty($cookie);
175
+ return false;
176
+ }
177
+ }
178
+
179
+ // Resolve conflicts with previously set cookies
180
+ foreach ($this->cookies as $i => $c) {
181
+
182
+ // Two cookies are identical, when their path, and domain are
183
+ // identical.
184
+ if ($c->getPath() != $cookie->getPath() ||
185
+ $c->getDomain() != $cookie->getDomain() ||
186
+ $c->getName() != $cookie->getName()
187
+ ) {
188
+ continue;
189
+ }
190
+
191
+ // The previously set cookie is a discard cookie and this one is
192
+ // not so allow the new cookie to be set
193
+ if (!$cookie->getDiscard() && $c->getDiscard()) {
194
+ unset($this->cookies[$i]);
195
+ continue;
196
+ }
197
+
198
+ // If the new cookie's expiration is further into the future, then
199
+ // replace the old cookie
200
+ if ($cookie->getExpires() > $c->getExpires()) {
201
+ unset($this->cookies[$i]);
202
+ continue;
203
+ }
204
+
205
+ // If the value has changed, we better change it
206
+ if ($cookie->getValue() !== $c->getValue()) {
207
+ unset($this->cookies[$i]);
208
+ continue;
209
+ }
210
+
211
+ // The cookie exists, so no need to continue
212
+ return false;
213
+ }
214
+
215
+ $this->cookies[] = $cookie;
216
+
217
+ return true;
218
+ }
219
+
220
+ public function count()
221
+ {
222
+ return count($this->cookies);
223
+ }
224
+
225
+ public function getIterator()
226
+ {
227
+ return new \ArrayIterator(array_values($this->cookies));
228
+ }
229
+
230
+ public function extractCookies(
231
+ RequestInterface $request,
232
+ ResponseInterface $response
233
+ ) {
234
+ if ($cookieHeader = $response->getHeader('Set-Cookie')) {
235
+ foreach ($cookieHeader as $cookie) {
236
+ $sc = SetCookie::fromString($cookie);
237
+ if (!$sc->getDomain()) {
238
+ $sc->setDomain($request->getUri()->getHost());
239
+ }
240
+ if (0 !== strpos($sc->getPath(), '/')) {
241
+ $sc->setPath($this->getCookiePathFromRequest($request));
242
+ }
243
+ $this->setCookie($sc);
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Computes cookie path following RFC 6265 section 5.1.4
250
+ *
251
+ * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
252
+ *
253
+ * @param RequestInterface $request
254
+ * @return string
255
+ */
256
+ private function getCookiePathFromRequest(RequestInterface $request)
257
+ {
258
+ $uriPath = $request->getUri()->getPath();
259
+ if ('' === $uriPath) {
260
+ return '/';
261
+ }
262
+ if (0 !== strpos($uriPath, '/')) {
263
+ return '/';
264
+ }
265
+ if ('/' === $uriPath) {
266
+ return '/';
267
+ }
268
+ if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
269
+ return '/';
270
+ }
271
+
272
+ return substr($uriPath, 0, $lastSlashPos);
273
+ }
274
+
275
+ public function withCookieHeader(RequestInterface $request)
276
+ {
277
+ $values = [];
278
+ $uri = $request->getUri();
279
+ $scheme = $uri->getScheme();
280
+ $host = $uri->getHost();
281
+ $path = $uri->getPath() ?: '/';
282
+
283
+ foreach ($this->cookies as $cookie) {
284
+ if ($cookie->matchesPath($path) &&
285
+ $cookie->matchesDomain($host) &&
286
+ !$cookie->isExpired() &&
287
+ (!$cookie->getSecure() || $scheme === 'https')
288
+ ) {
289
+ $values[] = $cookie->getName() . '='
290
+ . $cookie->getValue();
291
+ }
292
+ }
293
+
294
+ return $values
295
+ ? $request->withHeader('Cookie', implode('; ', $values))
296
+ : $request;
297
+ }
298
+
299
+ /**
300
+ * If a cookie already exists and the server asks to set it again with a
301
+ * null value, the cookie must be deleted.
302
+ *
303
+ * @param SetCookie $cookie
304
+ */
305
+ private function removeCookieIfEmpty(SetCookie $cookie)
306
+ {
307
+ $cookieValue = $cookie->getValue();
308
+ if ($cookieValue === null || $cookieValue === '') {
309
+ $this->clear(
310
+ $cookie->getDomain(),
311
+ $cookie->getPath(),
312
+ $cookie->getName()
313
+ );
314
+ }
315
+ }
316
+ }
vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php CHANGED
@@ -1,84 +1,84 @@
1
- <?php
2
- namespace GuzzleHttp\Cookie;
3
-
4
- use Psr\Http\Message\RequestInterface;
5
- use Psr\Http\Message\ResponseInterface;
6
-
7
- /**
8
- * Stores HTTP cookies.
9
- *
10
- * It extracts cookies from HTTP requests, and returns them in HTTP responses.
11
- * CookieJarInterface instances automatically expire contained cookies when
12
- * necessary. Subclasses are also responsible for storing and retrieving
13
- * cookies from a file, database, etc.
14
- *
15
- * @link http://docs.python.org/2/library/cookielib.html Inspiration
16
- */
17
- interface CookieJarInterface extends \Countable, \IteratorAggregate
18
- {
19
- /**
20
- * Create a request with added cookie headers.
21
- *
22
- * If no matching cookies are found in the cookie jar, then no Cookie
23
- * header is added to the request and the same request is returned.
24
- *
25
- * @param RequestInterface $request Request object to modify.
26
- *
27
- * @return RequestInterface returns the modified request.
28
- */
29
- public function withCookieHeader(RequestInterface $request);
30
-
31
- /**
32
- * Extract cookies from an HTTP response and store them in the CookieJar.
33
- *
34
- * @param RequestInterface $request Request that was sent
35
- * @param ResponseInterface $response Response that was received
36
- */
37
- public function extractCookies(
38
- RequestInterface $request,
39
- ResponseInterface $response
40
- );
41
-
42
- /**
43
- * Sets a cookie in the cookie jar.
44
- *
45
- * @param SetCookie $cookie Cookie to set.
46
- *
47
- * @return bool Returns true on success or false on failure
48
- */
49
- public function setCookie(SetCookie $cookie);
50
-
51
- /**
52
- * Remove cookies currently held in the cookie jar.
53
- *
54
- * Invoking this method without arguments will empty the whole cookie jar.
55
- * If given a $domain argument only cookies belonging to that domain will
56
- * be removed. If given a $domain and $path argument, cookies belonging to
57
- * the specified path within that domain are removed. If given all three
58
- * arguments, then the cookie with the specified name, path and domain is
59
- * removed.
60
- *
61
- * @param string $domain Clears cookies matching a domain
62
- * @param string $path Clears cookies matching a domain and path
63
- * @param string $name Clears cookies matching a domain, path, and name
64
- *
65
- * @return CookieJarInterface
66
- */
67
- public function clear($domain = null, $path = null, $name = null);
68
-
69
- /**
70
- * Discard all sessions cookies.
71
- *
72
- * Removes cookies that don't have an expire field or a have a discard
73
- * field set to true. To be called when the user agent shuts down according
74
- * to RFC 2965.
75
- */
76
- public function clearSessionCookies();
77
-
78
- /**
79
- * Converts the cookie jar to an array.
80
- *
81
- * @return array
82
- */
83
- public function toArray();
84
- }
1
+ <?php
2
+ namespace GuzzleHttp\Cookie;
3
+
4
+ use Psr\Http\Message\RequestInterface;
5
+ use Psr\Http\Message\ResponseInterface;
6
+
7
+ /**
8
+ * Stores HTTP cookies.
9
+ *
10
+ * It extracts cookies from HTTP requests, and returns them in HTTP responses.
11
+ * CookieJarInterface instances automatically expire contained cookies when
12
+ * necessary. Subclasses are also responsible for storing and retrieving
13
+ * cookies from a file, database, etc.
14
+ *
15
+ * @link http://docs.python.org/2/library/cookielib.html Inspiration
16
+ */
17
+ interface CookieJarInterface extends \Countable, \IteratorAggregate
18
+ {
19
+ /**
20
+ * Create a request with added cookie headers.
21
+ *
22
+ * If no matching cookies are found in the cookie jar, then no Cookie
23
+ * header is added to the request and the same request is returned.
24
+ *
25
+ * @param RequestInterface $request Request object to modify.
26
+ *
27
+ * @return RequestInterface returns the modified request.
28
+ */
29
+ public function withCookieHeader(RequestInterface $request);
30
+
31
+ /**
32
+ * Extract cookies from an HTTP response and store them in the CookieJar.
33
+ *
34
+ * @param RequestInterface $request Request that was sent
35
+ * @param ResponseInterface $response Response that was received
36
+ */
37
+ public function extractCookies(
38
+ RequestInterface $request,
39
+ ResponseInterface $response
40
+ );
41
+
42
+ /**
43
+ * Sets a cookie in the cookie jar.
44
+ *
45
+ * @param SetCookie $cookie Cookie to set.
46
+ *
47
+ * @return bool Returns true on success or false on failure
48
+ */
49
+ public function setCookie(SetCookie $cookie);
50
+
51
+ /**
52
+ * Remove cookies currently held in the cookie jar.
53
+ *
54
+ * Invoking this method without arguments will empty the whole cookie jar.
55
+ * If given a $domain argument only cookies belonging to that domain will
56
+ * be removed. If given a $domain and $path argument, cookies belonging to
57
+ * the specified path within that domain are removed. If given all three
58
+ * arguments, then the cookie with the specified name, path and domain is
59
+ * removed.
60
+ *
61
+ * @param string|null $domain Clears cookies matching a domain
62
+ * @param string|null $path Clears cookies matching a domain and path
63
+ * @param string|null $name Clears cookies matching a domain, path, and name
64
+ *
65
+ * @return CookieJarInterface
66
+ */
67
+ public function clear($domain = null, $path = null, $name = null);
68
+
69
+ /**
70
+ * Discard all sessions cookies.
71
+ *
72
+ * Removes cookies that don't have an expire field or a have a discard
73
+ * field set to true. To be called when the user agent shuts down according
74
+ * to RFC 2965.
75
+ */
76
+ public function clearSessionCookies();
77
+
78
+ /**
79
+ * Converts the cookie jar to an array.
80
+ *
81
+ * @return array
82
+ */
83
+ public function toArray();
84
+ }
vendor/guzzlehttp/guzzle/src/Exception/RequestException.php CHANGED
@@ -1,217 +1,192 @@
1
- <?php
2
- namespace GuzzleHttp\Exception;
3
-
4
- use Psr\Http\Message\RequestInterface;
5
- use Psr\Http\Message\ResponseInterface;
6
- use GuzzleHttp\Promise\PromiseInterface;
7
- use Psr\Http\Message\UriInterface;
8
-
9
- /**
10
- * HTTP Request exception
11
- */
12
- class RequestException extends TransferException
13
- {
14
- /** @var RequestInterface */
15
- private $request;
16
-
17
- /** @var ResponseInterface */
18
- private $response;
19
-
20
- /** @var array */
21
- private $handlerContext;
22
-
23
- public function __construct(
24
- $message,
25
- RequestInterface $request,
26
- ResponseInterface $response = null,
27
- \Exception $previous = null,
28
- array $handlerContext = []
29
- ) {
30
- // Set the code of the exception if the response is set and not future.
31
- $code = $response && !($response instanceof PromiseInterface)
32
- ? $response->getStatusCode()
33
- : 0;
34
- parent::__construct($message, $code, $previous);
35
- $this->request = $request;
36
- $this->response = $response;
37
- $this->handlerContext = $handlerContext;
38
- }
39
-
40
- /**
41
- * Wrap non-RequestExceptions with a RequestException
42
- *
43
- * @param RequestInterface $request
44
- * @param \Exception $e
45
- *
46
- * @return RequestException
47
- */
48
- public static function wrapException(RequestInterface $request, \Exception $e)
49
- {
50
- return $e instanceof RequestException
51
- ? $e
52
- : new RequestException($e->getMessage(), $request, null, $e);
53
- }
54
-
55
- /**
56
- * Factory method to create a new exception with a normalized error message
57
- *
58
- * @param RequestInterface $request Request
59
- * @param ResponseInterface $response Response received
60
- * @param \Exception $previous Previous exception
61
- * @param array $ctx Optional handler context.
62
- *
63
- * @return self
64
- */
65
- public static function create(
66
- RequestInterface $request,
67
- ResponseInterface $response = null,
68
- \Exception $previous = null,
69
- array $ctx = []
70
- ) {
71
- if (!$response) {
72
- return new self(
73
- 'Error completing request',
74
- $request,
75
- null,
76
- $previous,
77
- $ctx
78
- );
79
- }
80
-
81
- $level = (int) floor($response->getStatusCode() / 100);
82
- if ($level === 4) {
83
- $label = 'Client error';
84
- $className = ClientException::class;
85
- } elseif ($level === 5) {
86
- $label = 'Server error';
87
- $className = ServerException::class;
88
- } else {
89
- $label = 'Unsuccessful request';
90
- $className = __CLASS__;
91
- }
92
-
93
- $uri = $request->getUri();
94
- $uri = static::obfuscateUri($uri);
95
-
96
- // Client Error: `GET /` resulted in a `404 Not Found` response:
97
- // <html> ... (truncated)
98
- $message = sprintf(
99
- '%s: `%s %s` resulted in a `%s %s` response',
100
- $label,
101
- $request->getMethod(),
102
- $uri,
103
- $response->getStatusCode(),
104
- $response->getReasonPhrase()
105
- );
106
-
107
- $summary = static::getResponseBodySummary($response);
108
-
109
- if ($summary !== null) {
110
- $message .= ":\n{$summary}\n";
111
- }
112
-
113
- return new $className($message, $request, $response, $previous, $ctx);
114
- }
115
-
116
- /**
117
- * Get a short summary of the response
118
- *
119
- * Will return `null` if the response is not printable.
120
- *
121
- * @param ResponseInterface $response
122
- *
123
- * @return string|null
124
- */
125
- public static function getResponseBodySummary(ResponseInterface $response)
126
- {
127
- $body = $response->getBody();
128
-
129
- if (!$body->isSeekable() || !$body->isReadable()) {
130
- return null;
131
- }
132
-
133
- $size = $body->getSize();
134
-
135
- if ($size === 0) {
136
- return null;
137
- }
138
-
139
- $summary = $body->read(120);
140
- $body->rewind();
141
-
142
- if ($size > 120) {
143
- $summary .= ' (truncated...)';
144
- }
145
-
146
- // Matches any printable character, including unicode characters:
147
- // letters, marks, numbers, punctuation, spacing, and separators.
148
- if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
149
- return null;
150
- }
151
-
152
- return $summary;
153
- }
154
-
155
- /**
156
- * Obfuscates URI if there is an username and a password present
157
- *
158
- * @param UriInterface $uri
159
- *
160
- * @return UriInterface
161
- */
162
- private static function obfuscateUri($uri)
163
- {
164
- $userInfo = $uri->getUserInfo();
165
-
166
- if (false !== ($pos = strpos($userInfo, ':'))) {
167
- return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
168
- }
169
-
170
- return $uri;
171
- }
172
-
173
- /**
174
- * Get the request that caused the exception
175
- *
176
- * @return RequestInterface
177
- */
178
- public function getRequest()
179
- {
180
- return $this->request;
181
- }
182
-
183
- /**
184
- * Get the associated response
185
- *
186
- * @return ResponseInterface|null
187
- */
188
- public function getResponse()
189
- {
190
- return $this->response;
191
- }
192
-
193
- /**
194
- * Check if a response was received
195
- *
196
- * @return bool
197
- */
198
- public function hasResponse()
199
- {
200
- return $this->response !== null;
201
- }
202
-
203
- /**
204
- * Get contextual information about the error from the underlying handler.
205
- *
206
- * The contents of this array will vary depending on which handler you are
207
- * using. It may also be just an empty array. Relying on this data will
208
- * couple you to a specific handler, but can give more debug information
209
- * when needed.
210
- *
211
- * @return array
212
- */
213
- public function getHandlerContext()
214
- {
215
- return $this->handlerContext;
216
- }
217
- }
1
+ <?php
2
+ namespace GuzzleHttp\Exception;
3
+
4
+ use GuzzleHttp\Promise\PromiseInterface;
5
+ use Psr\Http\Message\RequestInterface;
6
+ use Psr\Http\Message\ResponseInterface;
7
+ use Psr\Http\Message\UriInterface;
8
+
9
+ /**
10
+ * HTTP Request exception
11
+ */
12
+ class RequestException extends TransferException
13
+ {
14
+ /** @var RequestInterface */
15
+ private $request;
16
+
17
+ /** @var ResponseInterface|null */
18
+ private $response;
19
+
20
+ /** @var array */
21
+ private $handlerContext;
22
+
23
+ public function __construct(
24
+ $message,
25
+ RequestInterface $request,
26
+ ResponseInterface $response = null,
27
+ \Exception $previous = null,
28
+ array $handlerContext = []
29
+ ) {
30
+ // Set the code of the exception if the response is set and not future.
31
+ $code = $response && !($response instanceof PromiseInterface)
32
+ ? $response->getStatusCode()
33
+ : 0;
34
+ parent::__construct($message, $code, $previous);
35
+ $this->request = $request;
36
+ $this->response = $response;
37
+ $this->handlerContext = $handlerContext;
38
+ }
39
+
40
+ /**
41
+ * Wrap non-RequestExceptions with a RequestException
42
+ *
43
+ * @param RequestInterface $request
44
+ * @param \Exception $e
45
+ *
46
+ * @return RequestException
47
+ */
48
+ public static function wrapException(RequestInterface $request, \Exception $e)
49
+ {
50
+ return $e instanceof RequestException
51
+ ? $e
52
+ : new RequestException($e->getMessage(), $request, null, $e);
53
+ }
54
+
55
+ /**
56
+ * Factory method to create a new exception with a normalized error message
57
+ *
58
+ * @param RequestInterface $request Request
59
+ * @param ResponseInterface $response Response received
60
+ * @param \Exception $previous Previous exception
61
+ * @param array $ctx Optional handler context.
62
+ *
63
+ * @return self
64
+ */
65
+ public static function create(
66
+ RequestInterface $request,
67
+ ResponseInterface $response = null,
68
+ \Exception $previous = null,
69
+ array $ctx = []
70
+ ) {
71
+ if (!$response) {
72
+ return new self(
73
+ 'Error completing request',
74
+ $request,
75
+ null,
76
+ $previous,
77
+ $ctx
78
+ );
79
+ }
80
+
81
+ $level = (int) floor($response->getStatusCode() / 100);
82
+ if ($level === 4) {
83
+ $label = 'Client error';
84
+ $className = ClientException::class;
85
+ } elseif ($level === 5) {
86
+ $label = 'Server error';
87
+ $className = ServerException::class;
88
+ } else {
89
+ $label = 'Unsuccessful request';
90
+ $className = __CLASS__;
91
+ }
92
+
93
+ $uri = $request->getUri();
94
+ $uri = static::obfuscateUri($uri);
95
+
96
+ // Client Error: `GET /` resulted in a `404 Not Found` response:
97
+ // <html> ... (truncated)
98
+ $message = sprintf(
99
+ '%s: `%s %s` resulted in a `%s %s` response',
100
+ $label,
101
+ $request->getMethod(),
102
+ $uri,
103
+ $response->getStatusCode(),
104
+ $response->getReasonPhrase()
105
+ );
106
+
107
+ $summary = static::getResponseBodySummary($response);
108
+
109
+ if ($summary !== null) {
110
+ $message .= ":\n{$summary}\n";
111
+ }
112
+
113
+ return new $className($message, $request, $response, $previous, $ctx);
114
+ }
115
+
116
+ /**
117
+ * Get a short summary of the response
118
+ *
119
+ * Will return `null` if the response is not printable.
120
+ *
121
+ * @param ResponseInterface $response
122
+ *
123
+ * @return string|null
124
+ */
125
+ public static function getResponseBodySummary(ResponseInterface $response)
126
+ {
127
+ return \GuzzleHttp\Psr7\get_message_body_summary($response);
128
+ }
129
+
130
+ /**
131
+ * Obfuscates URI if there is a username and a password present
132
+ *
133
+ * @param UriInterface $uri
134
+ *
135
+ * @return UriInterface
136
+ */
137
+ private static function obfuscateUri(UriInterface $uri)
138
+ {
139
+ $userInfo = $uri->getUserInfo();
140
+
141
+ if (false !== ($pos = strpos($userInfo, ':'))) {
142
+ return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
143
+ }
144
+
145
+ return $uri;
146
+ }
147
+
148
+ /**
149
+ * Get the request that caused the exception
150
+ *
151
+ * @return RequestInterface
152
+ */
153
+ public function getRequest()
154
+ {
155
+ return $this->request;
156
+ }
157
+
158
+ /**
159
+ * Get the associated response
160
+ *
161
+ * @return ResponseInterface|null
162
+ */
163
+ public function getResponse()
164
+ {
165
+ return $this->response;
166
+ }
167
+
168
+ /**
169
+ * Check if a response was received
170
+ *
171
+ * @return bool
172
+ */
173
+ public function hasResponse()
174
+ {
175
+ return $this->response !== null;
176
+ }
177
+
178
+ /**
179
+ * Get contextual information about the error from the underlying handler.
180
+ *
181
+ * The contents of this array will vary depending on which handler you are
182
+ * using. It may also be just an empty array. Relying on this data will
183
+ * couple you to a specific handler, but can give more debug information
184
+ * when needed.
185
+ *
186
+ * @return array
187
+ */
188
+ public function getHandlerContext()
189
+ {
190
+ return $this->handlerContext;
191
+ }
192
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php CHANGED
@@ -1,580 +1,585 @@
1
- <?php
2
- namespace GuzzleHttp\Handler;
3
-
4
- use GuzzleHttp\Exception\RequestException;
5
- use GuzzleHttp\Exception\ConnectException;
6
- use GuzzleHttp\Promise\FulfilledPromise;
7
- use GuzzleHttp\Psr7;
8
- use GuzzleHttp\Psr7\LazyOpenStream;
9
- use GuzzleHttp\TransferStats;
10
- use Psr\Http\Message\RequestInterface;
11
-
12
- /**
13
- * Creates curl resources from a request
14
- */
15
- class CurlFactory implements CurlFactoryInterface
16
- {
17
- const CURL_VERSION_STR = 'curl_version';
18
- const LOW_CURL_VERSION_NUMBER = '7.21.2';
19
-
20
- /** @var array */
21
- private $handles = [];
22
-
23
- /** @var int Total number of idle handles to keep in cache */
24
- private $maxHandles;
25
-
26
- /**
27
- * @param int $maxHandles Maximum number of idle handles.
28
- */
29
- public function __construct($maxHandles)
30
- {
31
- $this->maxHandles = $maxHandles;
32
- }
33
-
34
- public function create(RequestInterface $request, array $options)
35
- {
36
- if (isset($options['curl']['body_as_string'])) {
37
- $options['_body_as_string'] = $options['curl']['body_as_string'];
38
- unset($options['curl']['body_as_string']);
39
- }
40
-
41
- $easy = new EasyHandle;
42
- $easy->request = $request;
43
- $easy->options = $options;
44
- $conf = $this->getDefaultConf($easy);
45
- $this->applyMethod($easy, $conf);
46
- $this->applyHandlerOptions($easy, $conf);
47
- $this->applyHeaders($easy, $conf);
48
- unset($conf['_headers']);
49
-
50
- // Add handler options from the request configuration options
51
- if (isset($options['curl'])) {
52
- $conf = array_replace($conf, $options['curl']);
53
- }
54
-
55
- $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
56
- $easy->handle = $this->handles
57
- ? array_pop($this->handles)
58
- : curl_init();
59
- curl_setopt_array($easy->handle, $conf);
60
-
61
- return $easy;
62
- }
63
-
64
- public function release(EasyHandle $easy)
65
- {
66
- $resource = $easy->handle;
67
- unset($easy->handle);
68
-
69
- if (count($this->handles) >= $this->maxHandles) {
70
- curl_close($resource);
71
- } else {
72
- // Remove all callback functions as they can hold onto references
73
- // and are not cleaned up by curl_reset. Using curl_setopt_array
74
- // does not work for some reason, so removing each one
75
- // individually.
76
- curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
77
- curl_setopt($resource, CURLOPT_READFUNCTION, null);
78
- curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
79
- curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
80
- curl_reset($resource);
81
- $this->handles[] = $resource;
82
- }
83
- }
84
-
85
- /**
86
- * Completes a cURL transaction, either returning a response promise or a
87
- * rejected promise.
88
- *
89
- * @param callable $handler
90
- * @param EasyHandle $easy
91
- * @param CurlFactoryInterface $factory Dictates how the handle is released
92
- *
93
- * @return \GuzzleHttp\Promise\PromiseInterface
94
- */
95
- public static function finish(
96
- callable $handler,
97
- EasyHandle $easy,
98
- CurlFactoryInterface $factory
99
- ) {
100
- if (isset($easy->options['on_stats'])) {
101
- self::invokeStats($easy);
102
- }
103
-
104
- if (!$easy->response || $easy->errno) {
105
- return self::finishError($handler, $easy, $factory);
106
- }
107
-
108
- // Return the response if it is present and there is no error.
109
- $factory->release($easy);
110
-
111
- // Rewind the body of the response if possible.
112
- $body = $easy->response->getBody();
113
- if ($body->isSeekable()) {
114
- $body->rewind();
115
- }
116
-
117
- return new FulfilledPromise($easy->response);
118
- }
119
-
120
- private static function invokeStats(EasyHandle $easy)
121
- {
122
- $curlStats = curl_getinfo($easy->handle);
123
- $curlStats['appconnect_time'] = curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME);
124
- $stats = new TransferStats(
125
- $easy->request,
126
- $easy->response,
127
- $curlStats['total_time'],
128
- $easy->errno,
129
- $curlStats
130
- );
131
- call_user_func($easy->options['on_stats'], $stats);
132
- }
133
-
134
- private static function finishError(
135
- callable $handler,
136
- EasyHandle $easy,
137
- CurlFactoryInterface $factory
138
- ) {
139
- // Get error information and release the handle to the factory.
140
- $ctx = [
141
- 'errno' => $easy->errno,
142
- 'error' => curl_error($easy->handle),
143
- 'appconnect_time' => curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME),
144
- ] + curl_getinfo($easy->handle);
145
- $ctx[self::CURL_VERSION_STR] = curl_version()['version'];
146
- $factory->release($easy);
147
-
148
- // Retry when nothing is present or when curl failed to rewind.
149
- if (empty($easy->options['_err_message'])
150
- && (!$easy->errno || $easy->errno == 65)
151
- ) {
152
- return self::retryFailedRewind($handler, $easy, $ctx);
153
- }
154
-
155
- return self::createRejection($easy, $ctx);
156
- }
157
-
158
- private static function createRejection(EasyHandle $easy, array $ctx)
159
- {
160
- static $connectionErrors = [
161
- CURLE_OPERATION_TIMEOUTED => true,
162
- CURLE_COULDNT_RESOLVE_HOST => true,
163
- CURLE_COULDNT_CONNECT => true,
164
- CURLE_SSL_CONNECT_ERROR => true,
165
- CURLE_GOT_NOTHING => true,
166
- ];
167
-
168
- // If an exception was encountered during the onHeaders event, then
169
- // return a rejected promise that wraps that exception.
170
- if ($easy->onHeadersException) {
171
- return \GuzzleHttp\Promise\rejection_for(
172
- new RequestException(
173
- 'An error was encountered during the on_headers event',
174
- $easy->request,
175
- $easy->response,
176
- $easy->onHeadersException,
177
- $ctx
178
- )
179
- );
180
- }
181
- if (version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) {
182
- $message = sprintf(
183
- 'cURL error %s: %s (%s)',
184
- $ctx['errno'],
185
- $ctx['error'],
186
- 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'
187
- );
188
- } else {
189
- $message = sprintf(
190
- 'cURL error %s: %s (%s) for %s',
191
- $ctx['errno'],
192
- $ctx['error'],
193
- 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html',
194
- $easy->request->getUri()
195
- );
196
- }
197
-
198
- // Create a connection exception if it was a specific error code.
199
- $error = isset($connectionErrors[$easy->errno])
200
- ? new ConnectException($message, $easy->request, null, $ctx)
201
- : new RequestException($message, $easy->request, $easy->response, null, $ctx);
202
-
203
- return \GuzzleHttp\Promise\rejection_for($error);
204
- }
205
-
206
- private function getDefaultConf(EasyHandle $easy)
207
- {
208
- $conf = [
209
- '_headers' => $easy->request->getHeaders(),
210
- CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
211
- CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
212
- CURLOPT_RETURNTRANSFER => false,
213
- CURLOPT_HEADER => false,
214
- CURLOPT_CONNECTTIMEOUT => 150,
215
- ];
216
-
217
- if (defined('CURLOPT_PROTOCOLS')) {
218
- $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
219
- }
220
-
221
- $version = $easy->request->getProtocolVersion();
222
- if ($version == 1.1) {
223
- $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
224
- } elseif ($version == 2.0) {
225
- $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
226
- } else {
227
- $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
228
- }
229
-
230
- return $conf;
231
- }
232
-
233
- private function applyMethod(EasyHandle $easy, array &$conf)
234
- {
235
- $body = $easy->request->getBody();
236
- $size = $body->getSize();
237
-
238
- if ($size === null || $size > 0) {
239
- $this->applyBody($easy->request, $easy->options, $conf);
240
- return;
241
- }
242
-
243
- $method = $easy->request->getMethod();
244
- if ($method === 'PUT' || $method === 'POST') {
245
- // See http://tools.ietf.org/html/rfc7230#section-3.3.2
246
- if (!$easy->request->hasHeader('Content-Length')) {
247
- $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
248
- }
249
- } elseif ($method === 'HEAD') {
250
- $conf[CURLOPT_NOBODY] = true;
251
- unset(
252
- $conf[CURLOPT_WRITEFUNCTION],
253
- $conf[CURLOPT_READFUNCTION],
254
- $conf[CURLOPT_FILE],
255
- $conf[CURLOPT_INFILE]
256
- );
257
- }
258
- }
259
-
260
- private function applyBody(RequestInterface $request, array $options, array &$conf)
261
- {
262
- $size = $request->hasHeader('Content-Length')
263
- ? (int) $request->getHeaderLine('Content-Length')
264
- : null;
265
-
266
- // Send the body as a string if the size is less than 1MB OR if the
267
- // [curl][body_as_string] request value is set.
268
- if (($size !== null && $size < 1000000) ||
269
- !empty($options['_body_as_string'])
270
- ) {
271
- $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
272
- // Don't duplicate the Content-Length header
273
- $this->removeHeader('Content-Length', $conf);
274
- $this->removeHeader('Transfer-Encoding', $conf);
275
- } else {
276
- $conf[CURLOPT_UPLOAD] = true;
277
- if ($size !== null) {
278
- $conf[CURLOPT_INFILESIZE] = $size;
279
- $this->removeHeader('Content-Length', $conf);
280
- }
281
- $body = $request->getBody();
282
- if ($body->isSeekable()) {
283
- $body->rewind();
284
- }
285
- $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
286
- return $body->read($length);
287
- };
288
- }
289
-
290
- // If the Expect header is not present, prevent curl from adding it
291
- if (!$request->hasHeader('Expect')) {
292
- $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
293
- }
294
-
295
- // cURL sometimes adds a content-type by default. Prevent this.
296
- if (!$request->hasHeader('Content-Type')) {
297
- $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
298
- }
299
- }
300
-
301
- private function applyHeaders(EasyHandle $easy, array &$conf)
302
- {
303
- foreach ($conf['_headers'] as $name => $values) {
304
- foreach ($values as $value) {
305
- $value = (string) $value;
306
- if ($value === '') {
307
- // cURL requires a special format for empty headers.
308
- // See https://github.com/guzzle/guzzle/issues/1882 for more details.
309
- $conf[CURLOPT_HTTPHEADER][] = "$name;";
310
- } else {
311
- $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
312
- }
313
- }
314
- }
315
-
316
- // Remove the Accept header if one was not set
317
- if (!$easy->request->hasHeader('Accept')) {
318
- $conf[CURLOPT_HTTPHEADER][] = 'Accept:';
319
- }
320
- }
321
-
322
- /**
323
- * Remove a header from the options array.
324
- *
325
- * @param string $name Case-insensitive header to remove
326
- * @param array $options Array of options to modify
327
- */
328
- private function removeHeader($name, array &$options)
329
- {
330
- foreach (array_keys($options['_headers']) as $key) {
331
- if (!strcasecmp($key, $name)) {
332
- unset($options['_headers'][$key]);
333
- return;
334
- }
335
- }
336
- }
337
-
338
- private function applyHandlerOptions(EasyHandle $easy, array &$conf)
339
- {
340
- $options = $easy->options;
341
- if (isset($options['verify'])) {
342
- if ($options['verify'] === false) {
343
- unset($conf[CURLOPT_CAINFO]);
344
- $conf[CURLOPT_SSL_VERIFYHOST] = 0;
345
- $conf[CURLOPT_SSL_VERIFYPEER] = false;
346
- } else {
347
- $conf[CURLOPT_SSL_VERIFYHOST] = 2;
348
- $conf[CURLOPT_SSL_VERIFYPEER] = true;
349
- if (is_string($options['verify'])) {
350
- // Throw an error if the file/folder/link path is not valid or doesn't exist.
351
- if (!file_exists($options['verify'])) {
352
- throw new \InvalidArgumentException(
353
- "SSL CA bundle not found: {$options['verify']}"
354
- );
355
- }
356
- // If it's a directory or a link to a directory use CURLOPT_CAPATH.
357
- // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
358
- if (is_dir($options['verify']) ||
359
- (is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
360
- $conf[CURLOPT_CAPATH] = $options['verify'];
361
- } else {
362
- $conf[CURLOPT_CAINFO] = $options['verify'];
363
- }
364
- }
365
- }
366
- }
367
-
368
- if (!empty($options['decode_content'])) {
369
- $accept = $easy->request->getHeaderLine('Accept-Encoding');
370
- if ($accept) {
371
- $conf[CURLOPT_ENCODING] = $accept;
372
- } else {
373
- $conf[CURLOPT_ENCODING] = '';
374
- // Don't let curl send the header over the wire
375
- $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
376
- }
377
- }
378
-
379
- if (isset($options['sink'])) {
380
- $sink = $options['sink'];
381
- if (!is_string($sink)) {
382
- $sink = \GuzzleHttp\Psr7\stream_for($sink);
383
- } elseif (!is_dir(dirname($sink))) {
384
- // Ensure that the directory exists before failing in curl.
385
- throw new \RuntimeException(sprintf(
386
- 'Directory %s does not exist for sink value of %s',
387
- dirname($sink),
388
- $sink
389
- ));
390
- } else {
391
- $sink = new LazyOpenStream($sink, 'w+');
392
- }
393
- $easy->sink = $sink;
394
- $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
395
- return $sink->write($write);
396
- };
397
- } else {
398
- // Use a default temp stream if no sink was set.
399
- $conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
400
- $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
401
- }
402
- $timeoutRequiresNoSignal = false;
403
- if (isset($options['timeout'])) {
404
- $timeoutRequiresNoSignal |= $options['timeout'] < 1;
405
- $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
406
- }
407
-
408
- // CURL default value is CURL_IPRESOLVE_WHATEVER
409
- if (isset($options['force_ip_resolve'])) {
410
- if ('v4' === $options['force_ip_resolve']) {
411
- $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
412
- } elseif ('v6' === $options['force_ip_resolve']) {
413
- $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
414
- }
415
- }
416
-
417
- if (isset($options['connect_timeout'])) {
418
- $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
419
- $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
420
- }
421
-
422
- if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
423
- $conf[CURLOPT_NOSIGNAL] = true;
424
- }
425
-
426
- if (isset($options['proxy'])) {
427
- if (!is_array($options['proxy'])) {
428
- $conf[CURLOPT_PROXY] = $options['proxy'];
429
- } else {
430
- $scheme = $easy->request->getUri()->getScheme();
431
- if (isset($options['proxy'][$scheme])) {
432
- $host = $easy->request->getUri()->getHost();
433
- if (!isset($options['proxy']['no']) ||
434
- !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
435
- ) {
436
- $conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
437
- }
438
- }
439
- }
440
- }
441
-
442
- if (isset($options['cert'])) {
443
- $cert = $options['cert'];
444
- if (is_array($cert)) {
445
- $conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
446
- $cert = $cert[0];
447
- }
448
- if (!file_exists($cert)) {
449
- throw new \InvalidArgumentException(
450
- "SSL certificate not found: {$cert}"
451
- );
452
- }
453
- $conf[CURLOPT_SSLCERT] = $cert;
454
- }
455
-
456
- if (isset($options['ssl_key'])) {
457
- $sslKey = $options['ssl_key'];
458
- if (is_array($sslKey)) {
459
- $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
460
- $sslKey = $sslKey[0];
461
- }
462
- if (!file_exists($sslKey)) {
463
- throw new \InvalidArgumentException(
464
- "SSL private key not found: {$sslKey}"
465
- );
466
- }
467
- $conf[CURLOPT_SSLKEY] = $sslKey;
468
- }
469
-
470
- if (isset($options['progress'])) {
471
- $progress = $options['progress'];
472
- if (!is_callable($progress)) {
473
- throw new \InvalidArgumentException(
474
- 'progress client option must be callable'
475
- );
476
- }
477
- $conf[CURLOPT_NOPROGRESS] = false;
478
- $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
479
- $args = func_get_args();
480
- // PHP 5.5 pushed the handle onto the start of the args
481
- if (is_resource($args[0])) {
482
- array_shift($args);
483
- }
484
- call_user_func_array($progress, $args);
485
- };
486
- }
487
-
488
- if (!empty($options['debug'])) {
489
- $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
490
- $conf[CURLOPT_VERBOSE] = true;
491
- }
492
- }
493
-
494
- /**
495
- * This function ensures that a response was set on a transaction. If one
496
- * was not set, then the request is retried if possible. This error
497
- * typically means you are sending a payload, curl encountered a
498
- * "Connection died, retrying a fresh connect" error, tried to rewind the
499
- * stream, and then encountered a "necessary data rewind wasn't possible"
500
- * error, causing the request to be sent through curl_multi_info_read()
501
- * without an error status.
502
- */
503
- private static function retryFailedRewind(
504
- callable $handler,
505
- EasyHandle $easy,
506
- array $ctx
507
- ) {
508
- try {
509
- // Only rewind if the body has been read from.
510
- $body = $easy->request->getBody();
511
- if ($body->tell() > 0) {
512
- $body->rewind();
513
- }
514
- } catch (\RuntimeException $e) {
515
- $ctx['error'] = 'The connection unexpectedly failed without '
516
- . 'providing an error. The request would have been retried, '
517
- . 'but attempting to rewind the request body failed. '
518
- . 'Exception: ' . $e;
519
- return self::createRejection($easy, $ctx);
520
- }
521
-
522
- // Retry no more than 3 times before giving up.
523
- if (!isset($easy->options['_curl_retries'])) {
524
- $easy->options['_curl_retries'] = 1;
525
- } elseif ($easy->options['_curl_retries'] == 2) {
526
- $ctx['error'] = 'The cURL request was retried 3 times '
527
- . 'and did not succeed. The most likely reason for the failure '
528
- . 'is that cURL was unable to rewind the body of the request '
529
- . 'and subsequent retries resulted in the same error. Turn on '
530
- . 'the debug option to see what went wrong. See '
531
- . 'https://bugs.php.net/bug.php?id=47204 for more information.';
532
- return self::createRejection($easy, $ctx);
533
- } else {
534
- $easy->options['_curl_retries']++;
535
- }
536
-
537
- return $handler($easy->request, $easy->options);
538
- }
539
-
540
- private function createHeaderFn(EasyHandle $easy)
541
- {
542
- if (isset($easy->options['on_headers'])) {
543
- $onHeaders = $easy->options['on_headers'];
544
-
545
- if (!is_callable($onHeaders)) {
546
- throw new \InvalidArgumentException('on_headers must be callable');
547
- }
548
- } else {
549
- $onHeaders = null;
550
- }
551
-
552
- return function ($ch, $h) use (
553
- $onHeaders,
554
- $easy,
555
- &$startingResponse
556
- ) {
557
- $value = trim($h);
558
- if ($value === '') {
559
- $startingResponse = true;
560
- $easy->createResponse();
561
- if ($onHeaders !== null) {
562
- try {
563
- $onHeaders($easy->response);
564
- } catch (\Exception $e) {
565
- // Associate the exception with the handle and trigger
566
- // a curl header write error by returning 0.
567
- $easy->onHeadersException = $e;
568
- return -1;
569
- }
570
- }
571
- } elseif ($startingResponse) {
572
- $startingResponse = false;
573
- $easy->headers = [$value];
574
- } else {
575
- $easy->headers[] = $value;
576
- }
577
- return strlen($h);
578
- };
579
- }
580
- }
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp\Handler;
3
+
4
+ use GuzzleHttp\Exception\ConnectException;
5
+ use GuzzleHttp\Exception\RequestException;
6
+ use GuzzleHttp\Promise\FulfilledPromise;
7
+ use GuzzleHttp\Psr7;
8
+ use GuzzleHttp\Psr7\LazyOpenStream;
9
+ use GuzzleHttp\TransferStats;
10
+ use Psr\Http\Message\RequestInterface;
11
+
12
+ /**
13
+ * Creates curl resources from a request
14
+ */
15
+ class CurlFactory implements CurlFactoryInterface
16
+ {
17
+ const CURL_VERSION_STR = 'curl_version';
18
+ const LOW_CURL_VERSION_NUMBER = '7.21.2';
19
+
20
+ /** @var array */
21
+ private $handles = [];
22
+
23
+ /** @var int Total number of idle handles to keep in cache */
24
+ private $maxHandles;
25
+
26
+ /**
27
+ * @param int $maxHandles Maximum number of idle handles.
28
+ */
29
+ public function __construct($maxHandles)
30
+ {
31
+ $this->maxHandles = $maxHandles;
32
+ }
33
+
34
+ public function create(RequestInterface $request, array $options)
35
+ {
36
+ if (isset($options['curl']['body_as_string'])) {
37
+ $options['_body_as_string'] = $options['curl']['body_as_string'];
38
+ unset($options['curl']['body_as_string']);
39
+ }
40
+
41
+ $easy = new EasyHandle;
42
+ $easy->request = $request;
43
+ $easy->options = $options;
44
+ $conf = $this->getDefaultConf($easy);
45
+ $this->applyMethod($easy, $conf);
46
+ $this->applyHandlerOptions($easy, $conf);
47
+ $this->applyHeaders($easy, $conf);
48
+ unset($conf['_headers']);
49
+
50
+ // Add handler options from the request configuration options
51
+ if (isset($options['curl'])) {
52
+ $conf = array_replace($conf, $options['curl']);
53
+ }
54
+
55
+ $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
56
+ $easy->handle = $this->handles
57
+ ? array_pop($this->handles)
58
+ : curl_init();
59
+ curl_setopt_array($easy->handle, $conf);
60
+
61
+ return $easy;
62
+ }
63
+
64
+ public function release(EasyHandle $easy)
65
+ {
66
+ $resource = $easy->handle;
67
+ unset($easy->handle);
68
+
69
+ if (count($this->handles) >= $this->maxHandles) {
70
+ curl_close($resource);
71
+ } else {
72
+ // Remove all callback functions as they can hold onto references
73
+ // and are not cleaned up by curl_reset. Using curl_setopt_array
74
+ // does not work for some reason, so removing each one
75
+ // individually.
76
+ curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
77
+ curl_setopt($resource, CURLOPT_READFUNCTION, null);
78
+ curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
79
+ curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
80
+ curl_reset($resource);
81
+ $this->handles[] = $resource;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Completes a cURL transaction, either returning a response promise or a
87
+ * rejected promise.
88
+ *
89
+ * @param callable $handler
90
+ * @param EasyHandle $easy
91
+ * @param CurlFactoryInterface $factory Dictates how the handle is released
92
+ *
93
+ * @return \GuzzleHttp\Promise\PromiseInterface
94
+ */
95
+ public static function finish(
96
+ callable $handler,
97
+ EasyHandle $easy,
98
+ CurlFactoryInterface $factory
99
+ ) {
100
+ if (isset($easy->options['on_stats'])) {
101
+ self::invokeStats($easy);
102
+ }
103
+
104
+ if (!$easy->response || $easy->errno) {
105
+ return self::finishError($handler, $easy, $factory);
106
+ }
107
+
108
+ // Return the response if it is present and there is no error.
109
+ $factory->release($easy);
110
+
111
+ // Rewind the body of the response if possible.
112
+ $body = $easy->response->getBody();
113
+ if ($body->isSeekable()) {
114
+ $body->rewind();
115
+ }
116
+
117
+ return new FulfilledPromise($easy->response);
118
+ }
119
+
120
+ private static function invokeStats(EasyHandle $easy)
121
+ {
122
+ $curlStats = curl_getinfo($easy->handle);
123
+ $curlStats['appconnect_time'] = curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME);
124
+ $stats = new TransferStats(
125
+ $easy->request,
126
+ $easy->response,
127
+ $curlStats['total_time'],
128
+ $easy->errno,
129
+ $curlStats
130
+ );
131
+ call_user_func($easy->options['on_stats'], $stats);
132
+ }
133
+
134
+ private static function finishError(
135
+ callable $handler,
136
+ EasyHandle $easy,
137
+ CurlFactoryInterface $factory
138
+ ) {
139
+ // Get error information and release the handle to the factory.
140
+ $ctx = [
141
+ 'errno' => $easy->errno,
142
+ 'error' => curl_error($easy->handle),
143
+ 'appconnect_time' => curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME),
144
+ ] + curl_getinfo($easy->handle);
145
+ $ctx[self::CURL_VERSION_STR] = curl_version()['version'];
146
+ $factory->release($easy);
147
+
148
+ // Retry when nothing is present or when curl failed to rewind.
149
+ if (empty($easy->options['_err_message'])
150
+ && (!$easy->errno || $easy->errno == 65)
151
+ ) {
152
+ return self::retryFailedRewind($handler, $easy, $ctx);
153
+ }
154
+
155
+ return self::createRejection($easy, $ctx);
156
+ }
157
+
158
+ private static function createRejection(EasyHandle $easy, array $ctx)
159
+ {
160
+ static $connectionErrors = [
161
+ CURLE_OPERATION_TIMEOUTED => true,
162
+ CURLE_COULDNT_RESOLVE_HOST => true,
163
+ CURLE_COULDNT_CONNECT => true,
164
+ CURLE_SSL_CONNECT_ERROR => true,
165
+ CURLE_GOT_NOTHING => true,
166
+ ];
167
+
168
+ // If an exception was encountered during the onHeaders event, then
169
+ // return a rejected promise that wraps that exception.
170
+ if ($easy->onHeadersException) {
171
+ return \GuzzleHttp\Promise\rejection_for(
172
+ new RequestException(
173
+ 'An error was encountered during the on_headers event',
174
+ $easy->request,
175
+ $easy->response,
176
+ $easy->onHeadersException,
177
+ $ctx
178
+ )
179
+ );
180
+ }
181
+ if (version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) {
182
+ $message = sprintf(
183
+ 'cURL error %s: %s (%s)',
184
+ $ctx['errno'],
185
+ $ctx['error'],
186
+ 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'
187
+ );
188
+ } else {
189
+ $message = sprintf(
190
+ 'cURL error %s: %s (%s) for %s',
191
+ $ctx['errno'],
192
+ $ctx['error'],
193
+ 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html',
194
+ $easy->request->getUri()
195
+ );
196
+ }
197
+
198
+ // Create a connection exception if it was a specific error code.
199
+ $error = isset($connectionErrors[$easy->errno])
200
+ ? new ConnectException($message, $easy->request, null, $ctx)
201
+ : new RequestException($message, $easy->request, $easy->response, null, $ctx);
202
+
203
+ return \GuzzleHttp\Promise\rejection_for($error);
204
+ }
205
+
206
+ private function getDefaultConf(EasyHandle $easy)
207
+ {
208
+ $conf = [
209
+ '_headers' => $easy->request->getHeaders(),
210
+ CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
211
+ CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
212
+ CURLOPT_RETURNTRANSFER => false,
213
+ CURLOPT_HEADER => false,
214
+ CURLOPT_CONNECTTIMEOUT => 150,
215
+ ];
216
+
217
+ if (defined('CURLOPT_PROTOCOLS')) {
218
+ $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
219
+ }
220
+
221
+ $version = $easy->request->getProtocolVersion();
222
+ if ($version == 1.1) {
223
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
224
+ } elseif ($version == 2.0) {
225
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
226
+ } else {
227
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
228
+ }
229
+
230
+ return $conf;
231
+ }
232
+
233
+ private function applyMethod(EasyHandle $easy, array &$conf)
234
+ {
235
+ $body = $easy->request->getBody();
236
+ $size = $body->getSize();
237
+
238
+ if ($size === null || $size > 0) {
239
+ $this->applyBody($easy->request, $easy->options, $conf);
240
+ return;
241
+ }
242
+
243
+ $method = $easy->request->getMethod();
244
+ if ($method === 'PUT' || $method === 'POST') {
245
+ // See http://tools.ietf.org/html/rfc7230#section-3.3.2
246
+ if (!$easy->request->hasHeader('Content-Length')) {
247
+ $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
248
+ }
249
+ } elseif ($method === 'HEAD') {
250
+ $conf[CURLOPT_NOBODY] = true;
251
+ unset(
252
+ $conf[CURLOPT_WRITEFUNCTION],
253
+ $conf[CURLOPT_READFUNCTION],
254
+ $conf[CURLOPT_FILE],
255
+ $conf[CURLOPT_INFILE]
256
+ );
257
+ }
258
+ }
259
+
260
+ private function applyBody(RequestInterface $request, array $options, array &$conf)
261
+ {
262
+ $size = $request->hasHeader('Content-Length')
263
+ ? (int) $request->getHeaderLine('Content-Length')
264
+ : null;
265
+
266
+ // Send the body as a string if the size is less than 1MB OR if the
267
+ // [curl][body_as_string] request value is set.
268
+ if (($size !== null && $size < 1000000) ||
269
+ !empty($options['_body_as_string'])
270
+ ) {
271
+ $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
272
+ // Don't duplicate the Content-Length header
273
+ $this->removeHeader('Content-Length', $conf);
274
+ $this->removeHeader('Transfer-Encoding', $conf);
275
+ } else {
276
+ $conf[CURLOPT_UPLOAD] = true;
277
+ if ($size !== null) {
278
+ $conf[CURLOPT_INFILESIZE] = $size;
279
+ $this->removeHeader('Content-Length', $conf);
280
+ }
281
+ $body = $request->getBody();
282
+ if ($body->isSeekable()) {
283
+ $body->rewind();
284
+ }
285
+ $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
286
+ return $body->read($length);
287
+ };
288
+ }
289
+
290
+ // If the Expect header is not present, prevent curl from adding it
291
+ if (!$request->hasHeader('Expect')) {
292
+ $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
293
+ }
294
+
295
+ // cURL sometimes adds a content-type by default. Prevent this.
296
+ if (!$request->hasHeader('Content-Type')) {
297
+ $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
298
+ }
299
+ }
300
+
301
+ private function applyHeaders(EasyHandle $easy, array &$conf)
302
+ {
303
+ foreach ($conf['_headers'] as $name => $values) {
304
+ foreach ($values as $value) {
305
+ $value = (string) $value;
306
+ if ($value === '') {
307
+ // cURL requires a special format for empty headers.
308
+ // See https://github.com/guzzle/guzzle/issues/1882 for more details.
309
+ $conf[CURLOPT_HTTPHEADER][] = "$name;";
310
+ } else {
311
+ $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
312
+ }
313
+ }
314
+ }
315
+
316
+ // Remove the Accept header if one was not set
317
+ if (!$easy->request->hasHeader('Accept')) {
318
+ $conf[CURLOPT_HTTPHEADER][] = 'Accept:';
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Remove a header from the options array.
324
+ *
325
+ * @param string $name Case-insensitive header to remove
326
+ * @param array $options Array of options to modify
327
+ */
328
+ private function removeHeader($name, array &$options)
329
+ {
330
+ foreach (array_keys($options['_headers']) as $key) {
331
+ if (!strcasecmp($key, $name)) {
332
+ unset($options['_headers'][$key]);
333
+ return;
334
+ }
335
+ }
336
+ }
337
+
338
+ private function applyHandlerOptions(EasyHandle $easy, array &$conf)
339
+ {
340
+ $options = $easy->options;
341
+ if (isset($options['verify'])) {
342
+ if ($options['verify'] === false) {
343
+ unset($conf[CURLOPT_CAINFO]);
344
+ $conf[CURLOPT_SSL_VERIFYHOST] = 0;
345
+ $conf[CURLOPT_SSL_VERIFYPEER] = false;
346
+ } else {
347
+ $conf[CURLOPT_SSL_VERIFYHOST] = 2;
348
+ $conf[CURLOPT_SSL_VERIFYPEER] = true;
349
+ if (is_string($options['verify'])) {
350
+ // Throw an error if the file/folder/link path is not valid or doesn't exist.
351
+ if (!file_exists($options['verify'])) {
352
+ throw new \InvalidArgumentException(
353
+ "SSL CA bundle not found: {$options['verify']}"
354
+ );
355
+ }
356
+ // If it's a directory or a link to a directory use CURLOPT_CAPATH.
357
+ // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
358
+ if (is_dir($options['verify']) ||
359
+ (is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
360
+ $conf[CURLOPT_CAPATH] = $options['verify'];
361
+ } else {
362
+ $conf[CURLOPT_CAINFO] = $options['verify'];
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ if (!empty($options['decode_content'])) {
369
+ $accept = $easy->request->getHeaderLine('Accept-Encoding');
370
+ if ($accept) {
371
+ $conf[CURLOPT_ENCODING] = $accept;
372
+ } else {
373
+ $conf[CURLOPT_ENCODING] = '';
374
+ // Don't let curl send the header over the wire
375
+ $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
376
+ }
377
+ }
378
+
379
+ if (isset($options['sink'])) {
380
+ $sink = $options['sink'];
381
+ if (!is_string($sink)) {
382
+ $sink = \GuzzleHttp\Psr7\stream_for($sink);
383
+ } elseif (!is_dir(dirname($sink))) {
384
+ // Ensure that the directory exists before failing in curl.
385
+ throw new \RuntimeException(sprintf(
386
+ 'Directory %s does not exist for sink value of %s',
387
+ dirname($sink),
388
+ $sink
389
+ ));
390
+ } else {
391
+ $sink = new LazyOpenStream($sink, 'w+');
392
+ }
393
+ $easy->sink = $sink;
394
+ $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
395
+ return $sink->write($write);
396
+ };
397
+ } else {
398
+ // Use a default temp stream if no sink was set.
399
+ $conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
400
+ $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
401
+ }
402
+ $timeoutRequiresNoSignal = false;
403
+ if (isset($options['timeout'])) {
404
+ $timeoutRequiresNoSignal |= $options['timeout'] < 1;
405
+ $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
406
+ }
407
+
408
+ // CURL default value is CURL_IPRESOLVE_WHATEVER
409
+ if (isset($options['force_ip_resolve'])) {
410
+ if ('v4' === $options['force_ip_resolve']) {
411
+ $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
412
+ } elseif ('v6' === $options['force_ip_resolve']) {
413
+ $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
414
+ }
415
+ }
416
+
417
+ if (isset($options['connect_timeout'])) {
418
+ $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
419
+ $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
420
+ }
421
+
422
+ if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
423
+ $conf[CURLOPT_NOSIGNAL] = true;
424
+ }
425
+
426
+ if (isset($options['proxy'])) {
427
+ if (!is_array($options['proxy'])) {
428
+ $conf[CURLOPT_PROXY] = $options['proxy'];
429
+ } else {
430
+ $scheme = $easy->request->getUri()->getScheme();
431
+ if (isset($options['proxy'][$scheme])) {
432
+ $host = $easy->request->getUri()->getHost();
433
+ if (!isset($options['proxy']['no']) ||
434
+ !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
435
+ ) {
436
+ $conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
437
+ }
438
+ }
439
+ }
440
+ }
441
+
442
+ if (isset($options['cert'])) {
443
+ $cert = $options['cert'];
444
+ if (is_array($cert)) {
445
+ $conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
446
+ $cert = $cert[0];
447
+ }
448
+ if (!file_exists($cert)) {
449
+ throw new \InvalidArgumentException(
450
+ "SSL certificate not found: {$cert}"
451
+ );
452
+ }
453
+ $conf[CURLOPT_SSLCERT] = $cert;
454
+ }
455
+
456
+ if (isset($options['ssl_key'])) {
457
+ if (is_array($options['ssl_key'])) {
458
+ if (count($options['ssl_key']) === 2) {
459
+ list($sslKey, $conf[CURLOPT_SSLKEYPASSWD]) = $options['ssl_key'];
460
+ } else {
461
+ list($sslKey) = $options['ssl_key'];
462
+ }
463
+ }
464
+
465
+ $sslKey = isset($sslKey) ? $sslKey: $options['ssl_key'];
466
+
467
+ if (!file_exists($sslKey)) {
468
+ throw new \InvalidArgumentException(
469
+ "SSL private key not found: {$sslKey}"
470
+ );
471
+ }
472
+ $conf[CURLOPT_SSLKEY] = $sslKey;
473
+ }
474
+
475
+ if (isset($options['progress'])) {
476
+ $progress = $options['progress'];
477
+ if (!is_callable($progress)) {
478
+ throw new \InvalidArgumentException(
479
+ 'progress client option must be callable'
480
+ );
481
+ }
482
+ $conf[CURLOPT_NOPROGRESS] = false;
483
+ $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
484
+ $args = func_get_args();
485
+ // PHP 5.5 pushed the handle onto the start of the args
486
+ if (is_resource($args[0])) {
487
+ array_shift($args);
488
+ }
489
+ call_user_func_array($progress, $args);
490
+ };
491
+ }
492
+
493
+ if (!empty($options['debug'])) {
494
+ $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
495
+ $conf[CURLOPT_VERBOSE] = true;
496
+ }
497
+ }
498
+
499
+ /**
500
+ * This function ensures that a response was set on a transaction. If one
501
+ * was not set, then the request is retried if possible. This error
502
+ * typically means you are sending a payload, curl encountered a
503
+ * "Connection died, retrying a fresh connect" error, tried to rewind the
504
+ * stream, and then encountered a "necessary data rewind wasn't possible"
505
+ * error, causing the request to be sent through curl_multi_info_read()
506
+ * without an error status.
507
+ */
508
+ private static function retryFailedRewind(
509
+ callable $handler,
510
+ EasyHandle $easy,
511
+ array $ctx
512
+ ) {
513
+ try {
514
+ // Only rewind if the body has been read from.
515
+ $body = $easy->request->getBody();
516
+ if ($body->tell() > 0) {
517
+ $body->rewind();
518
+ }
519
+ } catch (\RuntimeException $e) {
520
+ $ctx['error'] = 'The connection unexpectedly failed without '
521
+ . 'providing an error. The request would have been retried, '
522
+ . 'but attempting to rewind the request body failed. '
523
+ . 'Exception: ' . $e;
524
+ return self::createRejection($easy, $ctx);
525
+ }
526
+
527
+ // Retry no more than 3 times before giving up.
528
+ if (!isset($easy->options['_curl_retries'])) {
529
+ $easy->options['_curl_retries'] = 1;
530
+ } elseif ($easy->options['_curl_retries'] == 2) {
531
+ $ctx['error'] = 'The cURL request was retried 3 times '
532
+ . 'and did not succeed. The most likely reason for the failure '
533
+ . 'is that cURL was unable to rewind the body of the request '
534
+ . 'and subsequent retries resulted in the same error. Turn on '
535
+ . 'the debug option to see what went wrong. See '
536
+ . 'https://bugs.php.net/bug.php?id=47204 for more information.';
537
+ return self::createRejection($easy, $ctx);
538
+ } else {
539
+ $easy->options['_curl_retries']++;
540
+ }
541
+
542
+ return $handler($easy->request, $easy->options);
543
+ }
544
+
545
+ private function createHeaderFn(EasyHandle $easy)
546
+ {
547
+ if (isset($easy->options['on_headers'])) {
548
+ $onHeaders = $easy->options['on_headers'];
549
+
550
+ if (!is_callable($onHeaders)) {
551
+ throw new \InvalidArgumentException('on_headers must be callable');
552
+ }
553
+ } else {
554
+ $onHeaders = null;
555
+ }
556
+
557
+ return function ($ch, $h) use (
558
+ $onHeaders,
559
+ $easy,
560
+ &$startingResponse
561
+ ) {
562
+ $value = trim($h);
563
+ if ($value === '') {
564
+ $startingResponse = true;
565
+ $easy->createResponse();
566
+ if ($onHeaders !== null) {
567
+ try {
568
+ $onHeaders($easy->response);
569
+ } catch (\Exception $e) {
570
+ // Associate the exception with the handle and trigger
571
+ // a curl header write error by returning 0.
572
+ $easy->onHeadersException = $e;
573
+ return -1;
574
+ }
575
+ }
576
+ } elseif ($startingResponse) {
577
+ $startingResponse = false;
578
+ $easy->headers = [$value];
579
+ } else {
580
+ $easy->headers[] = $value;
581
+ }
582
+ return strlen($h);
583
+ };
584
+ }
585
+ }
vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php CHANGED
@@ -1,205 +1,219 @@
1
- <?php
2
- namespace GuzzleHttp\Handler;
3
-
4
- use GuzzleHttp\Promise as P;
5
- use GuzzleHttp\Promise\Promise;
6
- use GuzzleHttp\Psr7;
7
- use Psr\Http\Message\RequestInterface;
8
-
9
- /**
10
- * Returns an asynchronous response using curl_multi_* functions.
11
- *
12
- * When using the CurlMultiHandler, custom curl options can be specified as an
13
- * associative array of curl option constants mapping to values in the
14
- * **curl** key of the provided request options.
15
- *
16
- * @property resource $_mh Internal use only. Lazy loaded multi-handle.
17
- */
18
- class CurlMultiHandler
19
- {
20
- /** @var CurlFactoryInterface */
21
- private $factory;
22
- private $selectTimeout;
23
- private $active;
24
- private $handles = [];
25
- private $delays = [];
26
-
27
- /**
28
- * This handler accepts the following options:
29
- *
30
- * - handle_factory: An optional factory used to create curl handles
31
- * - select_timeout: Optional timeout (in seconds) to block before timing
32
- * out while selecting curl handles. Defaults to 1 second.
33
- *
34
- * @param array $options
35
- */
36
- public function __construct(array $options = [])
37
- {
38
- $this->factory = isset($options['handle_factory'])
39
- ? $options['handle_factory'] : new CurlFactory(50);
40
-
41
- if (isset($options['select_timeout'])) {
42
- $this->selectTimeout = $options['select_timeout'];
43
- } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
44
- $this->selectTimeout = $selectTimeout;
45
- } else {
46
- $this->selectTimeout = 1;
47
- }
48
- }
49
-
50
- public function __get($name)
51
- {
52
- if ($name === '_mh') {
53
- return $this->_mh = curl_multi_init();
54
- }
55
-
56
- throw new \BadMethodCallException();
57
- }
58
-
59
- public function __destruct()
60
- {
61
- if (isset($this->_mh)) {
62
- curl_multi_close($this->_mh);
63
- unset($this->_mh);
64
- }
65
- }
66
-
67
- public function __invoke(RequestInterface $request, array $options)
68
- {
69
- $easy = $this->factory->create($request, $options);
70
- $id = (int) $easy->handle;
71
-
72
- $promise = new Promise(
73
- [$this, 'execute'],
74
- function () use ($id) {
75
- return $this->cancel($id);
76
- }
77
- );
78
-
79
- $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
80
-
81
- return $promise;
82
- }
83
-
84
- /**
85
- * Ticks the curl event loop.
86
- */
87
- public function tick()
88
- {
89
- // Add any delayed handles if needed.
90
- if ($this->delays) {
91
- $currentTime = \GuzzleHttp\_current_time();
92
- foreach ($this->delays as $id => $delay) {
93
- if ($currentTime >= $delay) {
94
- unset($this->delays[$id]);
95
- curl_multi_add_handle(
96
- $this->_mh,
97
- $this->handles[$id]['easy']->handle
98
- );
99
- }
100
- }
101
- }
102
-
103
- // Step through the task queue which may add additional requests.
104
- P\queue()->run();
105
-
106
- if ($this->active &&
107
- curl_multi_select($this->_mh, $this->selectTimeout) === -1
108
- ) {
109
- // Perform a usleep if a select returns -1.
110
- // See: https://bugs.php.net/bug.php?id=61141
111
- usleep(250);
112
- }
113
-
114
- while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
115
-
116
- $this->processMessages();
117
- }
118
-
119
- /**
120
- * Runs until all outstanding connections have completed.
121
- */
122
- public function execute()
123
- {
124
- $queue = P\queue();
125
-
126
- while ($this->handles || !$queue->isEmpty()) {
127
- // If there are no transfers, then sleep for the next delay
128
- if (!$this->active && $this->delays) {
129
- usleep($this->timeToNext());
130
- }
131
- $this->tick();
132
- }
133
- }
134
-
135
- private function addRequest(array $entry)
136
- {
137
- $easy = $entry['easy'];
138
- $id = (int) $easy->handle;
139
- $this->handles[$id] = $entry;
140
- if (empty($easy->options['delay'])) {
141
- curl_multi_add_handle($this->_mh, $easy->handle);
142
- } else {
143
- $this->delays[$id] = \GuzzleHttp\_current_time() + ($easy->options['delay'] / 1000);
144
- }
145
- }
146
-
147
- /**
148
- * Cancels a handle from sending and removes references to it.
149
- *
150
- * @param int $id Handle ID to cancel and remove.
151
- *
152
- * @return bool True on success, false on failure.
153
- */
154
- private function cancel($id)
155
- {
156
- // Cannot cancel if it has been processed.
157
- if (!isset($this->handles[$id])) {
158
- return false;
159
- }
160
-
161
- $handle = $this->handles[$id]['easy']->handle;
162
- unset($this->delays[$id], $this->handles[$id]);
163
- curl_multi_remove_handle($this->_mh, $handle);
164
- curl_close($handle);
165
-
166
- return true;
167
- }
168
-
169
- private function processMessages()
170
- {
171
- while ($done = curl_multi_info_read($this->_mh)) {
172
- $id = (int) $done['handle'];
173
- curl_multi_remove_handle($this->_mh, $done['handle']);
174
-
175
- if (!isset($this->handles[$id])) {
176
- // Probably was cancelled.
177
- continue;
178
- }
179
-
180
- $entry = $this->handles[$id];
181
- unset($this->handles[$id], $this->delays[$id]);
182
- $entry['easy']->errno = $done['result'];
183
- $entry['deferred']->resolve(
184
- CurlFactory::finish(
185
- $this,
186
- $entry['easy'],
187
- $this->factory
188
- )
189
- );
190
- }
191
- }
192
-
193
- private function timeToNext()
194
- {
195
- $currentTime = \GuzzleHttp\_current_time();
196
- $nextTime = PHP_INT_MAX;
197
- foreach ($this->delays as $time) {
198
- if ($time < $nextTime) {
199
- $nextTime = $time;
200
- }
201
- }
202
-
203
- return max(0, $nextTime - $currentTime) * 1000000;
204
- }
205
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp\Handler;
3
+
4
+ use GuzzleHttp\Exception\InvalidArgumentException;
5
+ use GuzzleHttp\Promise as P;
6
+ use GuzzleHttp\Promise\Promise;
7
+ use Psr\Http\Message\RequestInterface;
8
+
9
+ /**
10
+ * Returns an asynchronous response using curl_multi_* functions.
11
+ *
12
+ * When using the CurlMultiHandler, custom curl options can be specified as an
13
+ * associative array of curl option constants mapping to values in the
14
+ * **curl** key of the provided request options.
15
+ *
16
+ * @property resource $_mh Internal use only. Lazy loaded multi-handle.
17
+ */
18
+ class CurlMultiHandler
19
+ {
20
+ /** @var CurlFactoryInterface */
21
+ private $factory;
22
+ private $selectTimeout;
23
+ private $active;
24
+ private $handles = [];
25
+ private $delays = [];
26
+ private $options = [];
27
+
28
+ /**
29
+ * This handler accepts the following options:
30
+ *
31
+ * - handle_factory: An optional factory used to create curl handles
32
+ * - select_timeout: Optional timeout (in seconds) to block before timing
33
+ * out while selecting curl handles. Defaults to 1 second.
34
+ * - options: An associative array of CURLMOPT_* options and
35
+ * corresponding values for curl_multi_setopt()
36
+ *
37
+ * @param array $options
38
+ */
39
+ public function __construct(array $options = [])
40
+ {
41
+ $this->factory = isset($options['handle_factory'])
42
+ ? $options['handle_factory'] : new CurlFactory(50);
43
+
44
+ if (isset($options['select_timeout'])) {
45
+ $this->selectTimeout = $options['select_timeout'];
46
+ } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) {
47
+ $this->selectTimeout = $selectTimeout;
48
+ } else {
49
+ $this->selectTimeout = 1;
50
+ }
51
+
52
+ $this->options = isset($options['options']) ? $options['options'] : [];
53
+ }
54
+
55
+ public function __get($name)
56
+ {
57
+ if ($name === '_mh') {
58
+ $this->_mh = curl_multi_init();
59
+
60
+ foreach ($this->options as $option => $value) {
61
+ // A warning is raised in case of a wrong option.
62
+ curl_multi_setopt($this->_mh, $option, $value);
63
+ }
64
+
65
+ // Further calls to _mh will return the value directly, without entering the
66
+ // __get() method at all.
67
+ return $this->_mh;
68
+ }
69
+
70
+ throw new \BadMethodCallException();
71
+ }
72
+
73
+ public function __destruct()
74
+ {
75
+ if (isset($this->_mh)) {
76
+ curl_multi_close($this->_mh);
77
+ unset($this->_mh);
78
+ }
79
+ }
80
+
81
+ public function __invoke(RequestInterface $request, array $options)
82
+ {
83
+ $easy = $this->factory->create($request, $options);
84
+ $id = (int) $easy->handle;
85
+
86
+ $promise = new Promise(
87
+ [$this, 'execute'],
88
+ function () use ($id) {
89
+ return $this->cancel($id);
90
+ }
91
+ );
92
+
93
+ $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
94
+
95
+ return $promise;
96
+ }
97
+
98
+ /**
99
+ * Ticks the curl event loop.
100
+ */
101
+ public function tick()
102
+ {
103
+ // Add any delayed handles if needed.
104
+ if ($this->delays) {
105
+ $currentTime = \GuzzleHttp\_current_time();
106
+ foreach ($this->delays as $id => $delay) {
107
+ if ($currentTime >= $delay) {
108
+ unset($this->delays[$id]);
109
+ curl_multi_add_handle(
110
+ $this->_mh,
111
+ $this->handles[$id]['easy']->handle
112
+ );
113
+ }
114
+ }
115
+ }
116
+
117
+ // Step through the task queue which may add additional requests.
118
+ P\queue()->run();
119
+
120
+ if ($this->active &&
121
+ curl_multi_select($this->_mh, $this->selectTimeout) === -1
122
+ ) {
123
+ // Perform a usleep if a select returns -1.
124
+ // See: https://bugs.php.net/bug.php?id=61141
125
+ usleep(250);
126
+ }
127
+
128
+ while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
129
+
130
+ $this->processMessages();
131
+ }
132
+
133
+ /**
134
+ * Runs until all outstanding connections have completed.
135
+ */
136
+ public function execute()
137
+ {
138
+ $queue = P\queue();
139
+
140
+ while ($this->handles || !$queue->isEmpty()) {
141
+ // If there are no transfers, then sleep for the next delay
142
+ if (!$this->active && $this->delays) {
143
+ usleep($this->timeToNext());
144
+ }
145
+ $this->tick();
146
+ }
147
+ }
148
+
149
+ private function addRequest(array $entry)
150
+ {
151
+ $easy = $entry['easy'];
152
+ $id = (int) $easy->handle;
153
+ $this->handles[$id] = $entry;
154
+ if (empty($easy->options['delay'])) {
155
+ curl_multi_add_handle($this->_mh, $easy->handle);
156
+ } else {
157
+ $this->delays[$id] = \GuzzleHttp\_current_time() + ($easy->options['delay'] / 1000);
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Cancels a handle from sending and removes references to it.
163
+ *
164
+ * @param int $id Handle ID to cancel and remove.
165
+ *
166
+ * @return bool True on success, false on failure.
167
+ */
168
+ private function cancel($id)
169
+ {
170
+ // Cannot cancel if it has been processed.
171
+ if (!isset($this->handles[$id])) {
172
+ return false;
173
+ }
174
+
175
+ $handle = $this->handles[$id]['easy']->handle;
176
+ unset($this->delays[$id], $this->handles[$id]);
177
+ curl_multi_remove_handle($this->_mh, $handle);
178
+ curl_close($handle);
179
+
180
+ return true;
181
+ }
182
+
183
+ private function processMessages()
184
+ {
185
+ while ($done = curl_multi_info_read($this->_mh)) {
186
+ $id = (int) $done['handle'];
187
+ curl_multi_remove_handle($this->_mh, $done['handle']);
188
+
189
+ if (!isset($this->handles[$id])) {
190
+ // Probably was cancelled.
191
+ continue;
192
+ }
193
+
194
+ $entry = $this->handles[$id];
195
+ unset($this->handles[$id], $this->delays[$id]);
196
+ $entry['easy']->errno = $done['result'];
197
+ $entry['deferred']->resolve(
198
+ CurlFactory::finish(
199
+ $this,
200
+ $entry['easy'],
201
+ $this->factory
202
+ )
203
+ );
204
+ }
205
+ }
206
+
207
+ private function timeToNext()
208
+ {
209
+ $currentTime = \GuzzleHttp\_current_time();
210
+ $nextTime = PHP_INT_MAX;
211
+ foreach ($this->delays as $time) {
212
+ if ($time < $nextTime) {
213
+ $nextTime = $time;
214
+ }
215
+ }
216
+
217
+ return max(0, $nextTime - $currentTime) * 1000000;
218
+ }
219
+ }
vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php CHANGED
@@ -1,190 +1,195 @@
1
- <?php
2
- namespace GuzzleHttp\Handler;
3
-
4
- use GuzzleHttp\Exception\RequestException;
5
- use GuzzleHttp\HandlerStack;
6
- use GuzzleHttp\Promise\PromiseInterface;
7
- use GuzzleHttp\Promise\RejectedPromise;
8
- use GuzzleHttp\TransferStats;
9
- use Psr\Http\Message\RequestInterface;
10
- use Psr\Http\Message\ResponseInterface;
11
-
12
- /**
13
- * Handler that returns responses or throw exceptions from a queue.
14
- */
15
- class MockHandler implements \Countable
16
- {
17
- private $queue = [];
18
- private $lastRequest;
19
- private $lastOptions;
20
- private $onFulfilled;
21
- private $onRejected;
22
-
23
- /**
24
- * Creates a new MockHandler that uses the default handler stack list of
25
- * middlewares.
26
- *
27
- * @param array $queue Array of responses, callables, or exceptions.
28
- * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
29
- * @param callable $onRejected Callback to invoke when the return value is rejected.
30
- *
31
- * @return HandlerStack
32
- */
33
- public static function createWithMiddleware(
34
- array $queue = null,
35
- callable $onFulfilled = null,
36
- callable $onRejected = null
37
- ) {
38
- return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
39
- }
40
-
41
- /**
42
- * The passed in value must be an array of
43
- * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
44
- * callables, or Promises.
45
- *
46
- * @param array $queue
47
- * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
48
- * @param callable $onRejected Callback to invoke when the return value is rejected.
49
- */
50
- public function __construct(
51
- array $queue = null,
52
- callable $onFulfilled = null,
53
- callable $onRejected = null
54
- ) {
55
- $this->onFulfilled = $onFulfilled;
56
- $this->onRejected = $onRejected;
57
-
58
- if ($queue) {
59
- call_user_func_array([$this, 'append'], $queue);
60
- }
61
- }
62
-
63
- public function __invoke(RequestInterface $request, array $options)
64
- {
65
- if (!$this->queue) {
66
- throw new \OutOfBoundsException('Mock queue is empty');
67
- }
68
-
69
- if (isset($options['delay'])) {
70
- usleep($options['delay'] * 1000);
71
- }
72
-
73
- $this->lastRequest = $request;
74
- $this->lastOptions = $options;
75
- $response = array_shift($this->queue);
76
-
77
- if (isset($options['on_headers'])) {
78
- if (!is_callable($options['on_headers'])) {
79
- throw new \InvalidArgumentException('on_headers must be callable');
80
- }
81
- try {
82
- $options['on_headers']($response);
83
- } catch (\Exception $e) {
84
- $msg = 'An error was encountered during the on_headers event';
85
- $response = new RequestException($msg, $request, $response, $e);
86
- }
87
- }
88
-
89
- if (is_callable($response)) {
90
- $response = call_user_func($response, $request, $options);
91
- }
92
-
93
- $response = $response instanceof \Exception
94
- ? \GuzzleHttp\Promise\rejection_for($response)
95
- : \GuzzleHttp\Promise\promise_for($response);
96
-
97
- return $response->then(
98
- function ($value) use ($request, $options) {
99
- $this->invokeStats($request, $options, $value);
100
- if ($this->onFulfilled) {
101
- call_user_func($this->onFulfilled, $value);
102
- }
103
- if (isset($options['sink'])) {
104
- $contents = (string) $value->getBody();
105
- $sink = $options['sink'];
106
-
107
- if (is_resource($sink)) {
108
- fwrite($sink, $contents);
109
- } elseif (is_string($sink)) {
110
- file_put_contents($sink, $contents);
111
- } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
112
- $sink->write($contents);
113
- }
114
- }
115
-
116
- return $value;
117
- },
118
- function ($reason) use ($request, $options) {
119
- $this->invokeStats($request, $options, null, $reason);
120
- if ($this->onRejected) {
121
- call_user_func($this->onRejected, $reason);
122
- }
123
- return \GuzzleHttp\Promise\rejection_for($reason);
124
- }
125
- );
126
- }
127
-
128
- /**
129
- * Adds one or more variadic requests, exceptions, callables, or promises
130
- * to the queue.
131
- */
132
- public function append()
133
- {
134
- foreach (func_get_args() as $value) {
135
- if ($value instanceof ResponseInterface
136
- || $value instanceof \Exception
137
- || $value instanceof PromiseInterface
138
- || is_callable($value)
139
- ) {
140
- $this->queue[] = $value;
141
- } else {
142
- throw new \InvalidArgumentException('Expected a response or '
143
- . 'exception. Found ' . \GuzzleHttp\describe_type($value));
144
- }
145
- }
146
- }
147
-
148
- /**
149
- * Get the last received request.
150
- *
151
- * @return RequestInterface
152
- */
153
- public function getLastRequest()
154
- {
155
- return $this->lastRequest;
156
- }
157
-
158
- /**
159
- * Get the last received request options.
160
- *
161
- * @return array
162
- */
163
- public function getLastOptions()
164
- {
165
- return $this->lastOptions;
166
- }
167
-
168
- /**
169
- * Returns the number of remaining items in the queue.
170
- *
171
- * @return int
172
- */
173
- public function count()
174
- {
175
- return count($this->queue);
176
- }
177
-
178
- private function invokeStats(
179
- RequestInterface $request,
180
- array $options,
181
- ResponseInterface $response = null,
182
- $reason = null
183
- ) {
184
- if (isset($options['on_stats'])) {
185
- $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0;
186
- $stats = new TransferStats($request, $response, $transferTime, $reason);
187
- call_user_func($options['on_stats'], $stats);
188
- }
189
- }
190
- }
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp\Handler;
3
+
4
+ use GuzzleHttp\Exception\RequestException;
5
+ use GuzzleHttp\HandlerStack;
6
+ use GuzzleHttp\Promise\PromiseInterface;
7
+ use GuzzleHttp\Promise\RejectedPromise;
8
+ use GuzzleHttp\TransferStats;
9
+ use Psr\Http\Message\RequestInterface;
10
+ use Psr\Http\Message\ResponseInterface;
11
+
12
+ /**
13
+ * Handler that returns responses or throw exceptions from a queue.
14
+ */
15
+ class MockHandler implements \Countable
16
+ {
17
+ private $queue = [];
18
+ private $lastRequest;
19
+ private $lastOptions;
20
+ private $onFulfilled;
21
+ private $onRejected;
22
+
23
+ /**
24
+ * Creates a new MockHandler that uses the default handler stack list of
25
+ * middlewares.
26
+ *
27
+ * @param array $queue Array of responses, callables, or exceptions.
28
+ * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
29
+ * @param callable $onRejected Callback to invoke when the return value is rejected.
30
+ *
31
+ * @return HandlerStack
32
+ */
33
+ public static function createWithMiddleware(
34
+ array $queue = null,
35
+ callable $onFulfilled = null,
36
+ callable $onRejected = null
37
+ ) {
38
+ return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
39
+ }
40
+
41
+ /**
42
+ * The passed in value must be an array of
43
+ * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
44
+ * callables, or Promises.
45
+ *
46
+ * @param array $queue
47
+ * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
48
+ * @param callable $onRejected Callback to invoke when the return value is rejected.
49
+ */
50
+ public function __construct(
51
+ array $queue = null,
52
+ callable $onFulfilled = null,
53
+ callable $onRejected = null
54
+ ) {
55
+ $this->onFulfilled = $onFulfilled;
56
+ $this->onRejected = $onRejected;
57
+
58
+ if ($queue) {
59
+ call_user_func_array([$this, 'append'], $queue);
60
+ }
61
+ }
62
+
63
+ public function __invoke(RequestInterface $request, array $options)
64
+ {
65
+ if (!$this->queue) {
66
+ throw new \OutOfBoundsException('Mock queue is empty');
67
+ }
68
+
69
+ if (isset($options['delay']) && is_numeric($options['delay'])) {
70
+ usleep($options['delay'] * 1000);
71
+ }
72
+
73
+ $this->lastRequest = $request;
74
+ $this->lastOptions = $options;
75
+ $response = array_shift($this->queue);
76
+
77
+ if (isset($options['on_headers'])) {
78
+ if (!is_callable($options['on_headers'])) {
79
+ throw new \InvalidArgumentException('on_headers must be callable');
80
+ }
81
+ try {
82
+ $options['on_headers']($response);
83
+ } catch (\Exception $e) {
84
+ $msg = 'An error was encountered during the on_headers event';
85
+ $response = new RequestException($msg, $request, $response, $e);
86
+ }
87
+ }
88
+
89
+ if (is_callable($response)) {
90
+ $response = call_user_func($response, $request, $options);
91
+ }
92
+
93
+ $response = $response instanceof \Exception
94
+ ? \GuzzleHttp\Promise\rejection_for($response)
95
+ : \GuzzleHttp\Promise\promise_for($response);
96
+
97
+ return $response->then(
98
+ function ($value) use ($request, $options) {
99
+ $this->invokeStats($request, $options, $value);
100
+ if ($this->onFulfilled) {
101
+ call_user_func($this->onFulfilled, $value);
102
+ }
103
+ if (isset($options['sink'])) {
104
+ $contents = (string) $value->getBody();
105
+ $sink = $options['sink'];
106
+
107
+ if (is_resource($sink)) {
108
+ fwrite($sink, $contents);
109
+ } elseif (is_string($sink)) {
110
+ file_put_contents($sink, $contents);
111
+ } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
112
+ $sink->write($contents);
113
+ }
114
+ }
115
+
116
+ return $value;
117
+ },
118
+ function ($reason) use ($request, $options) {
119
+ $this->invokeStats($request, $options, null, $reason);
120
+ if ($this->onRejected) {
121
+ call_user_func($this->onRejected, $reason);
122
+ }
123
+ return \GuzzleHttp\Promise\rejection_for($reason);
124
+ }
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Adds one or more variadic requests, exceptions, callables, or promises
130
+ * to the queue.
131
+ */
132
+ public function append()
133
+ {
134
+ foreach (func_get_args() as $value) {
135
+ if ($value instanceof ResponseInterface
136
+ || $value instanceof \Exception
137
+ || $value instanceof PromiseInterface
138
+ || is_callable($value)
139
+ ) {
140
+ $this->queue[] = $value;
141
+ } else {
142
+ throw new \InvalidArgumentException('Expected a response or '
143
+ . 'exception. Found ' . \GuzzleHttp\describe_type($value));
144
+ }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Get the last received request.
150
+ *
151
+ * @return RequestInterface
152
+ */
153
+ public function getLastRequest()
154
+ {
155
+ return $this->lastRequest;
156
+ }
157
+
158
+ /**
159
+ * Get the last received request options.
160
+ *
161
+ * @return array
162
+ */
163
+ public function getLastOptions()
164
+ {
165
+ return $this->lastOptions;
166
+ }
167
+
168
+ /**
169
+ * Returns the number of remaining items in the queue.
170
+ *
171
+ * @return int
172
+ */
173
+ public function count()
174
+ {
175
+ return count($this->queue);
176
+ }
177
+
178
+ public function reset()
179
+ {
180
+ $this->queue = [];
181
+ }
182
+
183
+ private function invokeStats(
184
+ RequestInterface $request,
185
+ array $options,
186
+ ResponseInterface $response = null,
187
+ $reason = null
188
+ ) {
189
+ if (isset($options['on_stats'])) {
190
+ $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0;
191
+ $stats = new TransferStats($request, $response, $transferTime, $reason);
192
+ call_user_func($options['on_stats'], $stats);
193
+ }
194
+ }
195
+ }
vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php CHANGED
@@ -1,544 +1,544 @@
1
- <?php
2
- namespace GuzzleHttp\Handler;
3
-
4
- use GuzzleHttp\Exception\RequestException;
5
- use GuzzleHttp\Exception\ConnectException;
6
- use GuzzleHttp\Promise\FulfilledPromise;
7
- use GuzzleHttp\Promise\PromiseInterface;
8
- use GuzzleHttp\Psr7;
9
- use GuzzleHttp\TransferStats;
10
- use Psr\Http\Message\RequestInterface;
11
- use Psr\Http\Message\ResponseInterface;
12
- use Psr\Http\Message\StreamInterface;
13
-
14
- /**
15
- * HTTP handler that uses PHP's HTTP stream wrapper.
16
- */
17
- class StreamHandler
18
- {
19
- private $lastHeaders = [];
20
-
21
- /**
22
- * Sends an HTTP request.
23
- *
24
- * @param RequestInterface $request Request to send.
25
- * @param array $options Request transfer options.
26
- *
27
- * @return PromiseInterface
28
- */
29
- public function __invoke(RequestInterface $request, array $options)
30
- {
31
- // Sleep if there is a delay specified.
32
- if (isset($options['delay'])) {
33
- usleep($options['delay'] * 1000);
34
- }
35
-
36
- $startTime = isset($options['on_stats']) ? \GuzzleHttp\_current_time() : null;
37
-
38
- try {
39
- // Does not support the expect header.
40
- $request = $request->withoutHeader('Expect');
41
-
42
- // Append a content-length header if body size is zero to match
43
- // cURL's behavior.
44
- if (0 === $request->getBody()->getSize()) {
45
- $request = $request->withHeader('Content-Length', '0');
46
- }
47
-
48
- return $this->createResponse(
49
- $request,
50
- $options,
51
- $this->createStream($request, $options),
52
- $startTime
53
- );
54
- } catch (\InvalidArgumentException $e) {
55
- throw $e;
56
- } catch (\Exception $e) {
57
- // Determine if the error was a networking error.
58
- $message = $e->getMessage();
59
- // This list can probably get more comprehensive.
60
- if (strpos($message, 'getaddrinfo') // DNS lookup failed
61
- || strpos($message, 'Connection refused')
62
- || strpos($message, "couldn't connect to host") // error on HHVM
63
- || strpos($message, "connection attempt failed")
64
- ) {
65
- $e = new ConnectException($e->getMessage(), $request, $e);
66
- }
67
- $e = RequestException::wrapException($request, $e);
68
- $this->invokeStats($options, $request, $startTime, null, $e);
69
-
70
- return \GuzzleHttp\Promise\rejection_for($e);
71
- }
72
- }
73
-
74
- private function invokeStats(
75
- array $options,
76
- RequestInterface $request,
77
- $startTime,
78
- ResponseInterface $response = null,
79
- $error = null
80
- ) {
81
- if (isset($options['on_stats'])) {
82
- $stats = new TransferStats(
83
- $request,
84
- $response,
85
- \GuzzleHttp\_current_time() - $startTime,
86
- $error,
87
- []
88
- );
89
- call_user_func($options['on_stats'], $stats);
90
- }
91
- }
92
-
93
- private function createResponse(
94
- RequestInterface $request,
95
- array $options,
96
- $stream,
97
- $startTime
98
- ) {
99
- $hdrs = $this->lastHeaders;
100
- $this->lastHeaders = [];
101
- $parts = explode(' ', array_shift($hdrs), 3);
102
- $ver = explode('/', $parts[0])[1];
103
- $status = $parts[1];
104
- $reason = isset($parts[2]) ? $parts[2] : null;
105
- $headers = \GuzzleHttp\headers_from_lines($hdrs);
106
- list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
107
- $stream = Psr7\stream_for($stream);
108
- $sink = $stream;
109
-
110
- if (strcasecmp('HEAD', $request->getMethod())) {
111
- $sink = $this->createSink($stream, $options);
112
- }
113
-
114
- $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
115
-
116
- if (isset($options['on_headers'])) {
117
- try {
118
- $options['on_headers']($response);
119
- } catch (\Exception $e) {
120
- $msg = 'An error was encountered during the on_headers event';
121
- $ex = new RequestException($msg, $request, $response, $e);
122
- return \GuzzleHttp\Promise\rejection_for($ex);
123
- }
124
- }
125
-
126
- // Do not drain when the request is a HEAD request because they have
127
- // no body.
128
- if ($sink !== $stream) {
129
- $this->drain(
130
- $stream,
131
- $sink,
132
- $response->getHeaderLine('Content-Length')
133
- );
134
- }
135
-
136
- $this->invokeStats($options, $request, $startTime, $response, null);
137
-
138
- return new FulfilledPromise($response);
139
- }
140
-
141
- private function createSink(StreamInterface $stream, array $options)
142
- {
143
- if (!empty($options['stream'])) {
144
- return $stream;
145
- }
146
-
147
- $sink = isset($options['sink'])
148
- ? $options['sink']
149
- : fopen('php://temp', 'r+');
150
-
151
- return is_string($sink)
152
- ? new Psr7\LazyOpenStream($sink, 'w+')
153
- : Psr7\stream_for($sink);
154
- }
155
-
156
- private function checkDecode(array $options, array $headers, $stream)
157
- {
158
- // Automatically decode responses when instructed.
159
- if (!empty($options['decode_content'])) {
160
- $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
161
- if (isset($normalizedKeys['content-encoding'])) {
162
- $encoding = $headers[$normalizedKeys['content-encoding']];
163
- if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
164
- $stream = new Psr7\InflateStream(
165
- Psr7\stream_for($stream)
166
- );
167
- $headers['x-encoded-content-encoding']
168
- = $headers[$normalizedKeys['content-encoding']];
169
- // Remove content-encoding header
170
- unset($headers[$normalizedKeys['content-encoding']]);
171
- // Fix content-length header
172
- if (isset($normalizedKeys['content-length'])) {
173
- $headers['x-encoded-content-length']
174
- = $headers[$normalizedKeys['content-length']];
175
-
176
- $length = (int) $stream->getSize();
177
- if ($length === 0) {
178
- unset($headers[$normalizedKeys['content-length']]);
179
- } else {
180
- $headers[$normalizedKeys['content-length']] = [$length];
181
- }
182
- }
183
- }
184
- }
185
- }
186
-
187
- return [$stream, $headers];
188
- }
189
-
190
- /**
191
- * Drains the source stream into the "sink" client option.
192
- *
193
- * @param StreamInterface $source
194
- * @param StreamInterface $sink
195
- * @param string $contentLength Header specifying the amount of
196
- * data to read.
197
- *
198
- * @return StreamInterface
199
- * @throws \RuntimeException when the sink option is invalid.
200
- */
201
- private function drain(
202
- StreamInterface $source,
203
- StreamInterface $sink,
204
- $contentLength
205
- ) {
206
- // If a content-length header is provided, then stop reading once
207
- // that number of bytes has been read. This can prevent infinitely
208
- // reading from a stream when dealing with servers that do not honor
209
- // Connection: Close headers.
210
- Psr7\copy_to_stream(
211
- $source,
212
- $sink,
213
- (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
214
- );
215
-
216
- $sink->seek(0);
217
- $source->close();
218
-
219
- return $sink;
220
- }
221
-
222
- /**
223
- * Create a resource and check to ensure it was created successfully
224
- *
225
- * @param callable $callback Callable that returns stream resource
226
- *
227
- * @return resource
228
- * @throws \RuntimeException on error
229
- */
230
- private function createResource(callable $callback)
231
- {
232
- $errors = null;
233
- set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
234
- $errors[] = [
235
- 'message' => $msg,
236
- 'file' => $file,
237
- 'line' => $line
238
- ];
239
- return true;
240
- });
241
-
242
- $resource = $callback();
243
- restore_error_handler();
244
-
245
- if (!$resource) {
246
- $message = 'Error creating resource: ';
247
- foreach ($errors as $err) {
248
- foreach ($err as $key => $value) {
249
- $message .= "[$key] $value" . PHP_EOL;
250
- }
251
- }
252
- throw new \RuntimeException(trim($message));
253
- }
254
-
255
- return $resource;
256
- }
257
-
258
- private function createStream(RequestInterface $request, array $options)
259
- {
260
- static $methods;
261
- if (!$methods) {
262
- $methods = array_flip(get_class_methods(__CLASS__));
263
- }
264
-
265
- // HTTP/1.1 streams using the PHP stream wrapper require a
266
- // Connection: close header
267
- if ($request->getProtocolVersion() == '1.1'
268
- && !$request->hasHeader('Connection')
269
- ) {
270
- $request = $request->withHeader('Connection', 'close');
271
- }
272
-
273
- // Ensure SSL is verified by default
274
- if (!isset($options['verify'])) {
275
- $options['verify'] = true;
276
- }
277
-
278
- $params = [];
279
- $context = $this->getDefaultContext($request);
280
-
281
- if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
282
- throw new \InvalidArgumentException('on_headers must be callable');
283
- }
284
-
285
- if (!empty($options)) {
286
- foreach ($options as $key => $value) {
287
- $method = "add_{$key}";
288
- if (isset($methods[$method])) {
289
- $this->{$method}($request, $context, $value, $params);
290
- }
291
- }
292
- }
293
-
294
- if (isset($options['stream_context'])) {
295
- if (!is_array($options['stream_context'])) {
296
- throw new \InvalidArgumentException('stream_context must be an array');
297
- }
298
- $context = array_replace_recursive(
299
- $context,
300
- $options['stream_context']
301
- );
302
- }
303
-
304
- // Microsoft NTLM authentication only supported with curl handler
305
- if (isset($options['auth'])
306
- && is_array($options['auth'])
307
- && isset($options['auth'][2])
308
- && 'ntlm' == $options['auth'][2]
309
- ) {
310
- throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
311
- }
312
-
313
- $uri = $this->resolveHost($request, $options);
314
-
315
- $context = $this->createResource(
316
- function () use ($context, $params) {
317
- return stream_context_create($context, $params);
318
- }
319
- );
320
-
321
- return $this->createResource(
322
- function () use ($uri, &$http_response_header, $context, $options) {
323
- $resource = fopen((string) $uri, 'r', null, $context);
324
- $this->lastHeaders = $http_response_header;
325
-
326
- if (isset($options['read_timeout'])) {
327
- $readTimeout = $options['read_timeout'];
328
- $sec = (int) $readTimeout;
329
- $usec = ($readTimeout - $sec) * 100000;
330
- stream_set_timeout($resource, $sec, $usec);
331
- }
332
-
333
- return $resource;
334
- }
335
- );
336
- }
337
-
338
- private function resolveHost(RequestInterface $request, array $options)
339
- {
340
- $uri = $request->getUri();
341
-
342
- if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
343
- if ('v4' === $options['force_ip_resolve']) {
344
- $records = dns_get_record($uri->getHost(), DNS_A);
345
- if (!isset($records[0]['ip'])) {
346
- throw new ConnectException(
347
- sprintf(
348
- "Could not resolve IPv4 address for host '%s'",
349
- $uri->getHost()
350
- ),
351
- $request
352
- );
353
- }
354
- $uri = $uri->withHost($records[0]['ip']);
355
- } elseif ('v6' === $options['force_ip_resolve']) {
356
- $records = dns_get_record($uri->getHost(), DNS_AAAA);
357
- if (!isset($records[0]['ipv6'])) {
358
- throw new ConnectException(
359
- sprintf(
360
- "Could not resolve IPv6 address for host '%s'",
361
- $uri->getHost()
362
- ),
363
- $request
364
- );
365
- }
366
- $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
367
- }
368
- }
369
-
370
- return $uri;
371
- }
372
-
373
- private function getDefaultContext(RequestInterface $request)
374
- {
375
- $headers = '';
376
- foreach ($request->getHeaders() as $name => $value) {
377
- foreach ($value as $val) {
378
- $headers .= "$name: $val\r\n";
379
- }
380
- }
381
-
382
- $context = [
383
- 'http' => [
384
- 'method' => $request->getMethod(),
385
- 'header' => $headers,
386
- 'protocol_version' => $request->getProtocolVersion(),
387
- 'ignore_errors' => true,
388
- 'follow_location' => 0,
389
- ],
390
- ];
391
-
392
- $body = (string) $request->getBody();
393
-
394
- if (!empty($body)) {
395
- $context['http']['content'] = $body;
396
- // Prevent the HTTP handler from adding a Content-Type header.
397
- if (!$request->hasHeader('Content-Type')) {
398
- $context['http']['header'] .= "Content-Type:\r\n";
399
- }
400
- }
401
-
402
- $context['http']['header'] = rtrim($context['http']['header']);
403
-
404
- return $context;
405
- }
406
-
407
- private function add_proxy(RequestInterface $request, &$options, $value, &$params)
408
- {
409
- if (!is_array($value)) {
410
- $options['http']['proxy'] = $value;
411
- } else {
412
- $scheme = $request->getUri()->getScheme();
413
- if (isset($value[$scheme])) {
414
- if (!isset($value['no'])
415
- || !\GuzzleHttp\is_host_in_noproxy(
416
- $request->getUri()->getHost(),
417
- $value['no']
418
- )
419
- ) {
420
- $options['http']['proxy'] = $value[$scheme];
421
- }
422
- }
423
- }
424
- }
425
-
426
- private function add_timeout(RequestInterface $request, &$options, $value, &$params)
427
- {
428
- if ($value > 0) {
429
- $options['http']['timeout'] = $value;
430
- }
431
- }
432
-
433
- private function add_verify(RequestInterface $request, &$options, $value, &$params)
434
- {
435
- if ($value === true) {
436
- // PHP 5.6 or greater will find the system cert by default. When
437
- // < 5.6, use the Guzzle bundled cacert.
438
- if (PHP_VERSION_ID < 50600) {
439
- $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
440
- }
441
- } elseif (is_string($value)) {
442
- $options['ssl']['cafile'] = $value;
443
- if (!file_exists($value)) {
444
- throw new \RuntimeException("SSL CA bundle not found: $value");
445
- }
446
- } elseif ($value === false) {
447
- $options['ssl']['verify_peer'] = false;
448
- $options['ssl']['verify_peer_name'] = false;
449
- return;
450
- } else {
451
- throw new \InvalidArgumentException('Invalid verify request option');
452
- }
453
-
454
- $options['ssl']['verify_peer'] = true;
455
- $options['ssl']['verify_peer_name'] = true;
456
- $options['ssl']['allow_self_signed'] = false;
457
- }
458
-
459
- private function add_cert(RequestInterface $request, &$options, $value, &$params)
460
- {
461
- if (is_array($value)) {
462
- $options['ssl']['passphrase'] = $value[1];
463
- $value = $value[0];
464
- }
465
-
466
- if (!file_exists($value)) {
467
- throw new \RuntimeException("SSL certificate not found: {$value}");
468
- }
469
-
470
- $options['ssl']['local_cert'] = $value;
471
- }
472
-
473
- private function add_progress(RequestInterface $request, &$options, $value, &$params)
474
- {
475
- $this->addNotification(
476
- $params,
477
- function ($code, $a, $b, $c, $transferred, $total) use ($value) {
478
- if ($code == STREAM_NOTIFY_PROGRESS) {
479
- $value($total, $transferred, null, null);
480
- }
481
- }
482
- );
483
- }
484
-
485
- private function add_debug(RequestInterface $request, &$options, $value, &$params)
486
- {
487
- if ($value === false) {
488
- return;
489
- }
490
-
491
- static $map = [
492
- STREAM_NOTIFY_CONNECT => 'CONNECT',
493
- STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
494
- STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
495
- STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
496
- STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
497
- STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
498
- STREAM_NOTIFY_PROGRESS => 'PROGRESS',
499
- STREAM_NOTIFY_FAILURE => 'FAILURE',
500
- STREAM_NOTIFY_COMPLETED => 'COMPLETED',
501
- STREAM_NOTIFY_RESOLVE => 'RESOLVE',
502
- ];
503
- static $args = ['severity', 'message', 'message_code',
504
- 'bytes_transferred', 'bytes_max'];
505
-
506
- $value = \GuzzleHttp\debug_resource($value);
507
- $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
508
- $this->addNotification(
509
- $params,
510
- function () use ($ident, $value, $map, $args) {
511
- $passed = func_get_args();
512
- $code = array_shift($passed);
513
- fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
514
- foreach (array_filter($passed) as $i => $v) {
515
- fwrite($value, $args[$i] . ': "' . $v . '" ');
516
- }
517
- fwrite($value, "\n");
518
- }
519
- );
520
- }
521
-
522
- private function addNotification(array &$params, callable $notify)
523
- {
524
- // Wrap the existing function if needed.
525
- if (!isset($params['notification'])) {
526
- $params['notification'] = $notify;
527
- } else {
528
- $params['notification'] = $this->callArray([
529
- $params['notification'],
530
- $notify
531
- ]);
532
- }
533
- }
534
-
535
- private function callArray(array $functions)
536
- {
537
- return function () use ($functions) {
538
- $args = func_get_args();
539
- foreach ($functions as $fn) {
540
- call_user_func_array($fn, $args);
541
- }
542
- };
543
- }
544
- }
1
+ <?php
2
+ namespace GuzzleHttp\Handler;
3
+
4
+ use GuzzleHttp\Exception\ConnectException;
5
+ use GuzzleHttp\Exception\RequestException;
6
+ use GuzzleHttp\Promise\FulfilledPromise;
7
+ use GuzzleHttp\Promise\PromiseInterface;
8
+ use GuzzleHttp\Psr7;
9
+ use GuzzleHttp\TransferStats;
10
+ use Psr\Http\Message\RequestInterface;
11
+ use Psr\Http\Message\ResponseInterface;
12
+ use Psr\Http\Message\StreamInterface;
13
+
14
+ /**
15
+ * HTTP handler that uses PHP's HTTP stream wrapper.
16
+ */
17
+ class StreamHandler
18
+ {
19
+ private $lastHeaders = [];
20
+
21
+ /**
22
+ * Sends an HTTP request.
23
+ *
24
+ * @param RequestInterface $request Request to send.
25
+ * @param array $options Request transfer options.
26
+ *
27
+ * @return PromiseInterface
28
+ */
29
+ public function __invoke(RequestInterface $request, array $options)
30
+ {
31
+ // Sleep if there is a delay specified.
32
+ if (isset($options['delay'])) {
33
+ usleep($options['delay'] * 1000);
34
+ }
35
+
36
+ $startTime = isset($options['on_stats']) ? \GuzzleHttp\_current_time() : null;
37
+
38
+ try {
39
+ // Does not support the expect header.
40
+ $request = $request->withoutHeader('Expect');
41
+
42
+ // Append a content-length header if body size is zero to match
43
+ // cURL's behavior.
44
+ if (0 === $request->getBody()->getSize()) {
45
+ $request = $request->withHeader('Content-Length', '0');
46
+ }
47
+
48
+ return $this->createResponse(
49
+ $request,
50
+ $options,
51
+ $this->createStream($request, $options),
52
+ $startTime
53
+ );
54
+ } catch (\InvalidArgumentException $e) {
55
+ throw $e;
56
+ } catch (\Exception $e) {
57
+ // Determine if the error was a networking error.
58
+ $message = $e->getMessage();
59
+ // This list can probably get more comprehensive.
60
+ if (strpos($message, 'getaddrinfo') // DNS lookup failed
61
+ || strpos($message, 'Connection refused')
62
+ || strpos($message, "couldn't connect to host") // error on HHVM
63
+ || strpos($message, "connection attempt failed")
64
+ ) {
65
+ $e = new ConnectException($e->getMessage(), $request, $e);
66
+ }
67
+ $e = RequestException::wrapException($request, $e);
68
+ $this->invokeStats($options, $request, $startTime, null, $e);
69
+
70
+ return \GuzzleHttp\Promise\rejection_for($e);
71
+ }
72
+ }
73
+
74
+ private function invokeStats(
75
+ array $options,
76
+ RequestInterface $request,
77
+ $startTime,
78
+ ResponseInterface $response = null,
79
+ $error = null
80
+ ) {
81
+ if (isset($options['on_stats'])) {
82
+ $stats = new TransferStats(
83
+ $request,
84
+ $response,
85
+ \GuzzleHttp\_current_time() - $startTime,
86
+ $error,
87
+ []
88
+ );
89
+ call_user_func($options['on_stats'], $stats);
90
+ }
91
+ }
92
+
93
+ private function createResponse(
94
+ RequestInterface $request,
95
+ array $options,
96
+ $stream,
97
+ $startTime
98
+ ) {
99
+ $hdrs = $this->lastHeaders;
100
+ $this->lastHeaders = [];
101
+ $parts = explode(' ', array_shift($hdrs), 3);
102
+ $ver = explode('/', $parts[0])[1];
103
+ $status = $parts[1];
104
+ $reason = isset($parts[2]) ? $parts[2] : null;
105
+ $headers = \GuzzleHttp\headers_from_lines($hdrs);
106
+ list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
107
+ $stream = Psr7\stream_for($stream);
108
+ $sink = $stream;
109
+
110
+ if (strcasecmp('HEAD', $request->getMethod())) {
111
+ $sink = $this->createSink($stream, $options);
112
+ }
113
+
114
+ $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
115
+
116
+ if (isset($options['on_headers'])) {
117
+ try {
118
+ $options['on_headers']($response);
119
+ } catch (\Exception $e) {
120
+ $msg = 'An error was encountered during the on_headers event';
121
+ $ex = new RequestException($msg, $request, $response, $e);
122
+ return \GuzzleHttp\Promise\rejection_for($ex);
123
+ }
124
+ }
125
+
126
+ // Do not drain when the request is a HEAD request because they have
127
+ // no body.
128
+ if ($sink !== $stream) {
129
+ $this->drain(
130
+ $stream,
131
+ $sink,
132
+ $response->getHeaderLine('Content-Length')
133
+ );
134
+ }
135
+
136
+ $this->invokeStats($options, $request, $startTime, $response, null);
137
+
138
+ return new FulfilledPromise($response);
139
+ }
140
+
141
+ private function createSink(StreamInterface $stream, array $options)
142
+ {
143
+ if (!empty($options['stream'])) {
144
+ return $stream;
145
+ }
146
+
147
+ $sink = isset($options['sink'])
148
+ ? $options['sink']
149
+ : fopen('php://temp', 'r+');
150
+
151
+ return is_string($sink)
152
+ ? new Psr7\LazyOpenStream($sink, 'w+')
153
+ : Psr7\stream_for($sink);
154
+ }
155
+
156
+ private function checkDecode(array $options, array $headers, $stream)
157
+ {
158
+ // Automatically decode responses when instructed.
159
+ if (!empty($options['decode_content'])) {
160
+ $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
161
+ if (isset($normalizedKeys['content-encoding'])) {
162
+ $encoding = $headers[$normalizedKeys['content-encoding']];
163
+ if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
164
+ $stream = new Psr7\InflateStream(
165
+ Psr7\stream_for($stream)
166
+ );
167
+ $headers['x-encoded-content-encoding']
168
+ = $headers[$normalizedKeys['content-encoding']];
169
+ // Remove content-encoding header
170
+ unset($headers[$normalizedKeys['content-encoding']]);
171
+ // Fix content-length header
172
+ if (isset($normalizedKeys['content-length'])) {
173
+ $headers['x-encoded-content-length']
174
+ = $headers[$normalizedKeys['content-length']];
175
+
176
+ $length = (int) $stream->getSize();
177
+ if ($length === 0) {
178
+ unset($headers[$normalizedKeys['content-length']]);
179
+ } else {
180
+ $headers[$normalizedKeys['content-length']] = [$length];
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ return [$stream, $headers];
188
+ }
189
+
190
+ /**
191
+ * Drains the source stream into the "sink" client option.
192
+ *
193
+ * @param StreamInterface $source
194
+ * @param StreamInterface $sink
195
+ * @param string $contentLength Header specifying the amount of
196
+ * data to read.
197
+ *
198
+ * @return StreamInterface
199
+ * @throws \RuntimeException when the sink option is invalid.
200
+ */
201
+ private function drain(
202
+ StreamInterface $source,
203
+ StreamInterface $sink,
204
+ $contentLength
205
+ ) {
206
+ // If a content-length header is provided, then stop reading once
207
+ // that number of bytes has been read. This can prevent infinitely
208
+ // reading from a stream when dealing with servers that do not honor
209
+ // Connection: Close headers.
210
+ Psr7\copy_to_stream(
211
+ $source,
212
+ $sink,
213
+ (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
214
+ );
215
+
216
+ $sink->seek(0);
217
+ $source->close();
218
+
219
+ return $sink;
220
+ }
221
+
222
+ /**
223
+ * Create a resource and check to ensure it was created successfully
224
+ *
225
+ * @param callable $callback Callable that returns stream resource
226
+ *
227
+ * @return resource
228
+ * @throws \RuntimeException on error
229
+ */
230
+ private function createResource(callable $callback)
231
+ {
232
+ $errors = null;
233
+ set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
234
+ $errors[] = [
235
+ 'message' => $msg,
236
+ 'file' => $file,
237
+ 'line' => $line
238
+ ];
239
+ return true;
240
+ });
241
+
242
+ $resource = $callback();
243
+ restore_error_handler();
244
+
245
+ if (!$resource) {
246
+ $message = 'Error creating resource: ';
247
+ foreach ($errors as $err) {
248
+ foreach ($err as $key => $value) {
249
+ $message .= "[$key] $value" . PHP_EOL;
250
+ }
251
+ }
252
+ throw new \RuntimeException(trim($message));
253
+ }
254
+
255
+ return $resource;
256
+ }
257
+
258
+ private function createStream(RequestInterface $request, array $options)
259
+ {
260
+ static $methods;
261
+ if (!$methods) {
262
+ $methods = array_flip(get_class_methods(__CLASS__));
263
+ }
264
+
265
+ // HTTP/1.1 streams using the PHP stream wrapper require a
266
+ // Connection: close header
267
+ if ($request->getProtocolVersion() == '1.1'
268
+ && !$request->hasHeader('Connection')
269
+ ) {
270
+ $request = $request->withHeader('Connection', 'close');
271
+ }
272
+
273
+ // Ensure SSL is verified by default
274
+ if (!isset($options['verify'])) {
275
+ $options['verify'] = true;
276
+ }
277
+
278
+ $params = [];
279
+ $context = $this->getDefaultContext($request);
280
+
281
+ if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
282
+ throw new \InvalidArgumentException('on_headers must be callable');
283
+ }
284
+
285
+ if (!empty($options)) {
286
+ foreach ($options as $key => $value) {
287
+ $method = "add_{$key}";
288
+ if (isset($methods[$method])) {
289
+ $this->{$method}($request, $context, $value, $params);
290
+ }
291
+ }
292
+ }
293
+
294
+ if (isset($options['stream_context'])) {
295
+ if (!is_array($options['stream_context'])) {
296
+ throw new \InvalidArgumentException('stream_context must be an array');
297
+ }
298
+ $context = array_replace_recursive(
299
+ $context,
300
+ $options['stream_context']
301
+ );
302
+ }
303
+
304
+ // Microsoft NTLM authentication only supported with curl handler
305
+ if (isset($options['auth'])
306
+ && is_array($options['auth'])
307
+ && isset($options['auth'][2])
308
+ && 'ntlm' == $options['auth'][2]
309
+ ) {
310
+ throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
311
+ }
312
+
313
+ $uri = $this->resolveHost($request, $options);
314
+
315
+ $context = $this->createResource(
316
+ function () use ($context, $params) {
317
+ return stream_context_create($context, $params);
318
+ }
319
+ );
320
+
321
+ return $this->createResource(
322
+ function () use ($uri, &$http_response_header, $context, $options) {
323
+ $resource = fopen((string) $uri, 'r', null, $context);
324
+ $this->lastHeaders = $http_response_header;
325
+
326
+ if (isset($options['read_timeout'])) {
327
+ $readTimeout = $options['read_timeout'];
328
+ $sec = (int) $readTimeout;
329
+ $usec = ($readTimeout - $sec) * 100000;
330
+ stream_set_timeout($resource, $sec, $usec);
331
+ }
332
+
333
+ return $resource;
334
+ }
335
+ );
336
+ }
337
+
338
+ private function resolveHost(RequestInterface $request, array $options)
339
+ {
340
+ $uri = $request->getUri();
341
+
342
+ if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
343
+ if ('v4' === $options['force_ip_resolve']) {
344
+ $records = dns_get_record($uri->getHost(), DNS_A);
345
+ if (!isset($records[0]['ip'])) {
346
+ throw new ConnectException(
347
+ sprintf(
348
+ "Could not resolve IPv4 address for host '%s'",
349
+ $uri->getHost()
350
+ ),
351
+ $request
352
+ );
353
+ }
354
+ $uri = $uri->withHost($records[0]['ip']);
355
+ } elseif ('v6' === $options['force_ip_resolve']) {
356
+ $records = dns_get_record($uri->getHost(), DNS_AAAA);
357
+ if (!isset($records[0]['ipv6'])) {
358
+ throw new ConnectException(
359
+ sprintf(
360
+ "Could not resolve IPv6 address for host '%s'",
361
+ $uri->getHost()
362
+ ),
363
+ $request
364
+ );
365
+ }
366
+ $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
367
+ }
368
+ }
369
+
370
+ return $uri;
371
+ }
372
+
373
+ private function getDefaultContext(RequestInterface $request)
374
+ {
375
+ $headers = '';
376
+ foreach ($request->getHeaders() as $name => $value) {
377
+ foreach ($value as $val) {
378
+ $headers .= "$name: $val\r\n";
379
+ }
380
+ }
381
+
382
+ $context = [
383
+ 'http' => [
384
+ 'method' => $request->getMethod(),
385
+ 'header' => $headers,
386
+ 'protocol_version' => $request->getProtocolVersion(),
387
+ 'ignore_errors' => true,
388
+ 'follow_location' => 0,
389
+ ],
390
+ ];
391
+
392
+ $body = (string) $request->getBody();
393
+
394
+ if (!empty($body)) {
395
+ $context['http']['content'] = $body;
396
+ // Prevent the HTTP handler from adding a Content-Type header.
397
+ if (!$request->hasHeader('Content-Type')) {
398
+ $context['http']['header'] .= "Content-Type:\r\n";
399
+ }
400
+ }
401
+
402
+ $context['http']['header'] = rtrim($context['http']['header']);
403
+
404
+ return $context;
405
+ }
406
+
407
+ private function add_proxy(RequestInterface $request, &$options, $value, &$params)
408
+ {
409
+ if (!is_array($value)) {
410
+ $options['http']['proxy'] = $value;
411
+ } else {
412
+ $scheme = $request->getUri()->getScheme();
413
+ if (isset($value[$scheme])) {
414
+ if (!isset($value['no'])
415
+ || !\GuzzleHttp\is_host_in_noproxy(
416
+ $request->getUri()->getHost(),
417
+ $value['no']
418
+ )
419
+ ) {
420
+ $options['http']['proxy'] = $value[$scheme];
421
+ }
422
+ }
423
+ }
424
+ }
425
+
426
+ private function add_timeout(RequestInterface $request, &$options, $value, &$params)
427
+ {
428
+ if ($value > 0) {
429
+ $options['http']['timeout'] = $value;
430
+ }
431
+ }
432
+
433
+ private function add_verify(RequestInterface $request, &$options, $value, &$params)
434
+ {
435
+ if ($value === true) {
436
+ // PHP 5.6 or greater will find the system cert by default. When
437
+ // < 5.6, use the Guzzle bundled cacert.
438
+ if (PHP_VERSION_ID < 50600) {
439
+ $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
440
+ }
441
+ } elseif (is_string($value)) {
442
+ $options['ssl']['cafile'] = $value;
443
+ if (!file_exists($value)) {
444
+ throw new \RuntimeException("SSL CA bundle not found: $value");
445
+ }
446
+ } elseif ($value === false) {
447
+ $options['ssl']['verify_peer'] = false;
448
+ $options['ssl']['verify_peer_name'] = false;
449
+ return;
450
+ } else {
451
+ throw new \InvalidArgumentException('Invalid verify request option');
452
+ }
453
+
454
+ $options['ssl']['verify_peer'] = true;
455
+ $options['ssl']['verify_peer_name'] = true;
456
+ $options['ssl']['allow_self_signed'] = false;
457
+ }
458
+
459
+ private function add_cert(RequestInterface $request, &$options, $value, &$params)
460
+ {
461
+ if (is_array($value)) {
462
+ $options['ssl']['passphrase'] = $value[1];
463
+ $value = $value[0];
464
+ }
465
+
466
+ if (!file_exists($value)) {
467
+ throw new \RuntimeException("SSL certificate not found: {$value}");
468
+ }
469
+
470
+ $options['ssl']['local_cert'] = $value;
471
+ }
472
+
473
+ private function add_progress(RequestInterface $request, &$options, $value, &$params)
474
+ {
475
+ $this->addNotification(
476
+ $params,
477
+ function ($code, $a, $b, $c, $transferred, $total) use ($value) {
478
+ if ($code == STREAM_NOTIFY_PROGRESS) {
479
+ $value($total, $transferred, null, null);
480
+ }
481
+ }
482
+ );
483
+ }
484
+
485
+ private function add_debug(RequestInterface $request, &$options, $value, &$params)
486
+ {
487
+ if ($value === false) {
488
+ return;
489
+ }
490
+
491
+ static $map = [
492
+ STREAM_NOTIFY_CONNECT => 'CONNECT',
493
+ STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
494
+ STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
495
+ STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
496
+ STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
497
+ STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
498
+ STREAM_NOTIFY_PROGRESS => 'PROGRESS',
499
+ STREAM_NOTIFY_FAILURE => 'FAILURE',
500
+ STREAM_NOTIFY_COMPLETED => 'COMPLETED',
501
+ STREAM_NOTIFY_RESOLVE => 'RESOLVE',
502
+ ];
503
+ static $args = ['severity', 'message', 'message_code',
504
+ 'bytes_transferred', 'bytes_max'];
505
+
506
+ $value = \GuzzleHttp\debug_resource($value);
507
+ $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
508
+ $this->addNotification(
509
+ $params,
510
+ function () use ($ident, $value, $map, $args) {
511
+ $passed = func_get_args();
512
+ $code = array_shift($passed);
513
+ fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
514
+ foreach (array_filter($passed) as $i => $v) {
515
+ fwrite($value, $args[$i] . ': "' . $v . '" ');
516
+ }
517
+ fwrite($value, "\n");
518
+ }
519
+ );
520
+ }
521
+
522
+ private function addNotification(array &$params, callable $notify)
523
+ {
524
+ // Wrap the existing function if needed.
525
+ if (!isset($params['notification'])) {
526
+ $params['notification'] = $notify;
527
+ } else {
528
+ $params['notification'] = $this->callArray([
529
+ $params['notification'],
530
+ $notify
531
+ ]);
532
+ }
533
+ }
534
+
535
+ private function callArray(array $functions)
536
+ {
537
+ return function () use ($functions) {
538
+ $args = func_get_args();
539
+ foreach ($functions as $fn) {
540
+ call_user_func_array($fn, $args);
541
+ }
542
+ };
543
+ }
544
+ }
vendor/guzzlehttp/guzzle/src/HandlerStack.php CHANGED
@@ -1,273 +1,277 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use Psr\Http\Message\RequestInterface;
5
-
6
- /**
7
- * Creates a composed Guzzle handler function by stacking middlewares on top of
8
- * an HTTP handler function.
9
- */
10
- class HandlerStack
11
- {
12
- /** @var callable */
13
- private $handler;
14
-
15
- /** @var array */
16
- private $stack = [];
17
-
18
- /** @var callable|null */
19
- private $cached;
20
-
21
- /**
22
- * Creates a default handler stack that can be used by clients.
23
- *
24
- * The returned handler will wrap the provided handler or use the most
25
- * appropriate default handler for your system. The returned HandlerStack has
26
- * support for cookies, redirects, HTTP error exceptions, and preparing a body
27
- * before sending.
28
- *
29
- * The returned handler stack can be passed to a client in the "handler"
30
- * option.
31
- *
32
- * @param callable $handler HTTP handler function to use with the stack. If no
33
- * handler is provided, the best handler for your
34
- * system will be utilized.
35
- *
36
- * @return HandlerStack
37
- */
38
- public static function create(callable $handler = null)
39
- {
40
- $stack = new self($handler ?: choose_handler());
41
- $stack->push(Middleware::httpErrors(), 'http_errors');
42
- $stack->push(Middleware::redirect(), 'allow_redirects');
43
- $stack->push(Middleware::cookies(), 'cookies');
44
- $stack->push(Middleware::prepareBody(), 'prepare_body');
45
-
46
- return $stack;
47
- }
48
-
49
- /**
50
- * @param callable $handler Underlying HTTP handler.
51
- */
52
- public function __construct(callable $handler = null)
53
- {
54
- $this->handler = $handler;
55
- }
56
-
57
- /**
58
- * Invokes the handler stack as a composed handler
59
- *
60
- * @param RequestInterface $request
61
- * @param array $options
62
- */
63
- public function __invoke(RequestInterface $request, array $options)
64
- {
65
- $handler = $this->resolve();
66
-
67
- return $handler($request, $options);
68
- }
69
-
70
- /**
71
- * Dumps a string representation of the stack.
72
- *
73
- * @return string
74
- */
75
- public function __toString()
76
- {
77
- $depth = 0;
78
- $stack = [];
79
- if ($this->handler) {
80
- $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
81
- }
82
-
83
- $result = '';
84
- foreach (array_reverse($this->stack) as $tuple) {
85
- $depth++;
86
- $str = "{$depth}) Name: '{$tuple[1]}', ";
87
- $str .= "Function: " . $this->debugCallable($tuple[0]);
88
- $result = "> {$str}\n{$result}";
89
- $stack[] = $str;
90
- }
91
-
92
- foreach (array_keys($stack) as $k) {
93
- $result .= "< {$stack[$k]}\n";
94
- }
95
-
96
- return $result;
97
- }
98
-
99
- /**
100
- * Set the HTTP handler that actually returns a promise.
101
- *
102
- * @param callable $handler Accepts a request and array of options and
103
- * returns a Promise.
104
- */
105
- public function setHandler(callable $handler)
106
- {
107
- $this->handler = $handler;
108
- $this->cached = null;
109
- }
110
-
111
- /**
112
- * Returns true if the builder has a handler.
113
- *
114
- * @return bool
115
- */
116
- public function hasHandler()
117
- {
118
- return (bool) $this->handler;
119
- }
120
-
121
- /**
122
- * Unshift a middleware to the bottom of the stack.
123
- *
124
- * @param callable $middleware Middleware function
125
- * @param string $name Name to register for this middleware.
126
- */
127
- public function unshift(callable $middleware, $name = null)
128
- {
129
- array_unshift($this->stack, [$middleware, $name]);
130
- $this->cached = null;
131
- }
132
-
133
- /**
134
- * Push a middleware to the top of the stack.
135
- *
136
- * @param callable $middleware Middleware function
137
- * @param string $name Name to register for this middleware.
138
- */
139
- public function push(callable $middleware, $name = '')
140
- {
141
- $this->stack[] = [$middleware, $name];
142
- $this->cached = null;
143
- }
144
-
145
- /**
146
- * Add a middleware before another middleware by name.
147
- *
148
- * @param string $findName Middleware to find
149
- * @param callable $middleware Middleware function
150
- * @param string $withName Name to register for this middleware.
151
- */
152
- public function before($findName, callable $middleware, $withName = '')
153
- {
154
- $this->splice($findName, $withName, $middleware, true);
155
- }
156
-
157
- /**
158
- * Add a middleware after another middleware by name.
159
- *
160
- * @param string $findName Middleware to find
161
- * @param callable $middleware Middleware function
162
- * @param string $withName Name to register for this middleware.
163
- */
164
- public function after($findName, callable $middleware, $withName = '')
165
- {
166
- $this->splice($findName, $withName, $middleware, false);
167
- }
168
-
169
- /**
170
- * Remove a middleware by instance or name from the stack.
171
- *
172
- * @param callable|string $remove Middleware to remove by instance or name.
173
- */
174
- public function remove($remove)
175
- {
176
- $this->cached = null;
177
- $idx = is_callable($remove) ? 0 : 1;
178
- $this->stack = array_values(array_filter(
179
- $this->stack,
180
- function ($tuple) use ($idx, $remove) {
181
- return $tuple[$idx] !== $remove;
182
- }
183
- ));
184
- }
185
-
186
- /**
187
- * Compose the middleware and handler into a single callable function.
188
- *
189
- * @return callable
190
- */
191
- public function resolve()
192
- {
193
- if (!$this->cached) {
194
- if (!($prev = $this->handler)) {
195
- throw new \LogicException('No handler has been specified');
196
- }
197
-
198
- foreach (array_reverse($this->stack) as $fn) {
199
- $prev = $fn[0]($prev);
200
- }
201
-
202
- $this->cached = $prev;
203
- }
204
-
205
- return $this->cached;
206
- }
207
-
208
- /**
209
- * @param string $name
210
- * @return int
211
- */
212
- private function findByName($name)
213
- {
214
- foreach ($this->stack as $k => $v) {
215
- if ($v[1] === $name) {
216
- return $k;
217
- }
218
- }
219
-
220
- throw new \InvalidArgumentException("Middleware not found: $name");
221
- }
222
-
223
- /**
224
- * Splices a function into the middleware list at a specific position.
225
- *
226
- * @param string $findName
227
- * @param string $withName
228
- * @param callable $middleware
229
- * @param bool $before
230
- */
231
- private function splice($findName, $withName, callable $middleware, $before)
232
- {
233
- $this->cached = null;
234
- $idx = $this->findByName($findName);
235
- $tuple = [$middleware, $withName];
236
-
237
- if ($before) {
238
- if ($idx === 0) {
239
- array_unshift($this->stack, $tuple);
240
- } else {
241
- $replacement = [$tuple, $this->stack[$idx]];
242
- array_splice($this->stack, $idx, 1, $replacement);
243
- }
244
- } elseif ($idx === count($this->stack) - 1) {
245
- $this->stack[] = $tuple;
246
- } else {
247
- $replacement = [$this->stack[$idx], $tuple];
248
- array_splice($this->stack, $idx, 1, $replacement);
249
- }
250
- }
251
-
252
- /**
253
- * Provides a debug string for a given callable.
254
- *
255
- * @param array|callable $fn Function to write as a string.
256
- *
257
- * @return string
258
- */
259
- private function debugCallable($fn)
260
- {
261
- if (is_string($fn)) {
262
- return "callable({$fn})";
263
- }
264
-
265
- if (is_array($fn)) {
266
- return is_string($fn[0])
267
- ? "callable({$fn[0]}::{$fn[1]})"
268
- : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
269
- }
270
-
271
- return 'callable(' . spl_object_hash($fn) . ')';
272
- }
273
- }
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Promise\PromiseInterface;
5
+ use Psr\Http\Message\RequestInterface;
6
+ use Psr\Http\Message\ResponseInterface;
7
+
8
+ /**
9
+ * Creates a composed Guzzle handler function by stacking middlewares on top of
10
+ * an HTTP handler function.
11
+ */
12
+ class HandlerStack
13
+ {
14
+ /** @var callable|null */
15
+ private $handler;
16
+
17
+ /** @var array */
18
+ private $stack = [];
19
+
20
+ /** @var callable|null */
21
+ private $cached;
22
+
23
+ /**
24
+ * Creates a default handler stack that can be used by clients.
25
+ *
26
+ * The returned handler will wrap the provided handler or use the most
27
+ * appropriate default handler for your system. The returned HandlerStack has
28
+ * support for cookies, redirects, HTTP error exceptions, and preparing a body
29
+ * before sending.
30
+ *
31
+ * The returned handler stack can be passed to a client in the "handler"
32
+ * option.
33
+ *
34
+ * @param callable $handler HTTP handler function to use with the stack. If no
35
+ * handler is provided, the best handler for your
36
+ * system will be utilized.
37
+ *
38
+ * @return HandlerStack
39
+ */
40
+ public static function create(callable $handler = null)
41
+ {
42
+ $stack = new self($handler ?: choose_handler());
43
+ $stack->push(Middleware::httpErrors(), 'http_errors');
44
+ $stack->push(Middleware::redirect(), 'allow_redirects');
45
+ $stack->push(Middleware::cookies(), 'cookies');
46
+ $stack->push(Middleware::prepareBody(), 'prepare_body');
47
+
48
+ return $stack;
49
+ }
50
+
51
+ /**
52
+ * @param callable $handler Underlying HTTP handler.
53
+ */
54
+ public function __construct(callable $handler = null)
55
+ {
56
+ $this->handler = $handler;
57
+ }
58
+
59
+ /**
60
+ * Invokes the handler stack as a composed handler
61
+ *
62
+ * @param RequestInterface $request
63
+ * @param array $options
64
+ *
65
+ * @return ResponseInterface|PromiseInterface
66
+ */
67
+ public function __invoke(RequestInterface $request, array $options)
68
+ {
69
+ $handler = $this->resolve();
70
+
71
+ return $handler($request, $options);
72
+ }
73
+
74
+ /**
75
+ * Dumps a string representation of the stack.
76
+ *
77
+ * @return string
78
+ */
79
+ public function __toString()
80
+ {
81
+ $depth = 0;
82
+ $stack = [];
83
+ if ($this->handler) {
84
+ $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
85
+ }
86
+
87
+ $result = '';
88
+ foreach (array_reverse($this->stack) as $tuple) {
89
+ $depth++;
90
+ $str = "{$depth}) Name: '{$tuple[1]}', ";
91
+ $str .= "Function: " . $this->debugCallable($tuple[0]);
92
+ $result = "> {$str}\n{$result}";
93
+ $stack[] = $str;
94
+ }
95
+
96
+ foreach (array_keys($stack) as $k) {
97
+ $result .= "< {$stack[$k]}\n";
98
+ }
99
+
100
+ return $result;
101
+ }
102
+
103
+ /**
104
+ * Set the HTTP handler that actually returns a promise.
105
+ *
106
+ * @param callable $handler Accepts a request and array of options and
107
+ * returns a Promise.
108
+ */
109
+ public function setHandler(callable $handler)
110
+ {
111
+ $this->handler = $handler;
112
+ $this->cached = null;
113
+ }
114
+
115
+ /**
116
+ * Returns true if the builder has a handler.
117
+ *
118
+ * @return bool
119
+ */
120
+ public function hasHandler()
121
+ {
122
+ return (bool) $this->handler;
123
+ }
124
+
125
+ /**
126
+ * Unshift a middleware to the bottom of the stack.
127
+ *
128
+ * @param callable $middleware Middleware function
129
+ * @param string $name Name to register for this middleware.
130
+ */
131
+ public function unshift(callable $middleware, $name = null)
132
+ {
133
+ array_unshift($this->stack, [$middleware, $name]);
134
+ $this->cached = null;
135
+ }
136
+
137
+ /**
138
+ * Push a middleware to the top of the stack.
139
+ *
140
+ * @param callable $middleware Middleware function
141
+ * @param string $name Name to register for this middleware.
142
+ */
143
+ public function push(callable $middleware, $name = '')
144
+ {
145
+ $this->stack[] = [$middleware, $name];
146
+ $this->cached = null;
147
+ }
148
+
149
+ /**
150
+ * Add a middleware before another middleware by name.
151
+ *
152
+ * @param string $findName Middleware to find
153
+ * @param callable $middleware Middleware function
154
+ * @param string $withName Name to register for this middleware.
155
+ */
156
+ public function before($findName, callable $middleware, $withName = '')
157
+ {
158
+ $this->splice($findName, $withName, $middleware, true);
159
+ }
160
+
161
+ /**
162
+ * Add a middleware after another middleware by name.
163
+ *
164
+ * @param string $findName Middleware to find
165
+ * @param callable $middleware Middleware function
166
+ * @param string $withName Name to register for this middleware.
167
+ */
168
+ public function after($findName, callable $middleware, $withName = '')
169
+ {
170
+ $this->splice($findName, $withName, $middleware, false);
171
+ }
172
+
173
+ /**
174
+ * Remove a middleware by instance or name from the stack.
175
+ *
176
+ * @param callable|string $remove Middleware to remove by instance or name.
177
+ */
178
+ public function remove($remove)
179
+ {
180
+ $this->cached = null;
181
+ $idx = is_callable($remove) ? 0 : 1;
182
+ $this->stack = array_values(array_filter(
183
+ $this->stack,
184
+ function ($tuple) use ($idx, $remove) {
185
+ return $tuple[$idx] !== $remove;
186
+ }
187
+ ));
188
+ }
189
+
190
+ /**
191
+ * Compose the middleware and handler into a single callable function.
192
+ *
193
+ * @return callable
194
+ */
195
+ public function resolve()
196
+ {
197
+ if (!$this->cached) {
198
+ if (!($prev = $this->handler)) {
199
+ throw new \LogicException('No handler has been specified');
200
+ }
201
+
202
+ foreach (array_reverse($this->stack) as $fn) {
203
+ $prev = $fn[0]($prev);
204
+ }
205
+
206
+ $this->cached = $prev;
207
+ }
208
+
209
+ return $this->cached;
210
+ }
211
+
212
+ /**
213
+ * @param string $name
214
+ * @return int
215
+ */
216
+ private function findByName($name)
217
+ {
218
+ foreach ($this->stack as $k => $v) {
219
+ if ($v[1] === $name) {
220
+ return $k;
221
+ }
222
+ }
223
+
224
+ throw new \InvalidArgumentException("Middleware not found: $name");
225
+ }
226
+
227
+ /**
228
+ * Splices a function into the middleware list at a specific position.
229
+ *
230
+ * @param string $findName
231
+ * @param string $withName
232
+ * @param callable $middleware
233
+ * @param bool $before
234
+ */
235
+ private function splice($findName, $withName, callable $middleware, $before)
236
+ {
237
+ $this->cached = null;
238
+ $idx = $this->findByName($findName);
239
+ $tuple = [$middleware, $withName];
240
+
241
+ if ($before) {
242
+ if ($idx === 0) {
243
+ array_unshift($this->stack, $tuple);
244
+ } else {
245
+ $replacement = [$tuple, $this->stack[$idx]];
246
+ array_splice($this->stack, $idx, 1, $replacement);
247
+ }
248
+ } elseif ($idx === count($this->stack) - 1) {
249
+ $this->stack[] = $tuple;
250
+ } else {
251
+ $replacement = [$this->stack[$idx], $tuple];
252
+ array_splice($this->stack, $idx, 1, $replacement);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Provides a debug string for a given callable.
258
+ *
259
+ * @param array|callable $fn Function to write as a string.
260
+ *
261
+ * @return string
262
+ */
263
+ private function debugCallable($fn)
264
+ {
265
+ if (is_string($fn)) {
266
+ return "callable({$fn})";
267
+ }
268
+
269
+ if (is_array($fn)) {
270
+ return is_string($fn[0])
271
+ ? "callable({$fn[0]}::{$fn[1]})"
272
+ : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
273
+ }
274
+
275
+ return 'callable(' . spl_object_hash($fn) . ')';
276
+ }
277
+ }
vendor/guzzlehttp/guzzle/src/MessageFormatter.php CHANGED
@@ -1,180 +1,185 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use Psr\Http\Message\MessageInterface;
5
- use Psr\Http\Message\RequestInterface;
6
- use Psr\Http\Message\ResponseInterface;
7
-
8
- /**
9
- * Formats log messages using variable substitutions for requests, responses,
10
- * and other transactional data.
11
- *
12
- * The following variable substitutions are supported:
13
- *
14
- * - {request}: Full HTTP request message
15
- * - {response}: Full HTTP response message
16
- * - {ts}: ISO 8601 date in GMT
17
- * - {date_iso_8601} ISO 8601 date in GMT
18
- * - {date_common_log} Apache common log date using the configured timezone.
19
- * - {host}: Host of the request
20
- * - {method}: Method of the request
21
- * - {uri}: URI of the request
22
- * - {version}: Protocol version
23
- * - {target}: Request target of the request (path + query + fragment)
24
- * - {hostname}: Hostname of the machine that sent the request
25
- * - {code}: Status code of the response (if available)
26
- * - {phrase}: Reason phrase of the response (if available)
27
- * - {error}: Any error messages (if available)
28
- * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
29
- * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
30
- * - {req_headers}: Request headers
31
- * - {res_headers}: Response headers
32
- * - {req_body}: Request body
33
- * - {res_body}: Response body
34
- */
35
- class MessageFormatter
36
- {
37
- /**
38
- * Apache Common Log Format.
39
- * @link http://httpd.apache.org/docs/2.4/logs.html#common
40
- * @var string
41
- */
42
- const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
43
- const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
44
- const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
45
-
46
- /** @var string Template used to format log messages */
47
- private $template;
48
-
49
- /**
50
- * @param string $template Log message template
51
- */
52
- public function __construct($template = self::CLF)
53
- {
54
- $this->template = $template ?: self::CLF;
55
- }
56
-
57
- /**
58
- * Returns a formatted message string.
59
- *
60
- * @param RequestInterface $request Request that was sent
61
- * @param ResponseInterface $response Response that was received
62
- * @param \Exception $error Exception that was received
63
- *
64
- * @return string
65
- */
66
- public function format(
67
- RequestInterface $request,
68
- ResponseInterface $response = null,
69
- \Exception $error = null
70
- ) {
71
- $cache = [];
72
-
73
- return preg_replace_callback(
74
- '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
75
- function (array $matches) use ($request, $response, $error, &$cache) {
76
- if (isset($cache[$matches[1]])) {
77
- return $cache[$matches[1]];
78
- }
79
-
80
- $result = '';
81
- switch ($matches[1]) {
82
- case 'request':
83
- $result = Psr7\str($request);
84
- break;
85
- case 'response':
86
- $result = $response ? Psr7\str($response) : '';
87
- break;
88
- case 'req_headers':
89
- $result = trim($request->getMethod()
90
- . ' ' . $request->getRequestTarget())
91
- . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
92
- . $this->headers($request);
93
- break;
94
- case 'res_headers':
95
- $result = $response ?
96
- sprintf(
97
- 'HTTP/%s %d %s',
98
- $response->getProtocolVersion(),
99
- $response->getStatusCode(),
100
- $response->getReasonPhrase()
101
- ) . "\r\n" . $this->headers($response)
102
- : 'NULL';
103
- break;
104
- case 'req_body':
105
- $result = $request->getBody();
106
- break;
107
- case 'res_body':
108
- $result = $response ? $response->getBody() : 'NULL';
109
- break;
110
- case 'ts':
111
- case 'date_iso_8601':
112
- $result = gmdate('c');
113
- break;
114
- case 'date_common_log':
115
- $result = date('d/M/Y:H:i:s O');
116
- break;
117
- case 'method':
118
- $result = $request->getMethod();
119
- break;
120
- case 'version':
121
- $result = $request->getProtocolVersion();
122
- break;
123
- case 'uri':
124
- case 'url':
125
- $result = $request->getUri();
126
- break;
127
- case 'target':
128
- $result = $request->getRequestTarget();
129
- break;
130
- case 'req_version':
131
- $result = $request->getProtocolVersion();
132
- break;
133
- case 'res_version':
134
- $result = $response
135
- ? $response->getProtocolVersion()
136
- : 'NULL';
137
- break;
138
- case 'host':
139
- $result = $request->getHeaderLine('Host');
140
- break;
141
- case 'hostname':
142
- $result = gethostname();
143
- break;
144
- case 'code':
145
- $result = $response ? $response->getStatusCode() : 'NULL';
146
- break;
147
- case 'phrase':
148
- $result = $response ? $response->getReasonPhrase() : 'NULL';
149
- break;
150
- case 'error':
151
- $result = $error ? $error->getMessage() : 'NULL';
152
- break;
153
- default:
154
- // handle prefixed dynamic headers
155
- if (strpos($matches[1], 'req_header_') === 0) {
156
- $result = $request->getHeaderLine(substr($matches[1], 11));
157
- } elseif (strpos($matches[1], 'res_header_') === 0) {
158
- $result = $response
159
- ? $response->getHeaderLine(substr($matches[1], 11))
160
- : 'NULL';
161
- }
162
- }
163
-
164
- $cache[$matches[1]] = $result;
165
- return $result;
166
- },
167
- $this->template
168
- );
169
- }
170
-
171
- private function headers(MessageInterface $message)
172
- {
173
- $result = '';
174
- foreach ($message->getHeaders() as $name => $values) {
175
- $result .= $name . ': ' . implode(', ', $values) . "\r\n";
176
- }
177
-
178
- return trim($result);
179
- }
180
- }
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use Psr\Http\Message\MessageInterface;
5
+ use Psr\Http\Message\RequestInterface;
6
+ use Psr\Http\Message\ResponseInterface;
7
+
8
+ /**
9
+ * Formats log messages using variable substitutions for requests, responses,
10
+ * and other transactional data.
11
+ *
12
+ * The following variable substitutions are supported:
13
+ *
14
+ * - {request}: Full HTTP request message
15
+ * - {response}: Full HTTP response message
16
+ * - {ts}: ISO 8601 date in GMT
17
+ * - {date_iso_8601} ISO 8601 date in GMT
18
+ * - {date_common_log} Apache common log date using the configured timezone.
19
+ * - {host}: Host of the request
20
+ * - {method}: Method of the request
21
+ * - {uri}: URI of the request
22
+ * - {version}: Protocol version
23
+ * - {target}: Request target of the request (path + query + fragment)
24
+ * - {hostname}: Hostname of the machine that sent the request
25
+ * - {code}: Status code of the response (if available)
26
+ * - {phrase}: Reason phrase of the response (if available)
27
+ * - {error}: Any error messages (if available)
28
+ * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
29
+ * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
30
+ * - {req_headers}: Request headers
31
+ * - {res_headers}: Response headers
32
+ * - {req_body}: Request body
33
+ * - {res_body}: Response body
34
+ */
35
+ class MessageFormatter
36
+ {
37
+ /**
38
+ * Apache Common Log Format.
39
+ * @link http://httpd.apache.org/docs/2.4/logs.html#common
40
+ * @var string
41
+ */
42
+ const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
43
+ const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
44
+ const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
45
+
46
+ /** @var string Template used to format log messages */
47
+ private $template;
48
+
49
+ /**
50
+ * @param string $template Log message template
51
+ */
52
+ public function __construct($template = self::CLF)
53
+ {
54
+ $this->template = $template ?: self::CLF;
55
+ }
56
+
57
+ /**
58
+ * Returns a formatted message string.
59
+ *
60
+ * @param RequestInterface $request Request that was sent
61
+ * @param ResponseInterface $response Response that was received
62
+ * @param \Exception $error Exception that was received
63
+ *
64
+ * @return string
65
+ */
66
+ public function format(
67
+ RequestInterface $request,
68
+ ResponseInterface $response = null,
69
+ \Exception $error = null
70
+ ) {
71
+ $cache = [];
72
+
73
+ return preg_replace_callback(
74
+ '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
75
+ function (array $matches) use ($request, $response, $error, &$cache) {
76
+ if (isset($cache[$matches[1]])) {
77
+ return $cache[$matches[1]];
78
+ }
79
+
80
+ $result = '';
81
+ switch ($matches[1]) {
82
+ case 'request':
83
+ $result = Psr7\str($request);
84
+ break;
85
+ case 'response':
86
+ $result = $response ? Psr7\str($response) : '';
87
+ break;
88
+ case 'req_headers':
89
+ $result = trim($request->getMethod()
90
+ . ' ' . $request->getRequestTarget())
91
+ . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
92
+ . $this->headers($request);
93
+ break;
94
+ case 'res_headers':
95
+ $result = $response ?
96
+ sprintf(
97
+ 'HTTP/%s %d %s',
98
+ $response->getProtocolVersion(),
99
+ $response->getStatusCode(),
100
+ $response->getReasonPhrase()
101
+ ) . "\r\n" . $this->headers($response)
102
+ : 'NULL';
103
+ break;
104
+ case 'req_body':
105
+ $result = $request->getBody();
106
+ break;
107
+ case 'res_body':
108
+ $result = $response ? $response->getBody() : 'NULL';
109
+ break;
110
+ case 'ts':
111
+ case 'date_iso_8601':
112
+ $result = gmdate('c');
113
+ break;
114
+ case 'date_common_log':
115
+ $result = date('d/M/Y:H:i:s O');
116
+ break;
117
+ case 'method':
118
+ $result = $request->getMethod();
119
+ break;
120
+ case 'version':
121
+ $result = $request->getProtocolVersion();
122
+ break;
123
+ case 'uri':
124
+ case 'url':
125
+ $result = $request->getUri();
126
+ break;
127
+ case 'target':
128
+ $result = $request->getRequestTarget();
129
+ break;
130
+ case 'req_version':
131
+ $result = $request->getProtocolVersion();
132
+ break;
133
+ case 'res_version':
134
+ $result = $response
135
+ ? $response->getProtocolVersion()
136
+ : 'NULL';
137
+ break;
138
+ case 'host':
139
+ $result = $request->getHeaderLine('Host');
140
+ break;
141
+ case 'hostname':
142
+ $result = gethostname();
143
+ break;
144
+ case 'code':
145
+ $result = $response ? $response->getStatusCode() : 'NULL';
146
+ break;
147
+ case 'phrase':
148
+ $result = $response ? $response->getReasonPhrase() : 'NULL';
149
+ break;
150
+ case 'error':
151
+ $result = $error ? $error->getMessage() : 'NULL';
152
+ break;
153
+ default:
154
+ // handle prefixed dynamic headers
155
+ if (strpos($matches[1], 'req_header_') === 0) {
156
+ $result = $request->getHeaderLine(substr($matches[1], 11));
157
+ } elseif (strpos($matches[1], 'res_header_') === 0) {
158
+ $result = $response
159
+ ? $response->getHeaderLine(substr($matches[1], 11))
160
+ : 'NULL';
161
+ }
162
+ }
163
+
164
+ $cache[$matches[1]] = $result;
165
+ return $result;
166
+ },
167
+ $this->template
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Get headers from message as string
173
+ *
174
+ * @return string
175
+ */
176
+ private function headers(MessageInterface $message)
177
+ {
178
+ $result = '';
179
+ foreach ($message->getHeaders() as $name => $values) {
180
+ $result .= $name . ': ' . implode(', ', $values) . "\r\n";
181
+ }
182
+
183
+ return trim($result);
184
+ }
185
+ }
vendor/guzzlehttp/guzzle/src/Pool.php CHANGED
@@ -1,123 +1,132 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Promise\PromisorInterface;
5
- use Psr\Http\Message\RequestInterface;
6
- use GuzzleHttp\Promise\EachPromise;
7
-
8
- /**
9
- * Sends an iterator of requests concurrently using a capped pool size.
10
- *
11
- * The pool will read from an iterator until it is cancelled or until the
12
- * iterator is consumed. When a request is yielded, the request is sent after
13
- * applying the "request_options" request options (if provided in the ctor).
14
- *
15
- * When a function is yielded by the iterator, the function is provided the
16
- * "request_options" array that should be merged on top of any existing
17
- * options, and the function MUST then return a wait-able promise.
18
- */
19
- class Pool implements PromisorInterface
20
- {
21
- /** @var EachPromise */
22
- private $each;
23
-
24
- /**
25
- * @param ClientInterface $client Client used to send the requests.
26
- * @param array|\Iterator $requests Requests or functions that return
27
- * requests to send concurrently.
28
- * @param array $config Associative array of options
29
- * - concurrency: (int) Maximum number of requests to send concurrently
30
- * - options: Array of request options to apply to each request.
31
- * - fulfilled: (callable) Function to invoke when a request completes.
32
- * - rejected: (callable) Function to invoke when a request is rejected.
33
- */
34
- public function __construct(
35
- ClientInterface $client,
36
- $requests,
37
- array $config = []
38
- ) {
39
- // Backwards compatibility.
40
- if (isset($config['pool_size'])) {
41
- $config['concurrency'] = $config['pool_size'];
42
- } elseif (!isset($config['concurrency'])) {
43
- $config['concurrency'] = 25;
44
- }
45
-
46
- if (isset($config['options'])) {
47
- $opts = $config['options'];
48
- unset($config['options']);
49
- } else {
50
- $opts = [];
51
- }
52
-
53
- $iterable = \GuzzleHttp\Promise\iter_for($requests);
54
- $requests = function () use ($iterable, $client, $opts) {
55
- foreach ($iterable as $key => $rfn) {
56
- if ($rfn instanceof RequestInterface) {
57
- yield $key => $client->sendAsync($rfn, $opts);
58
- } elseif (is_callable($rfn)) {
59
- yield $key => $rfn($opts);
60
- } else {
61
- throw new \InvalidArgumentException('Each value yielded by '
62
- . 'the iterator must be a Psr7\Http\Message\RequestInterface '
63
- . 'or a callable that returns a promise that fulfills '
64
- . 'with a Psr7\Message\Http\ResponseInterface object.');
65
- }
66
- }
67
- };
68
-
69
- $this->each = new EachPromise($requests(), $config);
70
- }
71
-
72
- public function promise()
73
- {
74
- return $this->each->promise();
75
- }
76
-
77
- /**
78
- * Sends multiple requests concurrently and returns an array of responses
79
- * and exceptions that uses the same ordering as the provided requests.
80
- *
81
- * IMPORTANT: This method keeps every request and response in memory, and
82
- * as such, is NOT recommended when sending a large number or an
83
- * indeterminate number of requests concurrently.
84
- *
85
- * @param ClientInterface $client Client used to send the requests
86
- * @param array|\Iterator $requests Requests to send concurrently.
87
- * @param array $options Passes through the options available in
88
- * {@see GuzzleHttp\Pool::__construct}
89
- *
90
- * @return array Returns an array containing the response or an exception
91
- * in the same order that the requests were sent.
92
- * @throws \InvalidArgumentException if the event format is incorrect.
93
- */
94
- public static function batch(
95
- ClientInterface $client,
96
- $requests,
97
- array $options = []
98
- ) {
99
- $res = [];
100
- self::cmpCallback($options, 'fulfilled', $res);
101
- self::cmpCallback($options, 'rejected', $res);
102
- $pool = new static($client, $requests, $options);
103
- $pool->promise()->wait();
104
- ksort($res);
105
-
106
- return $res;
107
- }
108
-
109
- private static function cmpCallback(array &$options, $name, array &$results)
110
- {
111
- if (!isset($options[$name])) {
112
- $options[$name] = function ($v, $k) use (&$results) {
113
- $results[$k] = $v;
114
- };
115
- } else {
116
- $currentFn = $options[$name];
117
- $options[$name] = function ($v, $k) use (&$results, $currentFn) {
118
- $currentFn($v, $k);
119
- $results[$k] = $v;
120
- };
121
- }
122
- }
123
- }
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Promise\EachPromise;
5
+ use GuzzleHttp\Promise\PromisorInterface;
6
+ use Psr\Http\Message\RequestInterface;
7
+
8
+ /**
9
+ * Sends an iterator of requests concurrently using a capped pool size.
10
+ *
11
+ * The pool will read from an iterator until it is cancelled or until the
12
+ * iterator is consumed. When a request is yielded, the request is sent after
13
+ * applying the "request_options" request options (if provided in the ctor).
14
+ *
15
+ * When a function is yielded by the iterator, the function is provided the
16
+ * "request_options" array that should be merged on top of any existing
17
+ * options, and the function MUST then return a wait-able promise.
18
+ */
19
+ class Pool implements PromisorInterface
20
+ {
21
+ /** @var EachPromise */
22
+ private $each;
23
+
24
+ /**
25
+ * @param ClientInterface $client Client used to send the requests.
26
+ * @param array|\Iterator $requests Requests or functions that return
27
+ * requests to send concurrently.
28
+ * @param array $config Associative array of options
29
+ * - concurrency: (int) Maximum number of requests to send concurrently
30
+ * - options: Array of request options to apply to each request.
31
+ * - fulfilled: (callable) Function to invoke when a request completes.
32
+ * - rejected: (callable) Function to invoke when a request is rejected.
33
+ */
34
+ public function __construct(
35
+ ClientInterface $client,
36
+ $requests,
37
+ array $config = []
38
+ ) {
39
+ // Backwards compatibility.
40
+ if (isset($config['pool_size'])) {
41
+ $config['concurrency'] = $config['pool_size'];
42
+ } elseif (!isset($config['concurrency'])) {
43
+ $config['concurrency'] = 25;
44
+ }
45
+
46
+ if (isset($config['options'])) {
47
+ $opts = $config['options'];
48
+ unset($config['options']);
49
+ } else {
50
+ $opts = [];
51
+ }
52
+
53
+ $iterable = \GuzzleHttp\Promise\iter_for($requests);
54
+ $requests = function () use ($iterable, $client, $opts) {
55
+ foreach ($iterable as $key => $rfn) {
56
+ if ($rfn instanceof RequestInterface) {
57
+ yield $key => $client->sendAsync($rfn, $opts);
58
+ } elseif (is_callable($rfn)) {
59
+ yield $key => $rfn($opts);
60
+ } else {
61
+ throw new \InvalidArgumentException('Each value yielded by '
62
+ . 'the iterator must be a Psr7\Http\Message\RequestInterface '
63
+ . 'or a callable that returns a promise that fulfills '
64
+ . 'with a Psr7\Message\Http\ResponseInterface object.');
65
+ }
66
+ }
67
+ };
68
+
69
+ $this->each = new EachPromise($requests(), $config);
70
+ }
71
+
72
+ /**
73
+ * Get promise
74
+ * @return GuzzleHttp\Promise\Promise
75
+ */
76
+ public function promise()
77
+ {
78
+ return $this->each->promise();
79
+ }
80
+
81
+ /**
82
+ * Sends multiple requests concurrently and returns an array of responses
83
+ * and exceptions that uses the same ordering as the provided requests.
84
+ *
85
+ * IMPORTANT: This method keeps every request and response in memory, and
86
+ * as such, is NOT recommended when sending a large number or an
87
+ * indeterminate number of requests concurrently.
88
+ *
89
+ * @param ClientInterface $client Client used to send the requests
90
+ * @param array|\Iterator $requests Requests to send concurrently.
91
+ * @param array $options Passes through the options available in
92
+ * {@see GuzzleHttp\Pool::__construct}
93
+ *
94
+ * @return array Returns an array containing the response or an exception
95
+ * in the same order that the requests were sent.
96
+ * @throws \InvalidArgumentException if the event format is incorrect.
97
+ */
98
+ public static function batch(
99
+ ClientInterface $client,
100
+ $requests,
101
+ array $options = []
102
+ ) {
103
+ $res = [];
104
+ self::cmpCallback($options, 'fulfilled', $res);
105
+ self::cmpCallback($options, 'rejected', $res);
106
+ $pool = new static($client, $requests, $options);
107
+ $pool->promise()->wait();
108
+ ksort($res);
109
+
110
+ return $res;
111
+ }
112
+
113
+ /**
114
+ * Execute callback(s)
115
+ *
116
+ * @return void
117
+ */
118
+ private static function cmpCallback(array &$options, $name, array &$results)
119
+ {
120
+ if (!isset($options[$name])) {
121
+ $options[$name] = function ($v, $k) use (&$results) {
122
+ $results[$k] = $v;
123
+ };
124
+ } else {
125
+ $currentFn = $options[$name];
126
+ $options[$name] = function ($v, $k) use (&$results, $currentFn) {
127
+ $currentFn($v, $k);
128
+ $results[$k] = $v;
129
+ };
130
+ }
131
+ }
132
+ }
vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php CHANGED
@@ -1,106 +1,111 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Promise\PromiseInterface;
5
- use GuzzleHttp\Psr7;
6
- use Psr\Http\Message\RequestInterface;
7
-
8
- /**
9
- * Prepares requests that contain a body, adding the Content-Length,
10
- * Content-Type, and Expect headers.
11
- */
12
- class PrepareBodyMiddleware
13
- {
14
- /** @var callable */
15
- private $nextHandler;
16
-
17
- /**
18
- * @param callable $nextHandler Next handler to invoke.
19
- */
20
- public function __construct(callable $nextHandler)
21
- {
22
- $this->nextHandler = $nextHandler;
23
- }
24
-
25
- /**
26
- * @param RequestInterface $request
27
- * @param array $options
28
- *
29
- * @return PromiseInterface
30
- */
31
- public function __invoke(RequestInterface $request, array $options)
32
- {
33
- $fn = $this->nextHandler;
34
-
35
- // Don't do anything if the request has no body.
36
- if ($request->getBody()->getSize() === 0) {
37
- return $fn($request, $options);
38
- }
39
-
40
- $modify = [];
41
-
42
- // Add a default content-type if possible.
43
- if (!$request->hasHeader('Content-Type')) {
44
- if ($uri = $request->getBody()->getMetadata('uri')) {
45
- if ($type = Psr7\mimetype_from_filename($uri)) {
46
- $modify['set_headers']['Content-Type'] = $type;
47
- }
48
- }
49
- }
50
-
51
- // Add a default content-length or transfer-encoding header.
52
- if (!$request->hasHeader('Content-Length')
53
- && !$request->hasHeader('Transfer-Encoding')
54
- ) {
55
- $size = $request->getBody()->getSize();
56
- if ($size !== null) {
57
- $modify['set_headers']['Content-Length'] = $size;
58
- } else {
59
- $modify['set_headers']['Transfer-Encoding'] = 'chunked';
60
- }
61
- }
62
-
63
- // Add the expect header if needed.
64
- $this->addExpectHeader($request, $options, $modify);
65
-
66
- return $fn(Psr7\modify_request($request, $modify), $options);
67
- }
68
-
69
- private function addExpectHeader(
70
- RequestInterface $request,
71
- array $options,
72
- array &$modify
73
- ) {
74
- // Determine if the Expect header should be used
75
- if ($request->hasHeader('Expect')) {
76
- return;
77
- }
78
-
79
- $expect = isset($options['expect']) ? $options['expect'] : null;
80
-
81
- // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
82
- if ($expect === false || $request->getProtocolVersion() < 1.1) {
83
- return;
84
- }
85
-
86
- // The expect header is unconditionally enabled
87
- if ($expect === true) {
88
- $modify['set_headers']['Expect'] = '100-Continue';
89
- return;
90
- }
91
-
92
- // By default, send the expect header when the payload is > 1mb
93
- if ($expect === null) {
94
- $expect = 1048576;
95
- }
96
-
97
- // Always add if the body cannot be rewound, the size cannot be
98
- // determined, or the size is greater than the cutoff threshold
99
- $body = $request->getBody();
100
- $size = $body->getSize();
101
-
102
- if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
103
- $modify['set_headers']['Expect'] = '100-Continue';
104
- }
105
- }
106
- }
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Promise\PromiseInterface;
5
+ use GuzzleHttp\Psr7;
6
+ use Psr\Http\Message\RequestInterface;
7
+
8
+ /**
9
+ * Prepares requests that contain a body, adding the Content-Length,
10
+ * Content-Type, and Expect headers.
11
+ */
12
+ class PrepareBodyMiddleware
13
+ {
14
+ /** @var callable */
15
+ private $nextHandler;
16
+
17
+ /**
18
+ * @param callable $nextHandler Next handler to invoke.
19
+ */
20
+ public function __construct(callable $nextHandler)
21
+ {
22
+ $this->nextHandler = $nextHandler;
23
+ }
24
+
25
+ /**
26
+ * @param RequestInterface $request
27
+ * @param array $options
28
+ *
29
+ * @return PromiseInterface
30
+ */
31
+ public function __invoke(RequestInterface $request, array $options)
32
+ {
33
+ $fn = $this->nextHandler;
34
+
35
+ // Don't do anything if the request has no body.
36
+ if ($request->getBody()->getSize() === 0) {
37
+ return $fn($request, $options);
38
+ }
39
+
40
+ $modify = [];
41
+
42
+ // Add a default content-type if possible.
43
+ if (!$request->hasHeader('Content-Type')) {
44
+ if ($uri = $request->getBody()->getMetadata('uri')) {
45
+ if ($type = Psr7\mimetype_from_filename($uri)) {
46
+ $modify['set_headers']['Content-Type'] = $type;
47
+ }
48
+ }
49
+ }
50
+
51
+ // Add a default content-length or transfer-encoding header.
52
+ if (!$request->hasHeader('Content-Length')
53
+ && !$request->hasHeader('Transfer-Encoding')
54
+ ) {
55
+ $size = $request->getBody()->getSize();
56
+ if ($size !== null) {
57
+ $modify['set_headers']['Content-Length'] = $size;
58
+ } else {
59
+ $modify['set_headers']['Transfer-Encoding'] = 'chunked';
60
+ }
61
+ }
62
+
63
+ // Add the expect header if needed.
64
+ $this->addExpectHeader($request, $options, $modify);
65
+
66
+ return $fn(Psr7\modify_request($request, $modify), $options);
67
+ }
68
+
69
+ /**
70
+ * Add expect header
71
+ *
72
+ * @return void
73
+ */
74
+ private function addExpectHeader(
75
+ RequestInterface $request,
76
+ array $options,
77
+ array &$modify
78
+ ) {
79
+ // Determine if the Expect header should be used
80
+ if ($request->hasHeader('Expect')) {
81
+ return;
82
+ }
83
+
84
+ $expect = isset($options['expect']) ? $options['expect'] : null;
85
+
86
+ // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
87
+ if ($expect === false || $request->getProtocolVersion() < 1.1) {
88
+ return;
89
+ }
90
+
91
+ // The expect header is unconditionally enabled
92
+ if ($expect === true) {
93
+ $modify['set_headers']['Expect'] = '100-Continue';
94
+ return;
95
+ }
96
+
97
+ // By default, send the expect header when the payload is > 1mb
98
+ if ($expect === null) {
99
+ $expect = 1048576;
100
+ }
101
+
102
+ // Always add if the body cannot be rewound, the size cannot be
103
+ // determined, or the size is greater than the cutoff threshold
104
+ $body = $request->getBody();
105
+ $size = $body->getSize();
106
+
107
+ if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
108
+ $modify['set_headers']['Expect'] = '100-Continue';
109
+ }
110
+ }
111
+ }
vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php CHANGED
@@ -1,237 +1,249 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Exception\BadResponseException;
5
- use GuzzleHttp\Exception\TooManyRedirectsException;
6
- use GuzzleHttp\Promise\PromiseInterface;
7
- use GuzzleHttp\Psr7;
8
- use Psr\Http\Message\RequestInterface;
9
- use Psr\Http\Message\ResponseInterface;
10
- use Psr\Http\Message\UriInterface;
11
-
12
- /**
13
- * Request redirect middleware.
14
- *
15
- * Apply this middleware like other middleware using
16
- * {@see GuzzleHttp\Middleware::redirect()}.
17
- */
18
- class RedirectMiddleware
19
- {
20
- const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
21
-
22
- const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
23
-
24
- public static $defaultSettings = [
25
- 'max' => 5,
26
- 'protocols' => ['http', 'https'],
27
- 'strict' => false,
28
- 'referer' => false,
29
- 'track_redirects' => false,
30
- ];
31
-
32
- /** @var callable */
33
- private $nextHandler;
34
-
35
- /**
36
- * @param callable $nextHandler Next handler to invoke.
37
- */
38
- public function __construct(callable $nextHandler)
39
- {
40
- $this->nextHandler = $nextHandler;
41
- }
42
-
43
- /**
44
- * @param RequestInterface $request
45
- * @param array $options
46
- *
47
- * @return PromiseInterface
48
- */
49
- public function __invoke(RequestInterface $request, array $options)
50
- {
51
- $fn = $this->nextHandler;
52
-
53
- if (empty($options['allow_redirects'])) {
54
- return $fn($request, $options);
55
- }
56
-
57
- if ($options['allow_redirects'] === true) {
58
- $options['allow_redirects'] = self::$defaultSettings;
59
- } elseif (!is_array($options['allow_redirects'])) {
60
- throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
61
- } else {
62
- // Merge the default settings with the provided settings
63
- $options['allow_redirects'] += self::$defaultSettings;
64
- }
65
-
66
- if (empty($options['allow_redirects']['max'])) {
67
- return $fn($request, $options);
68
- }
69
-
70
- return $fn($request, $options)
71
- ->then(function (ResponseInterface $response) use ($request, $options) {
72
- return $this->checkRedirect($request, $options, $response);
73
- });
74
- }
75
-
76
- /**
77
- * @param RequestInterface $request
78
- * @param array $options
79
- * @param ResponseInterface|PromiseInterface $response
80
- *
81
- * @return ResponseInterface|PromiseInterface
82
- */
83
- public function checkRedirect(
84
- RequestInterface $request,
85
- array $options,
86
- ResponseInterface $response
87
- ) {
88
- if (substr($response->getStatusCode(), 0, 1) != '3'
89
- || !$response->hasHeader('Location')
90
- ) {
91
- return $response;
92
- }
93
-
94
- $this->guardMax($request, $options);
95
- $nextRequest = $this->modifyRequest($request, $options, $response);
96
-
97
- if (isset($options['allow_redirects']['on_redirect'])) {
98
- call_user_func(
99
- $options['allow_redirects']['on_redirect'],
100
- $request,
101
- $response,
102
- $nextRequest->getUri()
103
- );
104
- }
105
-
106
- /** @var PromiseInterface|ResponseInterface $promise */
107
- $promise = $this($nextRequest, $options);
108
-
109
- // Add headers to be able to track history of redirects.
110
- if (!empty($options['allow_redirects']['track_redirects'])) {
111
- return $this->withTracking(
112
- $promise,
113
- (string) $nextRequest->getUri(),
114
- $response->getStatusCode()
115
- );
116
- }
117
-
118
- return $promise;
119
- }
120
-
121
- private function withTracking(PromiseInterface $promise, $uri, $statusCode)
122
- {
123
- return $promise->then(
124
- function (ResponseInterface $response) use ($uri, $statusCode) {
125
- // Note that we are pushing to the front of the list as this
126
- // would be an earlier response than what is currently present
127
- // in the history header.
128
- $historyHeader = $response->getHeader(self::HISTORY_HEADER);
129
- $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
130
- array_unshift($historyHeader, $uri);
131
- array_unshift($statusHeader, $statusCode);
132
- return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
133
- ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
134
- }
135
- );
136
- }
137
-
138
- private function guardMax(RequestInterface $request, array &$options)
139
- {
140
- $current = isset($options['__redirect_count'])
141
- ? $options['__redirect_count']
142
- : 0;
143
- $options['__redirect_count'] = $current + 1;
144
- $max = $options['allow_redirects']['max'];
145
-
146
- if ($options['__redirect_count'] > $max) {
147
- throw new TooManyRedirectsException(
148
- "Will not follow more than {$max} redirects",
149
- $request
150
- );
151
- }
152
- }
153
-
154
- /**
155
- * @param RequestInterface $request
156
- * @param array $options
157
- * @param ResponseInterface $response
158
- *
159
- * @return RequestInterface
160
- */
161
- public function modifyRequest(
162
- RequestInterface $request,
163
- array $options,
164
- ResponseInterface $response
165
- ) {
166
- // Request modifications to apply.
167
- $modify = [];
168
- $protocols = $options['allow_redirects']['protocols'];
169
-
170
- // Use a GET request if this is an entity enclosing request and we are
171
- // not forcing RFC compliance, but rather emulating what all browsers
172
- // would do.
173
- $statusCode = $response->getStatusCode();
174
- if ($statusCode == 303 ||
175
- ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
176
- ) {
177
- $modify['method'] = 'GET';
178
- $modify['body'] = '';
179
- }
180
-
181
- $modify['uri'] = $this->redirectUri($request, $response, $protocols);
182
- Psr7\rewind_body($request);
183
-
184
- // Add the Referer header if it is told to do so and only
185
- // add the header if we are not redirecting from https to http.
186
- if ($options['allow_redirects']['referer']
187
- && $modify['uri']->getScheme() === $request->getUri()->getScheme()
188
- ) {
189
- $uri = $request->getUri()->withUserInfo('');
190
- $modify['set_headers']['Referer'] = (string) $uri;
191
- } else {
192
- $modify['remove_headers'][] = 'Referer';
193
- }
194
-
195
- // Remove Authorization header if host is different.
196
- if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
197
- $modify['remove_headers'][] = 'Authorization';
198
- }
199
-
200
- return Psr7\modify_request($request, $modify);
201
- }
202
-
203
- /**
204
- * Set the appropriate URL on the request based on the location header
205
- *
206
- * @param RequestInterface $request
207
- * @param ResponseInterface $response
208
- * @param array $protocols
209
- *
210
- * @return UriInterface
211
- */
212
- private function redirectUri(
213
- RequestInterface $request,
214
- ResponseInterface $response,
215
- array $protocols
216
- ) {
217
- $location = Psr7\UriResolver::resolve(
218
- $request->getUri(),
219
- new Psr7\Uri($response->getHeaderLine('Location'))
220
- );
221
-
222
- // Ensure that the redirect URI is allowed based on the protocols.
223
- if (!in_array($location->getScheme(), $protocols)) {
224
- throw new BadResponseException(
225
- sprintf(
226
- 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
227
- $location,
228
- implode(', ', $protocols)
229
- ),
230
- $request,
231
- $response
232
- );
233
- }
234
-
235
- return $location;
236
- }
237
- }
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Exception\BadResponseException;
5
+ use GuzzleHttp\Exception\TooManyRedirectsException;
6
+ use GuzzleHttp\Promise\PromiseInterface;
7
+ use GuzzleHttp\Psr7;
8
+ use Psr\Http\Message\RequestInterface;
9
+ use Psr\Http\Message\ResponseInterface;
10
+ use Psr\Http\Message\UriInterface;
11
+
12
+ /**
13
+ * Request redirect middleware.
14
+ *
15
+ * Apply this middleware like other middleware using
16
+ * {@see GuzzleHttp\Middleware::redirect()}.
17
+ */
18
+ class RedirectMiddleware
19
+ {
20
+ const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
21
+
22
+ const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
23
+
24
+ public static $defaultSettings = [
25
+ 'max' => 5,
26
+ 'protocols' => ['http', 'https'],
27
+ 'strict' => false,
28
+ 'referer' => false,
29
+ 'track_redirects' => false,
30
+ ];
31
+
32
+ /** @var callable */
33
+ private $nextHandler;
34
+
35
+ /**
36
+ * @param callable $nextHandler Next handler to invoke.
37
+ */
38
+ public function __construct(callable $nextHandler)
39
+ {
40
+ $this->nextHandler = $nextHandler;
41
+ }
42
+
43
+ /**
44
+ * @param RequestInterface $request
45
+ * @param array $options
46
+ *
47
+ * @return PromiseInterface
48
+ */
49
+ public function __invoke(RequestInterface $request, array $options)
50
+ {
51
+ $fn = $this->nextHandler;
52
+
53
+ if (empty($options['allow_redirects'])) {
54
+ return $fn($request, $options);
55
+ }
56
+
57
+ if ($options['allow_redirects'] === true) {
58
+ $options['allow_redirects'] = self::$defaultSettings;
59
+ } elseif (!is_array($options['allow_redirects'])) {
60
+ throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
61
+ } else {
62
+ // Merge the default settings with the provided settings
63
+ $options['allow_redirects'] += self::$defaultSettings;
64
+ }
65
+
66
+ if (empty($options['allow_redirects']['max'])) {
67
+ return $fn($request, $options);
68
+ }
69
+
70
+ return $fn($request, $options)
71
+ ->then(function (ResponseInterface $response) use ($request, $options) {
72
+ return $this->checkRedirect($request, $options, $response);
73
+ });
74
+ }
75
+
76
+ /**
77
+ * @param RequestInterface $request
78
+ * @param array $options
79
+ * @param ResponseInterface $response
80
+ *
81
+ * @return ResponseInterface|PromiseInterface
82
+ */
83
+ public function checkRedirect(
84
+ RequestInterface $request,
85
+ array $options,
86
+ ResponseInterface $response
87
+ ) {
88
+ if (substr($response->getStatusCode(), 0, 1) != '3'
89
+ || !$response->hasHeader('Location')
90
+ ) {
91
+ return $response;
92
+ }
93
+
94
+ $this->guardMax($request, $options);
95
+ $nextRequest = $this->modifyRequest($request, $options, $response);
96
+
97
+ if (isset($options['allow_redirects']['on_redirect'])) {
98
+ call_user_func(
99
+ $options['allow_redirects']['on_redirect'],
100
+ $request,
101
+ $response,
102
+ $nextRequest->getUri()
103
+ );
104
+ }
105
+
106
+ /** @var PromiseInterface|ResponseInterface $promise */
107
+ $promise = $this($nextRequest, $options);
108
+
109
+ // Add headers to be able to track history of redirects.
110
+ if (!empty($options['allow_redirects']['track_redirects'])) {
111
+ return $this->withTracking(
112
+ $promise,
113
+ (string) $nextRequest->getUri(),
114
+ $response->getStatusCode()
115
+ );
116
+ }
117
+
118
+ return $promise;
119
+ }
120
+
121
+ /**
122
+ * Enable tracking on promise.
123
+ *
124
+ * @return PromiseInterface
125
+ */
126
+ private function withTracking(PromiseInterface $promise, $uri, $statusCode)
127
+ {
128
+ return $promise->then(
129
+ function (ResponseInterface $response) use ($uri, $statusCode) {
130
+ // Note that we are pushing to the front of the list as this
131
+ // would be an earlier response than what is currently present
132
+ // in the history header.
133
+ $historyHeader = $response->getHeader(self::HISTORY_HEADER);
134
+ $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
135
+ array_unshift($historyHeader, $uri);
136
+ array_unshift($statusHeader, $statusCode);
137
+ return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
138
+ ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
139
+ }
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Check for too many redirects
145
+ *
146
+ * @return void
147
+ *
148
+ * @throws TooManyRedirectsException Too many redirects.
149
+ */
150
+ private function guardMax(RequestInterface $request, array &$options)
151
+ {
152
+ $current = isset($options['__redirect_count'])
153
+ ? $options['__redirect_count']
154
+ : 0;
155
+ $options['__redirect_count'] = $current + 1;
156
+ $max = $options['allow_redirects']['max'];
157
+
158
+ if ($options['__redirect_count'] > $max) {
159
+ throw new TooManyRedirectsException(
160
+ "Will not follow more than {$max} redirects",
161
+ $request
162
+ );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * @param RequestInterface $request
168
+ * @param array $options
169
+ * @param ResponseInterface $response
170
+ *
171
+ * @return RequestInterface
172
+ */
173
+ public function modifyRequest(
174
+ RequestInterface $request,
175
+ array $options,
176
+ ResponseInterface $response
177
+ ) {
178
+ // Request modifications to apply.
179
+ $modify = [];
180
+ $protocols = $options['allow_redirects']['protocols'];
181
+
182
+ // Use a GET request if this is an entity enclosing request and we are
183
+ // not forcing RFC compliance, but rather emulating what all browsers
184
+ // would do.
185
+ $statusCode = $response->getStatusCode();
186
+ if ($statusCode == 303 ||
187
+ ($statusCode <= 302 && !$options['allow_redirects']['strict'])
188
+ ) {
189
+ $modify['method'] = 'GET';
190
+ $modify['body'] = '';
191
+ }
192
+
193
+ $modify['uri'] = $this->redirectUri($request, $response, $protocols);
194
+ Psr7\rewind_body($request);
195
+
196
+ // Add the Referer header if it is told to do so and only
197
+ // add the header if we are not redirecting from https to http.
198
+ if ($options['allow_redirects']['referer']
199
+ && $modify['uri']->getScheme() === $request->getUri()->getScheme()
200
+ ) {
201
+ $uri = $request->getUri()->withUserInfo('');
202
+ $modify['set_headers']['Referer'] = (string) $uri;
203
+ } else {
204
+ $modify['remove_headers'][] = 'Referer';
205
+ }
206
+
207
+ // Remove Authorization header if host is different.
208
+ if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
209
+ $modify['remove_headers'][] = 'Authorization';
210
+ }
211
+
212
+ return Psr7\modify_request($request, $modify);
213
+ }
214
+
215
+ /**
216
+ * Set the appropriate URL on the request based on the location header
217
+ *
218
+ * @param RequestInterface $request
219
+ * @param ResponseInterface $response
220
+ * @param array $protocols
221
+ *
222
+ * @return UriInterface
223
+ */
224
+ private function redirectUri(
225
+ RequestInterface $request,
226
+ ResponseInterface $response,
227
+ array $protocols
228
+ ) {
229
+ $location = Psr7\UriResolver::resolve(
230
+ $request->getUri(),
231
+ new Psr7\Uri($response->getHeaderLine('Location'))
232
+ );
233
+
234
+ // Ensure that the redirect URI is allowed based on the protocols.
235
+ if (!in_array($location->getScheme(), $protocols)) {
236
+ throw new BadResponseException(
237
+ sprintf(
238
+ 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
239
+ $location,
240
+ implode(', ', $protocols)
241
+ ),
242
+ $request,
243
+ $response
244
+ );
245
+ }
246
+
247
+ return $location;
248
+ }
249
+ }
vendor/guzzlehttp/guzzle/src/RequestOptions.php CHANGED
@@ -1,255 +1,263 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- /**
5
- * This class contains a list of built-in Guzzle request options.
6
- *
7
- * More documentation for each option can be found at http://guzzlephp.org/.
8
- *
9
- * @link http://docs.guzzlephp.org/en/v6/request-options.html
10
- */
11
- final class RequestOptions
12
- {
13
- /**
14
- * allow_redirects: (bool|array) Controls redirect behavior. Pass false
15
- * to disable redirects, pass true to enable redirects, pass an
16
- * associative to provide custom redirect settings. Defaults to "false".
17
- * This option only works if your handler has the RedirectMiddleware. When
18
- * passing an associative array, you can provide the following key value
19
- * pairs:
20
- *
21
- * - max: (int, default=5) maximum number of allowed redirects.
22
- * - strict: (bool, default=false) Set to true to use strict redirects
23
- * meaning redirect POST requests with POST requests vs. doing what most
24
- * browsers do which is redirect POST requests with GET requests
25
- * - referer: (bool, default=false) Set to true to enable the Referer
26
- * header.
27
- * - protocols: (array, default=['http', 'https']) Allowed redirect
28
- * protocols.
29
- * - on_redirect: (callable) PHP callable that is invoked when a redirect
30
- * is encountered. The callable is invoked with the request, the redirect
31
- * response that was received, and the effective URI. Any return value
32
- * from the on_redirect function is ignored.
33
- */
34
- const ALLOW_REDIRECTS = 'allow_redirects';
35
-
36
- /**
37
- * auth: (array) Pass an array of HTTP authentication parameters to use
38
- * with the request. The array must contain the username in index [0],
39
- * the password in index [1], and you can optionally provide a built-in
40
- * authentication type in index [2]. Pass null to disable authentication
41
- * for a request.
42
- */
43
- const AUTH = 'auth';
44
-
45
- /**
46
- * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
47
- * Body to send in the request.
48
- */
49
- const BODY = 'body';
50
-
51
- /**
52
- * cert: (string|array) Set to a string to specify the path to a file
53
- * containing a PEM formatted SSL client side certificate. If a password
54
- * is required, then set cert to an array containing the path to the PEM
55
- * file in the first array element followed by the certificate password
56
- * in the second array element.
57
- */
58
- const CERT = 'cert';
59
-
60
- /**
61
- * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
62
- * Specifies whether or not cookies are used in a request or what cookie
63
- * jar to use or what cookies to send. This option only works if your
64
- * handler has the `cookie` middleware. Valid values are `false` and
65
- * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
66
- */
67
- const COOKIES = 'cookies';
68
-
69
- /**
70
- * connect_timeout: (float, default=0) Float describing the number of
71
- * seconds to wait while trying to connect to a server. Use 0 to wait
72
- * indefinitely (the default behavior).
73
- */
74
- const CONNECT_TIMEOUT = 'connect_timeout';
75
-
76
- /**
77
- * debug: (bool|resource) Set to true or set to a PHP stream returned by
78
- * fopen() enable debug output with the HTTP handler used to send a
79
- * request.
80
- */
81
- const DEBUG = 'debug';
82
-
83
- /**
84
- * decode_content: (bool, default=true) Specify whether or not
85
- * Content-Encoding responses (gzip, deflate, etc.) are automatically
86
- * decoded.
87
- */
88
- const DECODE_CONTENT = 'decode_content';
89
-
90
- /**
91
- * delay: (int) The amount of time to delay before sending in milliseconds.
92
- */
93
- const DELAY = 'delay';
94
-
95
- /**
96
- * expect: (bool|integer) Controls the behavior of the
97
- * "Expect: 100-Continue" header.
98
- *
99
- * Set to `true` to enable the "Expect: 100-Continue" header for all
100
- * requests that sends a body. Set to `false` to disable the
101
- * "Expect: 100-Continue" header for all requests. Set to a number so that
102
- * the size of the payload must be greater than the number in order to send
103
- * the Expect header. Setting to a number will send the Expect header for
104
- * all requests in which the size of the payload cannot be determined or
105
- * where the body is not rewindable.
106
- *
107
- * By default, Guzzle will add the "Expect: 100-Continue" header when the
108
- * size of the body of a request is greater than 1 MB and a request is
109
- * using HTTP/1.1.
110
- */
111
- const EXPECT = 'expect';
112
-
113
- /**
114
- * form_params: (array) Associative array of form field names to values
115
- * where each value is a string or array of strings. Sets the Content-Type
116
- * header to application/x-www-form-urlencoded when no Content-Type header
117
- * is already present.
118
- */
119
- const FORM_PARAMS = 'form_params';
120
-
121
- /**
122
- * headers: (array) Associative array of HTTP headers. Each value MUST be
123
- * a string or array of strings.
124
- */
125
- const HEADERS = 'headers';
126
-
127
- /**
128
- * http_errors: (bool, default=true) Set to false to disable exceptions
129
- * when a non- successful HTTP response is received. By default,
130
- * exceptions will be thrown for 4xx and 5xx responses. This option only
131
- * works if your handler has the `httpErrors` middleware.
132
- */
133
- const HTTP_ERRORS = 'http_errors';
134
-
135
- /**
136
- * json: (mixed) Adds JSON data to a request. The provided value is JSON
137
- * encoded and a Content-Type header of application/json will be added to
138
- * the request if no Content-Type header is already present.
139
- */
140
- const JSON = 'json';
141
-
142
- /**
143
- * multipart: (array) Array of associative arrays, each containing a
144
- * required "name" key mapping to the form field, name, a required
145
- * "contents" key mapping to a StreamInterface|resource|string, an
146
- * optional "headers" associative array of custom headers, and an
147
- * optional "filename" key mapping to a string to send as the filename in
148
- * the part. If no "filename" key is present, then no "filename" attribute
149
- * will be added to the part.
150
- */
151
- const MULTIPART = 'multipart';
152
-
153
- /**
154
- * on_headers: (callable) A callable that is invoked when the HTTP headers
155
- * of the response have been received but the body has not yet begun to
156
- * download.
157
- */
158
- const ON_HEADERS = 'on_headers';
159
-
160
- /**
161
- * on_stats: (callable) allows you to get access to transfer statistics of
162
- * a request and access the lower level transfer details of the handler
163
- * associated with your client. ``on_stats`` is a callable that is invoked
164
- * when a handler has finished sending a request. The callback is invoked
165
- * with transfer statistics about the request, the response received, or
166
- * the error encountered. Included in the data is the total amount of time
167
- * taken to send the request.
168
- */
169
- const ON_STATS = 'on_stats';
170
-
171
- /**
172
- * progress: (callable) Defines a function to invoke when transfer
173
- * progress is made. The function accepts the following positional
174
- * arguments: the total number of bytes expected to be downloaded, the
175
- * number of bytes downloaded so far, the number of bytes expected to be
176
- * uploaded, the number of bytes uploaded so far.
177
- */
178
- const PROGRESS = 'progress';
179
-
180
- /**
181
- * proxy: (string|array) Pass a string to specify an HTTP proxy, or an
182
- * array to specify different proxies for different protocols (where the
183
- * key is the protocol and the value is a proxy string).
184
- */
185
- const PROXY = 'proxy';
186
-
187
- /**
188
- * query: (array|string) Associative array of query string values to add
189
- * to the request. This option uses PHP's http_build_query() to create
190
- * the string representation. Pass a string value if you need more
191
- * control than what this method provides
192
- */
193
- const QUERY = 'query';
194
-
195
- /**
196
- * sink: (resource|string|StreamInterface) Where the data of the
197
- * response is written to. Defaults to a PHP temp stream. Providing a
198
- * string will write data to a file by the given name.
199
- */
200
- const SINK = 'sink';
201
-
202
- /**
203
- * synchronous: (bool) Set to true to inform HTTP handlers that you intend
204
- * on waiting on the response. This can be useful for optimizations. Note
205
- * that a promise is still returned if you are using one of the async
206
- * client methods.
207
- */
208
- const SYNCHRONOUS = 'synchronous';
209
-
210
- /**
211
- * ssl_key: (array|string) Specify the path to a file containing a private
212
- * SSL key in PEM format. If a password is required, then set to an array
213
- * containing the path to the SSL key in the first array element followed
214
- * by the password required for the certificate in the second element.
215
- */
216
- const SSL_KEY = 'ssl_key';
217
-
218
- /**
219
- * stream: Set to true to attempt to stream a response rather than
220
- * download it all up-front.
221
- */
222
- const STREAM = 'stream';
223
-
224
- /**
225
- * verify: (bool|string, default=true) Describes the SSL certificate
226
- * verification behavior of a request. Set to true to enable SSL
227
- * certificate verification using the system CA bundle when available
228
- * (the default). Set to false to disable certificate verification (this
229
- * is insecure!). Set to a string to provide the path to a CA bundle on
230
- * disk to enable verification using a custom certificate.
231
- */
232
- const VERIFY = 'verify';
233
-
234
- /**
235
- * timeout: (float, default=0) Float describing the timeout of the
236
- * request in seconds. Use 0 to wait indefinitely (the default behavior).
237
- */
238
- const TIMEOUT = 'timeout';
239
-
240
- /**
241
- * read_timeout: (float, default=default_socket_timeout ini setting) Float describing
242
- * the body read timeout, for stream requests.
243
- */
244
- const READ_TIMEOUT = 'read_timeout';
245
-
246
- /**
247
- * version: (float) Specifies the HTTP protocol version to attempt to use.
248
- */
249
- const VERSION = 'version';
250
-
251
- /**
252
- * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
253
- */
254
- const FORCE_IP_RESOLVE = 'force_ip_resolve';
255
- }
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ /**
5
+ * This class contains a list of built-in Guzzle request options.
6
+ *
7
+ * More documentation for each option can be found at http://guzzlephp.org/.
8
+ *
9
+ * @link http://docs.guzzlephp.org/en/v6/request-options.html
10
+ */
11
+ final class RequestOptions
12
+ {
13
+ /**
14
+ * allow_redirects: (bool|array) Controls redirect behavior. Pass false
15
+ * to disable redirects, pass true to enable redirects, pass an
16
+ * associative to provide custom redirect settings. Defaults to "false".
17
+ * This option only works if your handler has the RedirectMiddleware. When
18
+ * passing an associative array, you can provide the following key value
19
+ * pairs:
20
+ *
21
+ * - max: (int, default=5) maximum number of allowed redirects.
22
+ * - strict: (bool, default=false) Set to true to use strict redirects
23
+ * meaning redirect POST requests with POST requests vs. doing what most
24
+ * browsers do which is redirect POST requests with GET requests
25
+ * - referer: (bool, default=false) Set to true to enable the Referer
26
+ * header.
27
+ * - protocols: (array, default=['http', 'https']) Allowed redirect
28
+ * protocols.
29
+ * - on_redirect: (callable) PHP callable that is invoked when a redirect
30
+ * is encountered. The callable is invoked with the request, the redirect
31
+ * response that was received, and the effective URI. Any return value
32
+ * from the on_redirect function is ignored.
33
+ */
34
+ const ALLOW_REDIRECTS = 'allow_redirects';
35
+
36
+ /**
37
+ * auth: (array) Pass an array of HTTP authentication parameters to use
38
+ * with the request. The array must contain the username in index [0],
39
+ * the password in index [1], and you can optionally provide a built-in
40
+ * authentication type in index [2]. Pass null to disable authentication
41
+ * for a request.
42
+ */
43
+ const AUTH = 'auth';
44
+
45
+ /**
46
+ * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
47
+ * Body to send in the request.
48
+ */
49
+ const BODY = 'body';
50
+
51
+ /**
52
+ * cert: (string|array) Set to a string to specify the path to a file
53
+ * containing a PEM formatted SSL client side certificate. If a password
54
+ * is required, then set cert to an array containing the path to the PEM
55
+ * file in the first array element followed by the certificate password
56
+ * in the second array element.
57
+ */
58
+ const CERT = 'cert';
59
+
60
+ /**
61
+ * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
62
+ * Specifies whether or not cookies are used in a request or what cookie
63
+ * jar to use or what cookies to send. This option only works if your
64
+ * handler has the `cookie` middleware. Valid values are `false` and
65
+ * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
66
+ */
67
+ const COOKIES = 'cookies';
68
+
69
+ /**
70
+ * connect_timeout: (float, default=0) Float describing the number of
71
+ * seconds to wait while trying to connect to a server. Use 0 to wait
72
+ * indefinitely (the default behavior).
73
+ */
74
+ const CONNECT_TIMEOUT = 'connect_timeout';
75
+
76
+ /**
77
+ * debug: (bool|resource) Set to true or set to a PHP stream returned by
78
+ * fopen() enable debug output with the HTTP handler used to send a
79
+ * request.
80
+ */
81
+ const DEBUG = 'debug';
82
+
83
+ /**
84
+ * decode_content: (bool, default=true) Specify whether or not
85
+ * Content-Encoding responses (gzip, deflate, etc.) are automatically
86
+ * decoded.
87
+ */
88
+ const DECODE_CONTENT = 'decode_content';
89
+
90
+ /**
91
+ * delay: (int) The amount of time to delay before sending in milliseconds.
92
+ */
93
+ const DELAY = 'delay';
94
+
95
+ /**
96
+ * expect: (bool|integer) Controls the behavior of the
97
+ * "Expect: 100-Continue" header.
98
+ *
99
+ * Set to `true` to enable the "Expect: 100-Continue" header for all
100
+ * requests that sends a body. Set to `false` to disable the
101
+ * "Expect: 100-Continue" header for all requests. Set to a number so that
102
+ * the size of the payload must be greater than the number in order to send
103
+ * the Expect header. Setting to a number will send the Expect header for
104
+ * all requests in which the size of the payload cannot be determined or
105
+ * where the body is not rewindable.
106
+ *
107
+ * By default, Guzzle will add the "Expect: 100-Continue" header when the
108
+ * size of the body of a request is greater than 1 MB and a request is
109
+ * using HTTP/1.1.
110
+ */
111
+ const EXPECT = 'expect';
112
+
113
+ /**
114
+ * form_params: (array) Associative array of form field names to values
115
+ * where each value is a string or array of strings. Sets the Content-Type
116
+ * header to application/x-www-form-urlencoded when no Content-Type header
117
+ * is already present.
118
+ */
119
+ const FORM_PARAMS = 'form_params';
120
+
121
+ /**
122
+ * headers: (array) Associative array of HTTP headers. Each value MUST be
123
+ * a string or array of strings.
124
+ */
125
+ const HEADERS = 'headers';
126
+
127
+ /**
128
+ * http_errors: (bool, default=true) Set to false to disable exceptions
129
+ * when a non- successful HTTP response is received. By default,
130
+ * exceptions will be thrown for 4xx and 5xx responses. This option only
131
+ * works if your handler has the `httpErrors` middleware.
132
+ */
133
+ const HTTP_ERRORS = 'http_errors';
134
+
135
+ /**
136
+ * idn: (bool|int, default=true) A combination of IDNA_* constants for
137
+ * idn_to_ascii() PHP's function (see "options" parameter). Set to false to
138
+ * disable IDN support completely, or to true to use the default
139
+ * configuration (IDNA_DEFAULT constant).
140
+ */
141
+ const IDN_CONVERSION = 'idn_conversion';
142
+
143
+ /**
144
+ * json: (mixed) Adds JSON data to a request. The provided value is JSON
145
+ * encoded and a Content-Type header of application/json will be added to
146
+ * the request if no Content-Type header is already present.
147
+ */
148
+ const JSON = 'json';
149
+
150
+ /**
151
+ * multipart: (array) Array of associative arrays, each containing a
152
+ * required "name" key mapping to the form field, name, a required
153
+ * "contents" key mapping to a StreamInterface|resource|string, an
154
+ * optional "headers" associative array of custom headers, and an
155
+ * optional "filename" key mapping to a string to send as the filename in
156
+ * the part. If no "filename" key is present, then no "filename" attribute
157
+ * will be added to the part.
158
+ */
159
+ const MULTIPART = 'multipart';
160
+
161
+ /**
162
+ * on_headers: (callable) A callable that is invoked when the HTTP headers
163
+ * of the response have been received but the body has not yet begun to
164
+ * download.
165
+ */
166
+ const ON_HEADERS = 'on_headers';
167
+
168
+ /**
169
+ * on_stats: (callable) allows you to get access to transfer statistics of
170
+ * a request and access the lower level transfer details of the handler
171
+ * associated with your client. ``on_stats`` is a callable that is invoked
172
+ * when a handler has finished sending a request. The callback is invoked
173
+ * with transfer statistics about the request, the response received, or
174
+ * the error encountered. Included in the data is the total amount of time
175
+ * taken to send the request.
176
+ */
177
+ const ON_STATS = 'on_stats';
178
+
179
+ /**
180
+ * progress: (callable) Defines a function to invoke when transfer
181
+ * progress is made. The function accepts the following positional
182
+ * arguments: the total number of bytes expected to be downloaded, the
183
+ * number of bytes downloaded so far, the number of bytes expected to be
184
+ * uploaded, the number of bytes uploaded so far.
185
+ */
186
+ const PROGRESS = 'progress';
187
+
188
+ /**
189
+ * proxy: (string|array) Pass a string to specify an HTTP proxy, or an
190
+ * array to specify different proxies for different protocols (where the
191
+ * key is the protocol and the value is a proxy string).
192
+ */
193
+ const PROXY = 'proxy';
194
+
195
+ /**
196
+ * query: (array|string) Associative array of query string values to add
197
+ * to the request. This option uses PHP's http_build_query() to create
198
+ * the string representation. Pass a string value if you need more
199
+ * control than what this method provides
200
+ */
201
+ const QUERY = 'query';
202
+
203
+ /**
204
+ * sink: (resource|string|StreamInterface) Where the data of the
205
+ * response is written to. Defaults to a PHP temp stream. Providing a
206
+ * string will write data to a file by the given name.
207
+ */
208
+ const SINK = 'sink';
209
+
210
+ /**
211
+ * synchronous: (bool) Set to true to inform HTTP handlers that you intend
212
+ * on waiting on the response. This can be useful for optimizations. Note
213
+ * that a promise is still returned if you are using one of the async
214
+ * client methods.
215
+ */
216
+ const SYNCHRONOUS = 'synchronous';
217
+
218
+ /**
219
+ * ssl_key: (array|string) Specify the path to a file containing a private
220
+ * SSL key in PEM format. If a password is required, then set to an array
221
+ * containing the path to the SSL key in the first array element followed
222
+ * by the password required for the certificate in the second element.
223
+ */
224
+ const SSL_KEY = 'ssl_key';
225
+
226
+ /**
227
+ * stream: Set to true to attempt to stream a response rather than
228
+ * download it all up-front.
229
+ */
230
+ const STREAM = 'stream';
231
+
232
+ /**
233
+ * verify: (bool|string, default=true) Describes the SSL certificate
234
+ * verification behavior of a request. Set to true to enable SSL
235
+ * certificate verification using the system CA bundle when available
236
+ * (the default). Set to false to disable certificate verification (this
237
+ * is insecure!). Set to a string to provide the path to a CA bundle on
238
+ * disk to enable verification using a custom certificate.
239
+ */
240
+ const VERIFY = 'verify';
241
+
242
+ /**
243
+ * timeout: (float, default=0) Float describing the timeout of the
244
+ * request in seconds. Use 0 to wait indefinitely (the default behavior).
245
+ */
246
+ const TIMEOUT = 'timeout';
247
+
248
+ /**
249
+ * read_timeout: (float, default=default_socket_timeout ini setting) Float describing
250
+ * the body read timeout, for stream requests.
251
+ */
252
+ const READ_TIMEOUT = 'read_timeout';
253
+
254
+ /**
255
+ * version: (float) Specifies the HTTP protocol version to attempt to use.
256
+ */
257
+ const VERSION = 'version';
258
+
259
+ /**
260
+ * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
261
+ */
262
+ const FORCE_IP_RESOLVE = 'force_ip_resolve';
263
+ }
vendor/guzzlehttp/guzzle/src/RetryMiddleware.php CHANGED
@@ -1,115 +1,128 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Promise\PromiseInterface;
5
- use GuzzleHttp\Promise\RejectedPromise;
6
- use GuzzleHttp\Psr7;
7
- use Psr\Http\Message\RequestInterface;
8
- use Psr\Http\Message\ResponseInterface;
9
-
10
- /**
11
- * Middleware that retries requests based on the boolean result of
12
- * invoking the provided "decider" function.
13
- */
14
- class RetryMiddleware
15
- {
16
- /** @var callable */
17
- private $nextHandler;
18
-
19
- /** @var callable */
20
- private $decider;
21
-
22
- /** @var callable */
23
- private $delay;
24
-
25
- /**
26
- * @param callable $decider Function that accepts the number of retries,
27
- * a request, [response], and [exception] and
28
- * returns true if the request is to be
29
- * retried.
30
- * @param callable $nextHandler Next handler to invoke.
31
- * @param callable $delay Function that accepts the number of retries
32
- * and [response] and returns the number of
33
- * milliseconds to delay.
34
- */
35
- public function __construct(
36
- callable $decider,
37
- callable $nextHandler,
38
- callable $delay = null
39
- ) {
40
- $this->decider = $decider;
41
- $this->nextHandler = $nextHandler;
42
- $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
43
- }
44
-
45
- /**
46
- * Default exponential backoff delay function.
47
- *
48
- * @param int $retries
49
- *
50
- * @return int
51
- */
52
- public static function exponentialDelay($retries)
53
- {
54
- return (int) pow(2, $retries - 1);
55
- }
56
-
57
- /**
58
- * @param RequestInterface $request
59
- * @param array $options
60
- *
61
- * @return PromiseInterface
62
- */
63
- public function __invoke(RequestInterface $request, array $options)
64
- {
65
- if (!isset($options['retries'])) {
66
- $options['retries'] = 0;
67
- }
68
-
69
- $fn = $this->nextHandler;
70
- return $fn($request, $options)
71
- ->then(
72
- $this->onFulfilled($request, $options),
73
- $this->onRejected($request, $options)
74
- );
75
- }
76
-
77
- private function onFulfilled(RequestInterface $req, array $options)
78
- {
79
- return function ($value) use ($req, $options) {
80
- if (!call_user_func(
81
- $this->decider,
82
- $options['retries'],
83
- $req,
84
- $value,
85
- null
86
- )) {
87
- return $value;
88
- }
89
- return $this->doRetry($req, $options, $value);
90
- };
91
- }
92
-
93
- private function onRejected(RequestInterface $req, array $options)
94
- {
95
- return function ($reason) use ($req, $options) {
96
- if (!call_user_func(
97
- $this->decider,
98
- $options['retries'],
99
- $req,
100
- null,
101
- $reason
102
- )) {
103
- return \GuzzleHttp\Promise\rejection_for($reason);
104
- }
105
- return $this->doRetry($req, $options);
106
- };
107
- }
108
-
109
- private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
110
- {
111
- $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
112
-
113
- return $this($request, $options);
114
- }
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Promise\PromiseInterface;
5
+ use GuzzleHttp\Promise\RejectedPromise;
6
+ use GuzzleHttp\Psr7;
7
+ use Psr\Http\Message\RequestInterface;
8
+ use Psr\Http\Message\ResponseInterface;
9
+
10
+ /**
11
+ * Middleware that retries requests based on the boolean result of
12
+ * invoking the provided "decider" function.
13
+ */
14
+ class RetryMiddleware
15
+ {
16
+ /** @var callable */
17
+ private $nextHandler;
18
+
19
+ /** @var callable */
20
+ private $decider;
21
+
22
+ /** @var callable */
23
+ private $delay;
24
+
25
+ /**
26
+ * @param callable $decider Function that accepts the number of retries,
27
+ * a request, [response], and [exception] and
28
+ * returns true if the request is to be
29
+ * retried.
30
+ * @param callable $nextHandler Next handler to invoke.
31
+ * @param callable $delay Function that accepts the number of retries
32
+ * and [response] and returns the number of
33
+ * milliseconds to delay.
34
+ */
35
+ public function __construct(
36
+ callable $decider,
37
+ callable $nextHandler,
38
+ callable $delay = null
39
+ ) {
40
+ $this->decider = $decider;
41
+ $this->nextHandler = $nextHandler;
42
+ $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
43
+ }
44
+
45
+ /**
46
+ * Default exponential backoff delay function.
47
+ *
48
+ * @param int $retries
49
+ *
50
+ * @return int milliseconds.
51
+ */
52
+ public static function exponentialDelay($retries)
53
+ {
54
+ return (int) pow(2, $retries - 1) * 1000;
55
+ }
56
+
57
+ /**
58
+ * @param RequestInterface $request
59
+ * @param array $options
60
+ *
61
+ * @return PromiseInterface
62
+ */
63
+ public function __invoke(RequestInterface $request, array $options)
64
+ {
65
+ if (!isset($options['retries'])) {
66
+ $options['retries'] = 0;
67
+ }
68
+
69
+ $fn = $this->nextHandler;
70
+ return $fn($request, $options)
71
+ ->then(
72
+ $this->onFulfilled($request, $options),
73
+ $this->onRejected($request, $options)
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Execute fulfilled closure
79
+ *
80
+ * @return mixed
81
+ */
82
+ private function onFulfilled(RequestInterface $req, array $options)
83
+ {
84
+ return function ($value) use ($req, $options) {
85
+ if (!call_user_func(
86
+ $this->decider,
87
+ $options['retries'],
88
+ $req,
89
+ $value,
90
+ null
91
+ )) {
92
+ return $value;
93
+ }
94
+ return $this->doRetry($req, $options, $value);
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Execute rejected closure
100
+ *
101
+ * @return callable
102
+ */
103
+ private function onRejected(RequestInterface $req, array $options)
104
+ {
105
+ return function ($reason) use ($req, $options) {
106
+ if (!call_user_func(
107
+ $this->decider,
108
+ $options['retries'],
109
+ $req,
110
+ null,
111
+ $reason
112
+ )) {
113
+ return \GuzzleHttp\Promise\rejection_for($reason);
114
+ }
115
+ return $this->doRetry($req, $options);
116
+ };
117
+ }
118
+
119
+ /**
120
+ * @return self
121
+ */
122
+ private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
123
+ {
124
+ $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
125
+
126
+ return $this($request, $options);
127
+ }
128
+ }
vendor/guzzlehttp/guzzle/src/TransferStats.php CHANGED
@@ -1,126 +1,126 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use Psr\Http\Message\RequestInterface;
5
- use Psr\Http\Message\ResponseInterface;
6
- use Psr\Http\Message\UriInterface;
7
-
8
- /**
9
- * Represents data at the point after it was transferred either successfully
10
- * or after a network error.
11
- */
12
- final class TransferStats
13
- {
14
- private $request;
15
- private $response;
16
- private $transferTime;
17
- private $handlerStats;
18
- private $handlerErrorData;
19
-
20
- /**
21
- * @param RequestInterface $request Request that was sent.
22
- * @param ResponseInterface $response Response received (if any)
23
- * @param float|null $transferTime Total handler transfer time.
24
- * @param mixed $handlerErrorData Handler error data.
25
- * @param array $handlerStats Handler specific stats.
26
- */
27
- public function __construct(
28
- RequestInterface $request,
29
- ResponseInterface $response = null,
30
- $transferTime = null,
31
- $handlerErrorData = null,
32
- $handlerStats = []
33
- ) {
34
- $this->request = $request;
35
- $this->response = $response;
36
- $this->transferTime = $transferTime;
37
- $this->handlerErrorData = $handlerErrorData;
38
- $this->handlerStats = $handlerStats;
39
- }
40
-
41
- /**
42
- * @return RequestInterface
43
- */
44
- public function getRequest()
45
- {
46
- return $this->request;
47
- }
48
-
49
- /**
50
- * Returns the response that was received (if any).
51
- *
52
- * @return ResponseInterface|null
53
- */
54
- public function getResponse()
55
- {
56
- return $this->response;
57
- }
58
-
59
- /**
60
- * Returns true if a response was received.
61
- *
62
- * @return bool
63
- */
64
- public function hasResponse()
65
- {
66
- return $this->response !== null;
67
- }
68
-
69
- /**
70
- * Gets handler specific error data.
71
- *
72
- * This might be an exception, a integer representing an error code, or
73
- * anything else. Relying on this value assumes that you know what handler
74
- * you are using.
75
- *
76
- * @return mixed
77
- */
78
- public function getHandlerErrorData()
79
- {
80
- return $this->handlerErrorData;
81
- }
82
-
83
- /**
84
- * Get the effective URI the request was sent to.
85
- *
86
- * @return UriInterface
87
- */
88
- public function getEffectiveUri()
89
- {
90
- return $this->request->getUri();
91
- }
92
-
93
- /**
94
- * Get the estimated time the request was being transferred by the handler.
95
- *
96
- * @return float Time in seconds.
97
- */
98
- public function getTransferTime()
99
- {
100
- return $this->transferTime;
101
- }
102
-
103
- /**
104
- * Gets an array of all of the handler specific transfer data.
105
- *
106
- * @return array
107
- */
108
- public function getHandlerStats()
109
- {
110
- return $this->handlerStats;
111
- }
112
-
113
- /**
114
- * Get a specific handler statistic from the handler by name.
115
- *
116
- * @param string $stat Handler specific transfer stat to retrieve.
117
- *
118
- * @return mixed|null
119
- */
120
- public function getHandlerStat($stat)
121
- {
122
- return isset($this->handlerStats[$stat])
123
- ? $this->handlerStats[$stat]
124
- : null;
125
- }
126
- }
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use Psr\Http\Message\RequestInterface;
5
+ use Psr\Http\Message\ResponseInterface;
6
+ use Psr\Http\Message\UriInterface;
7
+
8
+ /**
9
+ * Represents data at the point after it was transferred either successfully
10
+ * or after a network error.
11
+ */
12
+ final class TransferStats
13
+ {
14
+ private $request;
15
+ private $response;
16
+ private $transferTime;
17
+ private $handlerStats;
18
+ private $handlerErrorData;
19
+
20
+ /**
21
+ * @param RequestInterface $request Request that was sent.
22
+ * @param ResponseInterface|null $response Response received (if any)
23
+ * @param float|null $transferTime Total handler transfer time.
24
+ * @param mixed $handlerErrorData Handler error data.
25
+ * @param array $handlerStats Handler specific stats.
26
+ */
27
+ public function __construct(
28
+ RequestInterface $request,
29
+ ResponseInterface $response = null,
30
+ $transferTime = null,
31
+ $handlerErrorData = null,
32
+ $handlerStats = []
33
+ ) {
34
+ $this->request = $request;
35
+ $this->response = $response;
36
+ $this->transferTime = $transferTime;
37
+ $this->handlerErrorData = $handlerErrorData;
38
+ $this->handlerStats = $handlerStats;
39
+ }
40
+
41
+ /**
42
+ * @return RequestInterface
43
+ */
44
+ public function getRequest()
45
+ {
46
+ return $this->request;
47
+ }
48
+
49
+ /**
50
+ * Returns the response that was received (if any).
51
+ *
52
+ * @return ResponseInterface|null
53
+ */
54
+ public function getResponse()
55
+ {
56
+ return $this->response;
57
+ }
58
+
59
+ /**
60
+ * Returns true if a response was received.
61
+ *
62
+ * @return bool
63
+ */
64
+ public function hasResponse()
65
+ {
66
+ return $this->response !== null;
67
+ }
68
+
69
+ /**
70
+ * Gets handler specific error data.
71
+ *
72
+ * This might be an exception, a integer representing an error code, or
73
+ * anything else. Relying on this value assumes that you know what handler
74
+ * you are using.
75
+ *
76
+ * @return mixed
77
+ */
78
+ public function getHandlerErrorData()
79
+ {
80
+ return $this->handlerErrorData;
81
+ }
82
+
83
+ /**
84
+ * Get the effective URI the request was sent to.
85
+ *
86
+ * @return UriInterface
87
+ */
88
+ public function getEffectiveUri()
89
+ {
90
+ return $this->request->getUri();
91
+ }
92
+
93
+ /**
94
+ * Get the estimated time the request was being transferred by the handler.
95
+ *
96
+ * @return float|null Time in seconds.
97
+ */
98
+ public function getTransferTime()
99
+ {
100
+ return $this->transferTime;
101
+ }
102
+
103
+ /**
104
+ * Gets an array of all of the handler specific transfer data.
105
+ *
106
+ * @return array
107
+ */
108
+ public function getHandlerStats()
109
+ {
110
+ return $this->handlerStats;
111
+ }
112
+
113
+ /**
114
+ * Get a specific handler statistic from the handler by name.
115
+ *
116
+ * @param string $stat Handler specific transfer stat to retrieve.
117
+ *
118
+ * @return mixed|null
119
+ */
120
+ public function getHandlerStat($stat)
121
+ {
122
+ return isset($this->handlerStats[$stat])
123
+ ? $this->handlerStats[$stat]
124
+ : null;
125
+ }
126
+ }
vendor/guzzlehttp/guzzle/src/functions.php CHANGED
@@ -1,346 +1,346 @@
1
- <?php
2
- namespace GuzzleHttp;
3
-
4
- use GuzzleHttp\Handler\CurlHandler;
5
- use GuzzleHttp\Handler\CurlMultiHandler;
6
- use GuzzleHttp\Handler\Proxy;
7
- use GuzzleHttp\Handler\StreamHandler;
8
-
9
- /**
10
- * Expands a URI template
11
- *
12
- * @param string $template URI template
13
- * @param array $variables Template variables
14
- *
15
- * @return string
16
- */
17
- function uri_template($template, array $variables)
18
- {
19
- if (extension_loaded('uri_template')) {
20
- // @codeCoverageIgnoreStart
21
- return \uri_template($template, $variables);
22
- // @codeCoverageIgnoreEnd
23
- }
24
-
25
- static $uriTemplate;
26
- if (!$uriTemplate) {
27
- $uriTemplate = new UriTemplate();
28
- }
29
-
30
- return $uriTemplate->expand($template, $variables);
31
- }
32
-
33
- /**
34
- * Debug function used to describe the provided value type and class.
35
- *
36
- * @param mixed $input
37
- *
38
- * @return string Returns a string containing the type of the variable and
39
- * if a class is provided, the class name.
40
- */
41
- function describe_type($input)
42
- {
43
- switch (gettype($input)) {
44
- case 'object':
45
- return 'object(' . get_class($input) . ')';
46
- case 'array':
47
- return 'array(' . count($input) . ')';
48
- default:
49
- ob_start();
50
- var_dump($input);
51
- // normalize float vs double
52
- return str_replace('double(', 'float(', rtrim(ob_get_clean()));
53
- }
54
- }
55
-
56
- /**
57
- * Parses an array of header lines into an associative array of headers.
58
- *
59
- * @param array $lines Header lines array of strings in the following
60
- * format: "Name: Value"
61
- * @return array
62
- */
63
- function headers_from_lines($lines)
64
- {
65
- $headers = [];
66
-
67
- foreach ($lines as $line) {
68
- $parts = explode(':', $line, 2);
69
- $headers[trim($parts[0])][] = isset($parts[1])
70
- ? trim($parts[1])
71
- : null;
72
- }
73
-
74
- return $headers;
75
- }
76
-
77
- /**
78
- * Returns a debug stream based on the provided variable.
79
- *
80
- * @param mixed $value Optional value
81
- *
82
- * @return resource
83
- */
84
- function debug_resource($value = null)
85
- {
86
- if (is_resource($value)) {
87
- return $value;
88
- } elseif (defined('STDOUT')) {
89
- return STDOUT;
90
- }
91
-
92
- return fopen('php://output', 'w');
93
- }
94
-
95
- /**
96
- * Chooses and creates a default handler to use based on the environment.
97
- *
98
- * The returned handler is not wrapped by any default middlewares.
99
- *
100
- * @throws \RuntimeException if no viable Handler is available.
101
- * @return callable Returns the best handler for the given system.
102
- */
103
- function choose_handler()
104
- {
105
- $handler = null;
106
- if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
107
- $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
108
- } elseif (function_exists('curl_exec')) {
109
- $handler = new CurlHandler();
110
- } elseif (function_exists('curl_multi_exec')) {
111
- $handler = new CurlMultiHandler();
112
- }
113
-
114
- if (ini_get('allow_url_fopen')) {
115
- $handler = $handler
116
- ? Proxy::wrapStreaming($handler, new StreamHandler())
117
- : new StreamHandler();
118
- } elseif (!$handler) {
119
- throw new \RuntimeException('GuzzleHttp requires cURL, the '
120
- . 'allow_url_fopen ini setting, or a custom HTTP handler.');
121
- }
122
-
123
- return $handler;
124
- }
125
-
126
- /**
127
- * Get the default User-Agent string to use with Guzzle
128
- *
129
- * @return string
130
- */
131
- function default_user_agent()
132
- {
133
- static $defaultAgent = '';
134
-
135
- if (!$defaultAgent) {
136
- $defaultAgent = 'GuzzleHttp/' . Client::VERSION;
137
- if (extension_loaded('curl') && function_exists('curl_version')) {
138
- $defaultAgent .= ' curl/' . \curl_version()['version'];
139
- }
140
- $defaultAgent .= ' PHP/' . PHP_VERSION;
141
- }
142
-
143
- return $defaultAgent;
144
- }
145
-
146
- /**
147
- * Returns the default cacert bundle for the current system.
148
- *
149
- * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
150
- * If those settings are not configured, then the common locations for
151
- * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
152
- * and Windows are checked. If any of these file locations are found on
153
- * disk, they will be utilized.
154
- *
155
- * Note: the result of this function is cached for subsequent calls.
156
- *
157
- * @return string
158
- * @throws \RuntimeException if no bundle can be found.
159
- */
160
- function default_ca_bundle()
161
- {
162
- static $cached = null;
163
- static $cafiles = [
164
- // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
165
- '/etc/pki/tls/certs/ca-bundle.crt',
166
- // Ubuntu, Debian (provided by the ca-certificates package)
167
- '/etc/ssl/certs/ca-certificates.crt',
168
- // FreeBSD (provided by the ca_root_nss package)
169
- '/usr/local/share/certs/ca-root-nss.crt',
170
- // SLES 12 (provided by the ca-certificates package)
171
- '/var/lib/ca-certificates/ca-bundle.pem',
172
- // OS X provided by homebrew (using the default path)
173
- '/usr/local/etc/openssl/cert.pem',
174
- // Google app engine
175
- '/etc/ca-certificates.crt',
176
- // Windows?
177
- 'C:\\windows\\system32\\curl-ca-bundle.crt',
178
- 'C:\\windows\\curl-ca-bundle.crt',
179
- ];
180
-
181
- if ($cached) {
182
- return $cached;
183
- }
184
-
185
- if ($ca = ini_get('openssl.cafile')) {
186
- return $cached = $ca;
187
- }
188
-
189
- if ($ca = ini_get('curl.cainfo')) {
190
- return $cached = $ca;
191
- }
192
-
193
- foreach ($cafiles as $filename) {
194
- if (file_exists($filename)) {
195
- return $cached = $filename;
196
- }
197
- }
198
-
199
- throw new \RuntimeException(
200
- <<< EOT
201
- No system CA bundle could be found in any of the the common system locations.
202
- PHP versions earlier than 5.6 are not properly configured to use the system's
203
- CA bundle by default. In order to verify peer certificates, you will need to
204
- supply the path on disk to a certificate bundle to the 'verify' request
205
- option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
206
- need a specific certificate bundle, then Mozilla provides a commonly used CA
207
- bundle which can be downloaded here (provided by the maintainer of cURL):
208
- https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
209
- you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
210
- ini setting to point to the path to the file, allowing you to omit the 'verify'
211
- request option. See http://curl.haxx.se/docs/sslcerts.html for more
212
- information.
213
- EOT
214
- );
215
- }
216
-
217
- /**
218
- * Creates an associative array of lowercase header names to the actual
219
- * header casing.
220
- *
221
- * @param array $headers
222
- *
223
- * @return array
224
- */
225
- function normalize_header_keys(array $headers)
226
- {
227
- $result = [];
228
- foreach (array_keys($headers) as $key) {
229
- $result[strtolower($key)] = $key;
230
- }
231
-
232
- return $result;
233
- }
234
-
235
- /**
236
- * Returns true if the provided host matches any of the no proxy areas.
237
- *
238
- * This method will strip a port from the host if it is present. Each pattern
239
- * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
240
- * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
241
- * "baz.foo.com", but ".foo.com" != "foo.com").
242
- *
243
- * Areas are matched in the following cases:
244
- * 1. "*" (without quotes) always matches any hosts.
245
- * 2. An exact match.
246
- * 3. The area starts with "." and the area is the last part of the host. e.g.
247
- * '.mit.edu' will match any host that ends with '.mit.edu'.
248
- *
249
- * @param string $host Host to check against the patterns.
250
- * @param array $noProxyArray An array of host patterns.
251
- *
252
- * @return bool
253
- */
254
- function is_host_in_noproxy($host, array $noProxyArray)
255
- {
256
- if (strlen($host) === 0) {
257
- throw new \InvalidArgumentException('Empty host provided');
258
- }
259
-
260
- // Strip port if present.
261
- if (strpos($host, ':')) {
262
- $host = explode($host, ':', 2)[0];
263
- }
264
-
265
- foreach ($noProxyArray as $area) {
266
- // Always match on wildcards.
267
- if ($area === '*') {
268
- return true;
269
- } elseif (empty($area)) {
270
- // Don't match on empty values.
271
- continue;
272
- } elseif ($area === $host) {
273
- // Exact matches.
274
- return true;
275
- } else {
276
- // Special match if the area when prefixed with ".". Remove any
277
- // existing leading "." and add a new leading ".".
278
- $area = '.' . ltrim($area, '.');
279
- if (substr($host, -(strlen($area))) === $area) {
280
- return true;
281
- }
282
- }
283
- }
284
-
285
- return false;
286
- }
287
-
288
- /**
289
- * Wrapper for json_decode that throws when an error occurs.
290
- *
291
- * @param string $json JSON data to parse
292
- * @param bool $assoc When true, returned objects will be converted
293
- * into associative arrays.
294
- * @param int $depth User specified recursion depth.
295
- * @param int $options Bitmask of JSON decode options.
296
- *
297
- * @return mixed
298
- * @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
299
- * @link http://www.php.net/manual/en/function.json-decode.php
300
- */
301
- function json_decode($json, $assoc = false, $depth = 512, $options = 0)
302
- {
303
- $data = \json_decode($json, $assoc, $depth, $options);
304
- if (JSON_ERROR_NONE !== json_last_error()) {
305
- throw new Exception\InvalidArgumentException(
306
- 'json_decode error: ' . json_last_error_msg()
307
- );
308
- }
309
-
310
- return $data;
311
- }
312
-
313
- /**
314
- * Wrapper for JSON encoding that throws when an error occurs.
315
- *
316
- * @param mixed $value The value being encoded
317
- * @param int $options JSON encode option bitmask
318
- * @param int $depth Set the maximum depth. Must be greater than zero.
319
- *
320
- * @return string
321
- * @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
322
- * @link http://www.php.net/manual/en/function.json-encode.php
323
- */
324
- function json_encode($value, $options = 0, $depth = 512)
325
- {
326
- $json = \json_encode($value, $options, $depth);
327
- if (JSON_ERROR_NONE !== json_last_error()) {
328
- throw new Exception\InvalidArgumentException(
329
- 'json_encode error: ' . json_last_error_msg()
330
- );
331
- }
332
-
333
- return $json;
334
- }
335
-
336
- /**
337
- * Wrapper for the hrtime() or microtime() functions
338
- * (depending on the PHP version, one of the two is used)
339
- *
340
- * @return float|mixed UNIX timestamp
341
- * @internal
342
- */
343
- function _current_time()
344
- {
345
- return function_exists('hrtime') ? hrtime(true) / 1e9 : microtime(true);
346
- }
1
+ <?php
2
+ namespace GuzzleHttp;
3
+
4
+ use GuzzleHttp\Handler\CurlHandler;
5
+ use GuzzleHttp\Handler\CurlMultiHandler;
6
+ use GuzzleHttp\Handler\Proxy;
7
+ use GuzzleHttp\Handler\StreamHandler;
8
+
9
+ /**
10
+ * Expands a URI template
11
+ *
12
+ * @param string $template URI template
13
+ * @param array $variables Template variables
14
+ *
15
+ * @return string
16
+ */
17
+ function uri_template($template, array $variables)
18
+ {
19
+ if (extension_loaded('uri_template')) {
20
+ // @codeCoverageIgnoreStart
21
+ return \uri_template($template, $variables);
22
+ // @codeCoverageIgnoreEnd
23
+ }
24
+
25
+ static $uriTemplate;
26
+ if (!$uriTemplate) {
27
+ $uriTemplate = new UriTemplate();
28
+ }
29
+
30
+ return $uriTemplate->expand($template, $variables);
31
+ }
32
+
33
+ /**
34
+ * Debug function used to describe the provided value type and class.
35
+ *
36
+ * @param mixed $input
37
+ *
38
+ * @return string Returns a string containing the type of the variable and
39
+ * if a class is provided, the class name.
40
+ */
41
+ function describe_type($input)
42
+ {
43
+ switch (gettype($input)) {
44
+ case 'object':
45
+ return 'object(' . get_class($input) . ')';
46
+ case 'array':
47
+ return 'array(' . count($input) . ')';
48
+ default:
49
+ ob_start();
50
+ var_dump($input);
51
+ // normalize float vs double
52
+ return str_replace('double(', 'float(', rtrim(ob_get_clean()));
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Parses an array of header lines into an associative array of headers.
58
+ *
59
+ * @param iterable $lines Header lines array of strings in the following
60
+ * format: "Name: Value"
61
+ * @return array
62
+ */
63
+ function headers_from_lines($lines)
64
+ {
65
+ $headers = [];
66
+
67
+ foreach ($lines as $line) {
68
+ $parts = explode(':', $line, 2);
69
+ $headers[trim($parts[0])][] = isset($parts[1])
70
+ ? trim($parts[1])
71
+ : null;
72
+ }
73
+
74
+ return $headers;
75
+ }
76
+
77
+ /**
78
+ * Returns a debug stream based on the provided variable.
79
+ *
80
+ * @param mixed $value Optional value
81
+ *
82
+ * @return resource
83
+ */
84
+ function debug_resource($value = null)
85
+ {
86
+ if (is_resource($value)) {
87
+ return $value;
88
+ } elseif (defined('STDOUT')) {
89
+ return STDOUT;
90
+ }
91
+
92
+ return fopen('php://output', 'w');
93
+ }
94
+
95
+ /**
96
+ * Chooses and creates a default handler to use based on the environment.
97
+ *
98
+ * The returned handler is not wrapped by any default middlewares.
99
+ *
100
+ * @throws \RuntimeException if no viable Handler is available.
101
+ * @return callable Returns the best handler for the given system.
102
+ */
103
+ function choose_handler()
104
+ {
105
+ $handler = null;
106
+ if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
107
+ $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
108
+ } elseif (function_exists('curl_exec')) {
109
+ $handler = new CurlHandler();
110
+ } elseif (function_exists('curl_multi_exec')) {
111
+ $handler = new CurlMultiHandler();
112
+ }
113
+
114
+ if (ini_get('allow_url_fopen')) {
115
+ $handler = $handler
116
+ ? Proxy::wrapStreaming($handler, new StreamHandler())
117
+ : new StreamHandler();
118
+ } elseif (!$handler) {
119
+ throw new \RuntimeException('GuzzleHttp requires cURL, the '
120
+ . 'allow_url_fopen ini setting, or a custom HTTP handler.');
121
+ }
122
+
123
+ return $handler;
124
+ }
125
+
126
+ /**
127
+ * Get the default User-Agent string to use with Guzzle
128
+ *
129
+ * @return string
130
+ */
131
+ function default_user_agent()
132
+ {
133
+ static $defaultAgent = '';
134
+
135
+ if (!$defaultAgent) {
136
+ $defaultAgent = 'GuzzleHttp/' . Client::VERSION;
137
+ if (extension_loaded('curl') && function_exists('curl_version')) {
138
+ $defaultAgent .= ' curl/' . \curl_version()['version'];
139
+ }
140
+ $defaultAgent .= ' PHP/' . PHP_VERSION;
141
+ }
142
+
143
+ return $defaultAgent;
144
+ }
145
+
146
+ /**
147
+ * Returns the default cacert bundle for the current system.
148
+ *
149
+ * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
150
+ * If those settings are not configured, then the common locations for
151
+ * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
152
+ * and Windows are checked. If any of these file locations are found on
153
+ * disk, they will be utilized.
154
+ *
155
+ * Note: the result of this function is cached for subsequent calls.
156
+ *
157
+ * @return string
158
+ * @throws \RuntimeException if no bundle can be found.
159
+ */
160
+ function default_ca_bundle()
161
+ {
162
+ static $cached = null;
163
+ static $cafiles = [
164
+ // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
165
+ '/etc/pki/tls/certs/ca-bundle.crt',
166
+ // Ubuntu, Debian (provided by the ca-certificates package)
167
+ '/etc/ssl/certs/ca-certificates.crt',
168
+ // FreeBSD (provided by the ca_root_nss package)
169
+ '/usr/local/share/certs/ca-root-nss.crt',
170
+ // SLES 12 (provided by the ca-certificates package)
171
+ '/var/lib/ca-certificates/ca-bundle.pem',
172
+ // OS X provided by homebrew (using the default path)
173
+ '/usr/local/etc/openssl/cert.pem',
174
+ // Google app engine
175
+ '/etc/ca-certificates.crt',
176
+ // Windows?
177
+ 'C:\\windows\\system32\\curl-ca-bundle.crt',
178
+ 'C:\\windows\\curl-ca-bundle.crt',
179
+ ];
180
+
181
+ if ($cached) {
182
+ return $cached;
183
+ }
184
+
185
+ if ($ca = ini_get('openssl.cafile')) {
186
+ return $cached = $ca;
187
+ }
188
+
189
+ if ($ca = ini_get('curl.cainfo')) {
190
+ return $cached = $ca;
191
+ }
192
+
193
+ foreach ($cafiles as $filename) {
194
+ if (file_exists($filename)) {
195
+ return $cached = $filename;
196
+ }
197
+ }
198
+
199
+ throw new \RuntimeException(
200
+ <<< EOT
201
+ No system CA bundle could be found in any of the the common system locations.
202
+ PHP versions earlier than 5.6 are not properly configured to use the system's
203
+ CA bundle by default. In order to verify peer certificates, you will need to
204
+ supply the path on disk to a certificate bundle to the 'verify' request
205
+ option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
206
+ need a specific certificate bundle, then Mozilla provides a commonly used CA
207
+ bundle which can be downloaded here (provided by the maintainer of cURL):
208
+ https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
209
+ you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
210
+ ini setting to point to the path to the file, allowing you to omit the 'verify'
211
+ request option. See http://curl.haxx.se/docs/sslcerts.html for more
212
+ information.
213
+ EOT
214
+ );
215
+ }
216
+
217
+ /**
218
+ * Creates an associative array of lowercase header names to the actual
219
+ * header casing.
220
+ *
221
+ * @param array $headers
222
+ *
223
+ * @return array
224
+ */
225
+ function normalize_header_keys(array $headers)
226
+ {
227
+ $result = [];
228
+ foreach (array_keys($headers) as $key) {
229
+ $result[strtolower($key)] = $key;
230
+ }
231
+
232
+ return $result;
233
+ }
234
+
235
+ /**
236
+ * Returns true if the provided host matches any of the no proxy areas.
237
+ *
238
+ * This method will strip a port from the host if it is present. Each pattern
239
+ * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
240
+ * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
241
+ * "baz.foo.com", but ".foo.com" != "foo.com").
242
+ *
243
+ * Areas are matched in the following cases:
244
+ * 1. "*" (without quotes) always matches any hosts.
245
+ * 2. An exact match.
246
+ * 3. The area starts with "." and the area is the last part of the host. e.g.
247
+ * '.mit.edu' will match any host that ends with '.mit.edu'.
248
+ *
249
+ * @param string $host Host to check against the patterns.
250
+ * @param array $noProxyArray An array of host patterns.
251
+ *
252
+ * @return bool
253
+ */
254
+ function is_host_in_noproxy($host, array $noProxyArray)
255
+ {
256
+ if (strlen($host) === 0) {
257
+ throw new \InvalidArgumentException('Empty host provided');
258
+ }
259
+
260
+ // Strip port if present.
261
+ if (strpos($host, ':')) {
262
+ $host = explode($host, ':', 2)[0];
263
+ }
264
+
265
+ foreach ($noProxyArray as $area) {
266
+ // Always match on wildcards.
267
+ if ($area === '*') {
268
+ return true;
269
+ } elseif (empty($area)) {
270
+ // Don't match on empty values.
271
+ continue;
272
+ } elseif ($area === $host) {
273
+ // Exact matches.
274
+ return true;
275
+ } else {
276
+ // Special match if the area when prefixed with ".". Remove any
277
+ // existing leading "." and add a new leading ".".
278
+ $area = '.' . ltrim($area, '.');
279
+ if (substr($host, -(strlen($area))) === $area) {
280
+ return true;
281
+ }
282
+ }
283
+ }
284
+
285
+ return false;
286
+ }
287
+
288
+ /**
289
+ * Wrapper for json_decode that throws when an error occurs.
290
+ *
291
+ * @param string $json JSON data to parse
292
+ * @param bool $assoc When true, returned objects will be converted
293
+ * into associative arrays.
294
+ * @param int $depth User specified recursion depth.
295
+ * @param int $options Bitmask of JSON decode options.
296
+ *
297
+ * @return mixed
298
+ * @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
299
+ * @link http://www.php.net/manual/en/function.json-decode.php
300
+ */
301
+ function json_decode($json, $assoc = false, $depth = 512, $options = 0)
302
+ {
303
+ $data = \json_decode($json, $assoc, $depth, $options);
304
+ if (JSON_ERROR_NONE !== json_last_error()) {
305
+ throw new Exception\InvalidArgumentException(
306
+ 'json_decode error: ' . json_last_error_msg()
307
+ );
308
+ }
309
+
310
+ return $data;
311
+ }
312
+
313
+ /**
314
+ * Wrapper for JSON encoding that throws when an error occurs.
315
+ *
316
+ * @param mixed $value The value being encoded
317
+ * @param int $options JSON encode option bitmask
318
+ * @param int $depth Set the maximum depth. Must be greater than zero.
319
+ *
320
+ * @return string
321
+ * @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
322
+ * @link http://www.php.net/manual/en/function.json-encode.php
323
+ */
324
+ function json_encode($value, $options = 0, $depth = 512)
325
+ {
326
+ $json = \json_encode($value, $options, $depth);
327
+ if (JSON_ERROR_NONE !== json_last_error()) {
328
+ throw new Exception\InvalidArgumentException(
329
+ 'json_encode error: ' . json_last_error_msg()
330
+ );
331
+ }
332
+
333
+ return $json;
334
+ }
335
+
336
+ /**
337
+ * Wrapper for the hrtime() or microtime() functions
338
+ * (depending on the PHP version, one of the two is used)
339
+ *
340
+ * @return float|mixed UNIX timestamp
341
+ * @internal
342
+ */
343
+ function _current_time()
344
+ {
345
+ return function_exists('hrtime') ? hrtime(true) / 1e9 : microtime(true);
346
+ }
vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Monolog\Formatter;
13
 
 
 
14
  /**
15
  * Class FluentdFormatter
16
  *
@@ -71,7 +73,7 @@ class FluentdFormatter implements FormatterInterface
71
  $message['level_name'] = $record['level_name'];
72
  }
73
 
74
- return json_encode(array($tag, $record['datetime']->getTimestamp(), $message));
75
  }
76
 
77
  public function formatBatch(array $records)
11
 
12
  namespace Monolog\Formatter;
13
 
14
+ use Monolog\Utils;
15
+
16
  /**
17
  * Class FluentdFormatter
18
  *
73
  $message['level_name'] = $record['level_name'];
74
  }
75
 
76
+ return Utils::jsonEncode(array($tag, $record['datetime']->getTimestamp(), $message));
77
  }
78
 
79
  public function formatBatch(array $records)
vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php CHANGED
@@ -11,6 +11,7 @@
11
  namespace Monolog\Formatter;
12
 
13
  use Monolog\Logger;
 
14
 
15
  /**
16
  * Formats incoming records into an HTML table
@@ -133,9 +134,9 @@ class HtmlFormatter extends NormalizerFormatter
133
 
134
  $data = $this->normalize($data);
135
  if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
136
- return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
137
  }
138
 
139
- return str_replace('\\/', '/', json_encode($data));
140
  }
141
  }
11
  namespace Monolog\Formatter;
12
 
13
  use Monolog\Logger;
14
+ use Monolog\Utils;
15
 
16
  /**
17
  * Formats incoming records into an HTML table
134
 
135
  $data = $this->normalize($data);
136
  if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
137
+ return Utils::jsonEncode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE, true);
138
  }
139
 
140
+ return str_replace('\\/', '/', Utils::jsonEncode($data, null, true));
141
  }
142
  }
vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php CHANGED
@@ -145,7 +145,7 @@ class JsonFormatter extends NormalizerFormatter
145
  return 'Over 9 levels deep, aborting normalization';
146
  }
147
 
148
- if (is_array($data) || $data instanceof \Traversable) {
149
  $normalized = array();
150
 
151
  $count = 1;
@@ -186,7 +186,7 @@ class JsonFormatter extends NormalizerFormatter
186
  $data = array(
187
  'class' => Utils::getClass($e),
188
  'message' => $e->getMessage(),
189
- 'code' => $e->getCode(),
190
  'file' => $e->getFile().':'.$e->getLine(),
191
  );
192
 
145
  return 'Over 9 levels deep, aborting normalization';
146
  }
147
 
148
+ if (is_array($data)) {
149
  $normalized = array();
150
 
151
  $count = 1;
186
  $data = array(
187
  'class' => Utils::getClass($e),
188
  'message' => $e->getMessage(),
189
+ 'code' => (int) $e->getCode(),
190
  'file' => $e->getFile().':'.$e->getLine(),
191
  );
192
 
vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php CHANGED
@@ -163,7 +163,7 @@ class LineFormatter extends NormalizerFormatter
163
  return $this->toJson($data, true);
164
  }
165
 
166
- return str_replace('\\/', '/', @json_encode($data));
167
  }
168
 
169
  protected function replaceNewlines($str)
163
  return $this->toJson($data, true);
164
  }
165
 
166
+ return str_replace('\\/', '/', $this->toJson($data, true));
167
  }
168
 
169
  protected function replaceNewlines($str)
vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php CHANGED
@@ -87,7 +87,7 @@ class MongoDBFormatter implements FormatterInterface
87
  $formattedException = array(
88
  'class' => Utils::getClass($exception),
89
  'message' => $exception->getMessage(),
90
- 'code' => $exception->getCode(),
91
  'file' => $exception->getFile() . ':' . $exception->getLine(),
92
  );
93
 
87
  $formattedException = array(
88
  'class' => Utils::getClass($exception),
89
  'message' => $exception->getMessage(),
90
+ 'code' => (int) $exception->getCode(),
91
  'file' => $exception->getFile() . ':' . $exception->getLine(),
92
  );
93
 
vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php CHANGED
@@ -129,7 +129,7 @@ class NormalizerFormatter implements FormatterInterface
129
  $data = array(
130
  'class' => Utils::getClass($e),
131
  'message' => $e->getMessage(),
132
- 'code' => $e->getCode(),
133
  'file' => $e->getFile().':'.$e->getLine(),
134
  );
135
 
@@ -142,8 +142,8 @@ class NormalizerFormatter implements FormatterInterface
142
  $data['faultactor'] = $e->faultactor;
143
  }
144
 
145
- if (isset($e->detail)) {
146
- $data['detail'] = $e->detail;
147
  }
148
  }
149
 
@@ -171,127 +171,6 @@ class NormalizerFormatter implements FormatterInterface
171
  */
172
  protected function toJson($data, $ignoreErrors = false)
173
  {
174
- // suppress json_encode errors since it's twitchy with some inputs
175
- if ($ignoreErrors) {
176
- return @$this->jsonEncode($data);
177
- }
178
-
179
- $json = $this->jsonEncode($data);
180
-
181
- if ($json === false) {
182
- $json = $this->handleJsonError(json_last_error(), $data);
183
- }
184
-
185
- return $json;
186
- }
187
-
188
- /**
189
- * @param mixed $data
190
- * @return string JSON encoded data or null on failure
191
- */
192
- private function jsonEncode($data)
193
- {
194
- if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
195
- return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
196
- }
197
-
198
- return json_encode($data);
199
- }
200
-
201
- /**
202
- * Handle a json_encode failure.
203
- *
204
- * If the failure is due to invalid string encoding, try to clean the
205
- * input and encode again. If the second encoding attempt fails, the
206
- * inital error is not encoding related or the input can't be cleaned then
207
- * raise a descriptive exception.
208
- *
209
- * @param int $code return code of json_last_error function
210
- * @param mixed $data data that was meant to be encoded
211
- * @throws \RuntimeException if failure can't be corrected
212
- * @return string JSON encoded data after error correction
213
- */
214
- private function handleJsonError($code, $data)
215
- {
216
- if ($code !== JSON_ERROR_UTF8) {
217
- $this->throwEncodeError($code, $data);
218
- }
219
-
220
- if (is_string($data)) {
221
- $this->detectAndCleanUtf8($data);
222
- } elseif (is_array($data)) {
223
- array_walk_recursive($data, array($this, 'detectAndCleanUtf8'));
224
- } else {
225
- $this->throwEncodeError($code, $data);
226
- }
227
-
228
- $json = $this->jsonEncode($data);
229
-
230
- if ($json === false) {
231
- $this->throwEncodeError(json_last_error(), $data);
232
- }
233
-
234
- return $json;
235
- }
236
-
237
- /**
238
- * Throws an exception according to a given code with a customized message
239
- *
240
- * @param int $code return code of json_last_error function
241
- * @param mixed $data data that was meant to be encoded
242
- * @throws \RuntimeException
243
- */
244
- private function throwEncodeError($code, $data)
245
- {
246
- switch ($code) {
247
- case JSON_ERROR_DEPTH:
248
- $msg = 'Maximum stack depth exceeded';
249
- break;
250
- case JSON_ERROR_STATE_MISMATCH:
251
- $msg = 'Underflow or the modes mismatch';
252
- break;
253
- case JSON_ERROR_CTRL_CHAR:
254
- $msg = 'Unexpected control character found';
255
- break;
256
- case JSON_ERROR_UTF8:
257
- $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
258
- break;
259
- default:
260
- $msg = 'Unknown error';
261
- }
262
-
263
- throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
264
- }
265
-
266
- /**
267
- * Detect invalid UTF-8 string characters and convert to valid UTF-8.
268
- *
269
- * Valid UTF-8 input will be left unmodified, but strings containing
270
- * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
271
- * original encoding of ISO-8859-15. This conversion may result in
272
- * incorrect output if the actual encoding was not ISO-8859-15, but it
273
- * will be clean UTF-8 output and will not rely on expensive and fragile
274
- * detection algorithms.
275
- *
276
- * Function converts the input in place in the passed variable so that it
277
- * can be used as a callback for array_walk_recursive.
278
- *
279
- * @param mixed &$data Input to check and convert if needed
280
- * @private
281
- */
282
- public function detectAndCleanUtf8(&$data)
283
- {
284
- if (is_string($data) && !preg_match('//u', $data)) {
285
- $data = preg_replace_callback(
286
- '/[\x80-\xFF]+/',
287
- function ($m) { return utf8_encode($m[0]); },
288
- $data
289
- );
290
- $data = str_replace(
291
- array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'),
292
- array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'),
293
- $data
294
- );
295
- }
296
  }
297
  }
129
  $data = array(
130
  'class' => Utils::getClass($e),
131
  'message' => $e->getMessage(),
132
+ 'code' => (int) $e->getCode(),
133
  'file' => $e->getFile().':'.$e->getLine(),
134
  );
135
 
142
  $data['faultactor'] = $e->faultactor;
143
  }
144
 
145
+ if (isset($e->detail) && (is_string($e->detail) || is_object($e->detail) || is_array($e->detail))) {
146
+ $data['detail'] = is_string($e->detail) ? $e->detail : reset($e->detail);
147
  }
148
  }
149
 
171
  */
172
  protected function toJson($data, $ignoreErrors = false)
173
  {
174
+ return Utils::jsonEncode($data, null, $ignoreErrors);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  }
176
  }
vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php CHANGED
@@ -164,21 +164,22 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
164
 
165
  private static function handleStyles($formatted)
166
  {
167
- $args = array(static::quote('font-weight: normal'));
168
  $format = '%c' . $formatted;
169
  preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
170
 
171
  foreach (array_reverse($matches) as $match) {
172
- $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
173
  $args[] = '"font-weight: normal"';
 
174
 
175
  $pos = $match[0][1];
176
  $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
177
  }
178
 
179
- array_unshift($args, static::quote($format));
 
180
 
181
- return $args;
182
  }
183
 
184
  private static function handleCustomStyles($style, $string)
164
 
165
  private static function handleStyles($formatted)
166
  {
167
+ $args = array();
168
  $format = '%c' . $formatted;
169
  preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
170
 
171
  foreach (array_reverse($matches) as $match) {
 
172
  $args[] = '"font-weight: normal"';
173
+ $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
174
 
175
  $pos = $match[0][1];
176
  $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
177
  }
178
 
179
+ $args[] = static::quote('font-weight: normal');
180
+ $args[] = static::quote($format);
181
 
182
+ return array_reverse($args);
183
  }
184
 
185
  private static function handleCustomStyles($style, $string)
vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php CHANGED
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
  use Monolog\ResettableInterface;
 
16
 
17
  /**
18
  * Buffers all records until closing the handler and then pass them as batch.
@@ -126,4 +127,22 @@ class BufferHandler extends AbstractHandler
126
  $this->handler->reset();
127
  }
128
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
13
 
14
  use Monolog\Logger;
15
  use Monolog\ResettableInterface;
16
+ use Monolog\Formatter\FormatterInterface;
17
 
18
  /**
19
  * Buffers all records until closing the handler and then pass them as batch.
127
  $this->handler->reset();
128
  }
129
  }
130
+
131
+ /**
132
+ * {@inheritdoc}
133
+ */
134
+ public function setFormatter(FormatterInterface $formatter)
135
+ {
136
+ $this->handler->setFormatter($formatter);
137
+
138
+ return $this;
139
+ }
140
+
141
+ /**
142
+ * {@inheritdoc}
143
+ */
144
+ public function getFormatter()
145
+ {
146
+ return $this->handler->getFormatter();
147
+ }
148
  }
vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php CHANGED
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
13
 
14
  use Monolog\Formatter\ChromePHPFormatter;
15
  use Monolog\Logger;
 
16
 
17
  /**
18
  * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
@@ -134,7 +135,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
134
  self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
135
  }
136
 
137
- $json = @json_encode(self::$json);
138
  $data = base64_encode(utf8_encode($json));
139
  if (strlen($data) > 3 * 1024) {
140
  self::$overflowed = true;
@@ -149,7 +150,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
149
  'extra' => array(),
150
  );
151
  self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
152
- $json = @json_encode(self::$json);
153
  $data = base64_encode(utf8_encode($json));
154
  }
155
 
13
 
14
  use Monolog\Formatter\ChromePHPFormatter;
15
  use Monolog\Logger;
16
+ use Monolog\Utils;
17
 
18
  /**
19
  * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
135
  self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
136
  }
137
 
138
+ $json = Utils::jsonEncode(self::$json, null, true);
139
  $data = base64_encode(utf8_encode($json));
140
  if (strlen($data) > 3 * 1024) {
141
  self::$overflowed = true;
150
  'extra' => array(),
151
  );
152
  self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
153
+ $json = Utils::jsonEncode(self::$json, null, true);
154
  $data = base64_encode(utf8_encode($json));
155
  }
156
 
vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
 
15
 
16
  /**
17
  * Logs to Cube.
@@ -119,9 +120,9 @@ class CubeHandler extends AbstractProcessingHandler
119
  $data['data']['level'] = $record['level'];
120
 
121
  if ($this->scheme === 'http') {
122
- $this->writeHttp(json_encode($data));
123
  } else {
124
- $this->writeUdp(json_encode($data));
125
  }
126
  }
127
 
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Utils;
16
 
17
  /**
18
  * Logs to Cube.
120
  $data['data']['level'] = $record['level'];
121
 
122
  if ($this->scheme === 'http') {
123
+ $this->writeHttp(Utils::jsonEncode($data));
124
  } else {
125
+ $this->writeUdp(Utils::jsonEncode($data));
126
  }
127
  }
128
 
vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
 
15
 
16
  /**
17
  * Simple handler wrapper that filters records based on a list of levels
@@ -45,7 +46,7 @@ class FilterHandler extends AbstractHandler
45
  protected $bubble;
46
 
47
  /**
48
- * @param callable|HandlerInterface $handler Handler or factory callable($record, $this).
49
  * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
50
  * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
51
  * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
@@ -104,21 +105,13 @@ class FilterHandler extends AbstractHandler
104
  return false;
105
  }
106
 
107
- // The same logic as in FingersCrossedHandler
108
- if (!$this->handler instanceof HandlerInterface) {
109
- $this->handler = call_user_func($this->handler, $record, $this);
110
- if (!$this->handler instanceof HandlerInterface) {
111
- throw new \RuntimeException("The factory callable should return a HandlerInterface");
112
- }
113
- }
114
-
115
  if ($this->processors) {
116
  foreach ($this->processors as $processor) {
117
  $record = call_user_func($processor, $record);
118
  }
119
  }
120
 
121
- $this->handler->handle($record);
122
 
123
  return false === $this->bubble;
124
  }
@@ -135,6 +128,43 @@ class FilterHandler extends AbstractHandler
135
  }
136
  }
137
 
138
- $this->handler->handleBatch($filtered);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
140
  }
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Formatter\FormatterInterface;
16
 
17
  /**
18
  * Simple handler wrapper that filters records based on a list of levels
46
  protected $bubble;
47
 
48
  /**
49
+ * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
50
  * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
51
  * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
52
  * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
105
  return false;
106
  }
107
 
 
 
 
 
 
 
 
 
108
  if ($this->processors) {
109
  foreach ($this->processors as $processor) {
110
  $record = call_user_func($processor, $record);
111
  }
112
  }
113
 
114
+ $this->getHandler($record)->handle($record);
115
 
116
  return false === $this->bubble;
117
  }
128
  }
129
  }
130
 
131
+ $this->getHandler($filtered[count($filtered) - 1])->handleBatch($filtered);
132
+ }
133
+
134
+ /**
135
+ * Return the nested handler
136
+ *
137
+ * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
138
+ *
139
+ * @return HandlerInterface
140
+ */
141
+ public function getHandler(array $record = null)
142
+ {
143
+ if (!$this->handler instanceof HandlerInterface) {
144
+ $this->handler = call_user_func($this->handler, $record, $this);
145
+ if (!$this->handler instanceof HandlerInterface) {
146
+ throw new \RuntimeException("The factory callable should return a HandlerInterface");
147
+ }
148
+ }
149
+
150
+ return $this->handler;
151
+ }
152
+
153
+ /**
154
+ * {@inheritdoc}
155
+ */
156
+ public function setFormatter(FormatterInterface $formatter)
157
+ {
158
+ $this->getHandler()->setFormatter($formatter);
159
+
160
+ return $this;
161
+ }
162
+
163
+ /**
164
+ * {@inheritdoc}
165
+ */
166
+ public function getFormatter()
167
+ {
168
+ return $this->getHandler()->getFormatter();
169
  }
170
  }
vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php CHANGED
@@ -15,6 +15,7 @@ use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
15
  use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
16
  use Monolog\Logger;
17
  use Monolog\ResettableInterface;
 
18
 
19
  /**
20
  * Buffers all records until a certain level is reached
@@ -39,7 +40,7 @@ class FingersCrossedHandler extends AbstractHandler
39
  protected $passthruLevel;
40
 
41
  /**
42
- * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler).
43
  * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
44
  * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
45
  * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
@@ -88,15 +89,7 @@ class FingersCrossedHandler extends AbstractHandler
88
  if ($this->stopBuffering) {
89
  $this->buffering = false;
90
  }
91
- if (!$this->handler instanceof HandlerInterface) {
92
- $record = end($this->buffer) ?: null;
93
-
94
- $this->handler = call_user_func($this->handler, $record, $this);
95
- if (!$this->handler instanceof HandlerInterface) {
96
- throw new \RuntimeException("The factory callable should return a HandlerInterface");
97
- }
98
- }
99
- $this->handler->handleBatch($this->buffer);
100
  $this->buffer = array();
101
  }
102
 
@@ -120,7 +113,7 @@ class FingersCrossedHandler extends AbstractHandler
120
  $this->activate();
121
  }
122
  } else {
123
- $this->handler->handle($record);
124
  }
125
 
126
  return false === $this->bubble;
@@ -140,8 +133,8 @@ class FingersCrossedHandler extends AbstractHandler
140
 
141
  parent::reset();
142
 
143
- if ($this->handler instanceof ResettableInterface) {
144
- $this->handler->reset();
145
  }
146
  }
147
 
@@ -167,11 +160,48 @@ class FingersCrossedHandler extends AbstractHandler
167
  return $record['level'] >= $level;
168
  });
169
  if (count($this->buffer) > 0) {
170
- $this->handler->handleBatch($this->buffer);
171
  }
172
  }
173
 
174
  $this->buffer = array();
175
  $this->buffering = true;
176
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
15
  use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
16
  use Monolog\Logger;
17
  use Monolog\ResettableInterface;
18
+ use Monolog\Formatter\FormatterInterface;
19
 
20
  /**
21
  * Buffers all records until a certain level is reached
40
  protected $passthruLevel;
41
 
42
  /**
43
+ * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
44
  * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
45
  * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
46
  * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
89
  if ($this->stopBuffering) {
90
  $this->buffering = false;
91
  }
92
+ $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
 
 
 
 
 
 
 
 
93
  $this->buffer = array();
94
  }
95
 
113
  $this->activate();
114
  }
115
  } else {
116
+ $this->getHandler($record)->handle($record);
117
  }
118
 
119
  return false === $this->bubble;
133
 
134
  parent::reset();
135
 
136
+ if ($this->getHandler() instanceof ResettableInterface) {
137
+ $this->getHandler()->reset();
138
  }
139
  }
140
 
160
  return $record['level'] >= $level;
161
  });
162
  if (count($this->buffer) > 0) {
163
+ $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
164
  }
165
  }
166
 
167
  $this->buffer = array();
168
  $this->buffering = true;
169
  }
170
+
171
+ /**
172
+ * Return the nested handler
173
+ *
174
+ * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
175
+ *
176
+ * @return HandlerInterface
177
+ */
178
+ public function getHandler(array $record = null)
179
+ {
180
+ if (!$this->handler instanceof HandlerInterface) {
181
+ $this->handler = call_user_func($this->handler, $record, $this);
182
+ if (!$this->handler instanceof HandlerInterface) {
183
+ throw new \RuntimeException("The factory callable should return a HandlerInterface");
184
+ }
185
+ }
186
+
187
+ return $this->handler;
188
+ }
189
+
190
+ /**
191
+ * {@inheritdoc}
192
+ */
193
+ public function setFormatter(FormatterInterface $formatter)
194
+ {
195
+ $this->getHandler()->setFormatter($formatter);
196
+
197
+ return $this;
198
+ }
199
+
200
+ /**
201
+ * {@inheritdoc}
202
+ */
203
+ public function getFormatter()
204
+ {
205
+ return $this->getHandler()->getFormatter();
206
+ }
207
  }
vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
 
15
  use Monolog\Formatter\FlowdockFormatter;
16
  use Monolog\Formatter\FormatterInterface;
17
 
@@ -105,7 +106,7 @@ class FlowdockHandler extends SocketHandler
105
  */
106
  private function buildContent($record)
107
  {
108
- return json_encode($record['formatted']['flowdock']);
109
  }
110
 
111
  /**
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Utils;
16
  use Monolog\Formatter\FlowdockFormatter;
17
  use Monolog\Formatter\FormatterInterface;
18
 
106
  */
107
  private function buildContent($record)
108
  {
109
+ return Utils::jsonEncode($record['formatted']['flowdock']);
110
  }
111
 
112
  /**
vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
 
15
 
16
  /**
17
  * IFTTTHandler uses cURL to trigger IFTTT Maker actions
@@ -53,7 +54,7 @@ class IFTTTHandler extends AbstractProcessingHandler
53
  "value2" => $record["level_name"],
54
  "value3" => $record["message"],
55
  );
56
- $postString = json_encode($postData);
57
 
58
  $ch = curl_init();
59
  curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey);
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Utils;
16
 
17
  /**
18
  * IFTTTHandler uses cURL to trigger IFTTT Maker actions
54
  "value2" => $record["level_name"],
55
  "value3" => $record["message"],
56
  );
57
+ $postString = Utils::jsonEncode($postData);
58
 
59
  $ch = curl_init();
60
  curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey);
vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
 
15
  use Monolog\Formatter\NormalizerFormatter;
16
 
17
  /**
@@ -190,7 +191,7 @@ class NewRelicHandler extends AbstractProcessingHandler
190
  if (null === $value || is_scalar($value)) {
191
  newrelic_add_custom_parameter($key, $value);
192
  } else {
193
- newrelic_add_custom_parameter($key, @json_encode($value));
194
  }
195
  }
196
 
12
  namespace Monolog\Handler;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Utils;
16
  use Monolog\Formatter\NormalizerFormatter;
17
 
18
  /**
191
  if (null === $value || is_scalar($value)) {
192
  newrelic_add_custom_parameter($key, $value);
193
  } else {
194
+ newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, true));
195
  }
196
  }
197
 
vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php CHANGED
@@ -14,6 +14,7 @@ namespace Monolog\Handler;
14
  use Exception;
15
  use Monolog\Formatter\LineFormatter;
16
  use Monolog\Logger;
 
17
  use PhpConsole\Connector;
18
  use PhpConsole\Handler;
19
  use PhpConsole\Helper;
@@ -188,7 +189,7 @@ class PHPConsoleHandler extends AbstractProcessingHandler
188
  $tags = $this->getRecordTags($record);
189
  $message = $record['message'];
190
  if ($record['context']) {
191
- $message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context'])));
192
  }
193
  $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
194
  }
14
  use Exception;
15
  use Monolog\Formatter\LineFormatter;
16
  use Monolog\Logger;
17
+ use Monolog\Utils;
18
  use PhpConsole\Connector;
19
  use PhpConsole\Handler;
20
  use PhpConsole\Helper;
189
  $tags = $this->getRecordTags($record);
190
  $message = $record['message'];
191
  if ($record['context']) {
192
+ $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true);
193
  }
194
  $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
195
  }
vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php CHANGED
@@ -11,6 +11,8 @@
11
 
12
  namespace Monolog\Handler;
13
 
 
 
14
  /**
15
  * Sampling handler
16
  *
@@ -38,7 +40,7 @@ class SamplingHandler extends AbstractHandler
38
  protected $factor;
39
 
40
  /**
41
- * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler).
42
  * @param int $factor Sample factor
43
  */
44
  public function __construct($handler, $factor)
@@ -54,29 +56,58 @@ class SamplingHandler extends AbstractHandler
54
 
55
  public function isHandling(array $record)
56
  {
57
- return $this->handler->isHandling($record);
58
  }
59
 
60
  public function handle(array $record)
61
  {
62
  if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
63
- // The same logic as in FingersCrossedHandler
64
- if (!$this->handler instanceof HandlerInterface) {
65
- $this->handler = call_user_func($this->handler, $record, $this);
66
- if (!$this->handler instanceof HandlerInterface) {
67
- throw new \RuntimeException("The factory callable should return a HandlerInterface");
68
- }
69
- }
70
-
71
  if ($this->processors) {
72
  foreach ($this->processors as $processor) {
73
  $record = call_user_func($processor, $record);
74
  }
75
  }
76
 
77
- $this->handler->handle($record);
78
  }
79
 
80
  return false === $this->bubble;
81
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
11
 
12
  namespace Monolog\Handler;
13
 
14
+ use Monolog\Formatter\FormatterInterface;
15
+
16
  /**
17
  * Sampling handler
18
  *
40
  protected $factor;
41
 
42
  /**
43
+ * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler).
44
  * @param int $factor Sample factor
45
  */
46
  public function __construct($handler, $factor)
56
 
57
  public function isHandling(array $record)
58
  {
59
+ return $this->getHandler($record)->isHandling($record);
60
  }
61
 
62
  public function handle(array $record)
63
  {
64
  if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
 
 
 
 
 
 
 
 
65
  if ($this->processors) {
66
  foreach ($this->processors as $processor) {
67
  $record = call_user_func($processor, $record);
68
  }
69
  }
70
 
71
+ $this->getHandler($record)->handle($record);
72
  }
73
 
74
  return false === $this->bubble;
75
  }
76
+
77
+ /**
78
+ * Return the nested handler
79
+ *
80
+ * If the handler was provided as a factory callable, this will trigger the handler's instantiation.
81
+ *
82
+ * @return HandlerInterface
83
+ */
84
+ public function getHandler(array $record = null)
85
+ {
86
+ if (!$this->handler instanceof HandlerInterface) {
87
+ $this->handler = call_user_func($this->handler, $record, $this);
88
+ if (!$this->handler instanceof HandlerInterface) {
89
+ throw new \RuntimeException("The factory callable should return a HandlerInterface");
90
+ }
91
+ }
92
+
93
+ return $this->handler;
94
+ }
95
+
96
+ /**
97
+ * {@inheritdoc}
98
+ */
99
+ public function setFormatter(FormatterInterface $formatter)
100
+ {
101
+ $this->getHandler()->setFormatter($formatter);
102
+
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * {@inheritdoc}
108
+ */
109
+ public function getFormatter()
110
+ {
111
+ return $this->getHandler()->getFormatter();
112
+ }
113
  }
vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php CHANGED
@@ -12,6 +12,7 @@
12
  namespace Monolog\Handler\Slack;
13
 
14
  use Monolog\Logger;
 
15
  use Monolog\Formatter\NormalizerFormatter;
16
  use Monolog\Formatter\FormatterInterface;
17
 
@@ -207,13 +208,17 @@ class SlackRecord
207
  {
208
  $normalized = $this->normalizerFormatter->format($fields);
209
  $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
 
 
 
 
210
 
211
  $hasSecondDimension = count(array_filter($normalized, 'is_array'));
212
  $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
213
 
214
  return $hasSecondDimension || $hasNonNumericKeys
215
- ? json_encode($normalized, $prettyPrintFlag)
216
- : json_encode($normalized);
217
  }
218
 
219
  /**
12
  namespace Monolog\Handler\Slack;
13
 
14
  use Monolog\Logger;
15
+ use Monolog\Utils;
16
  use Monolog\Formatter\NormalizerFormatter;
17
  use Monolog\Formatter\FormatterInterface;
18
 
208
  {
209
  $normalized = $this->normalizerFormatter->format($fields);
210
  $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
211
+ $flags = 0;
212
+ if (PHP_VERSION_ID >= 50400) {
213
+ $flags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
214
+ }
215
 
216
  $hasSecondDimension = count(array_filter($normalized, 'is_array'));
217
  $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
218
 
219
  return $hasSecondDimension || $hasNonNumericKeys
220
+ ? Utils::jsonEncode($normalized, $prettyPrintFlag | $flags)
221
+ : Utils::jsonEncode($normalized, $flags);
222
  }
223
 
224
  /**
vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php CHANGED
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
13
 
14
  use Monolog\Formatter\FormatterInterface;
15
  use Monolog\Logger;
 
16
  use Monolog\Handler\Slack\SlackRecord;
17
 
18
  /**
@@ -118,7 +119,7 @@ class SlackHandler extends SocketHandler
118
  $dataArray['token'] = $this->token;
119
 
120
  if (!empty($dataArray['attachments'])) {
121
- $dataArray['attachments'] = json_encode($dataArray['attachments']);
122
  }
123
 
124
  return $dataArray;
13
 
14
  use Monolog\Formatter\FormatterInterface;
15
  use Monolog\Logger;
16
+ use Monolog\Utils;
17
  use Monolog\Handler\Slack\SlackRecord;
18
 
19
  /**
119
  $dataArray['token'] = $this->token;
120
 
121
  if (!empty($dataArray['attachments'])) {
122
+ $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
123
  }
124
 
125
  return $dataArray;
vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php CHANGED
@@ -13,6 +13,7 @@ namespace Monolog\Handler;
13
 
14
  use Monolog\Formatter\FormatterInterface;
15
  use Monolog\Logger;
 
16
  use Monolog\Handler\Slack\SlackRecord;
17
 
18
  /**
@@ -83,7 +84,7 @@ class SlackWebhookHandler extends AbstractProcessingHandler
83
  protected function write(array $record)
84
  {
85
  $postData = $this->slackRecord->getSlackData($record);
86
- $postString = json_encode($postData);
87
 
88
  $ch = curl_init();
89
  $options = array(
13
 
14
  use Monolog\Formatter\FormatterInterface;
15
  use Monolog\Logger;
16
+ use Monolog\Utils;
17
  use Monolog\Handler\Slack\SlackRecord;
18
 
19
  /**
84
  protected function write(array $record)
85
  {
86
  $postData = $this->slackRecord->getSlackData($record);
87
+ $postString = Utils::jsonEncode($postData);
88
 
89
  $ch = curl_init();
90
  $options = array(
vendor/monolog/monolog/src/Monolog/Utils.php CHANGED
@@ -22,4 +22,138 @@ class Utils
22
 
23
  return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
22
 
23
  return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
24
  }
25
+
26
+ /**
27
+ * Return the JSON representation of a value
28
+ *
29
+ * @param mixed $data
30
+ * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
31
+ * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null
32
+ * @throws \RuntimeException if encoding fails and errors are not ignored
33
+ * @return string
34
+ */
35
+ public static function jsonEncode($data, $encodeFlags = null, $ignoreErrors = false)
36
+ {
37
+ if (null === $encodeFlags && version_compare(PHP_VERSION, '5.4.0', '>=')) {
38
+ $encodeFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
39
+ }
40
+
41
+ if ($ignoreErrors) {
42
+ $json = @json_encode($data, $encodeFlags);
43
+ if (false === $json) {
44
+ return 'null';
45
+ }
46
+
47
+ return $json;
48
+ }
49
+
50
+ $json = json_encode($data, $encodeFlags);
51
+ if (false === $json) {
52
+ $json = self::handleJsonError(json_last_error(), $data);
53
+ }
54
+
55
+ return $json;
56
+ }
57
+
58
+ /**
59
+ * Handle a json_encode failure.
60
+ *
61
+ * If the failure is due to invalid string encoding, try to clean the
62
+ * input and encode again. If the second encoding attempt fails, the
63
+ * inital error is not encoding related or the input can't be cleaned then
64
+ * raise a descriptive exception.
65
+ *
66
+ * @param int $code return code of json_last_error function
67
+ * @param mixed $data data that was meant to be encoded
68
+ * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
69
+ * @throws \RuntimeException if failure can't be corrected
70
+ * @return string JSON encoded data after error correction
71
+ */
72
+ public static function handleJsonError($code, $data, $encodeFlags = null)
73
+ {
74
+ if ($code !== JSON_ERROR_UTF8) {
75
+ self::throwEncodeError($code, $data);
76
+ }
77
+
78
+ if (is_string($data)) {
79
+ self::detectAndCleanUtf8($data);
80
+ } elseif (is_array($data)) {
81
+ array_walk_recursive($data, array('Monolog\Utils', 'detectAndCleanUtf8'));
82
+ } else {
83
+ self::throwEncodeError($code, $data);
84
+ }
85
+
86
+ if (null === $encodeFlags && version_compare(PHP_VERSION, '5.4.0', '>=')) {
87
+ $encodeFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
88
+ }
89
+
90
+ $json = json_encode($data, $encodeFlags);
91
+
92
+ if ($json === false) {
93
+ self::throwEncodeError(json_last_error(), $data);
94
+ }
95
+
96
+ return $json;
97
+ }
98
+
99
+ /**
100
+ * Throws an exception according to a given code with a customized message
101
+ *
102
+ * @param int $code return code of json_last_error function
103
+ * @param mixed $data data that was meant to be encoded
104
+ * @throws \RuntimeException
105
+ */
106
+ private static function throwEncodeError($code, $data)
107
+ {
108
+ switch ($code) {
109
+ case JSON_ERROR_DEPTH:
110
+ $msg = 'Maximum stack depth exceeded';
111
+ break;
112
+ case JSON_ERROR_STATE_MISMATCH:
113
+ $msg = 'Underflow or the modes mismatch';
114
+ break;
115
+ case JSON_ERROR_CTRL_CHAR:
116
+ $msg = 'Unexpected control character found';
117
+ break;
118
+ case JSON_ERROR_UTF8:
119
+ $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
120
+ break;
121
+ default:
122
+ $msg = 'Unknown error';
123
+ }
124
+
125
+ throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true));
126
+ }
127
+
128
+ /**
129
+ * Detect invalid UTF-8 string characters and convert to valid UTF-8.
130
+ *
131
+ * Valid UTF-8 input will be left unmodified, but strings containing
132
+ * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed
133
+ * original encoding of ISO-8859-15. This conversion may result in
134
+ * incorrect output if the actual encoding was not ISO-8859-15, but it
135
+ * will be clean UTF-8 output and will not rely on expensive and fragile
136
+ * detection algorithms.
137
+ *
138
+ * Function converts the input in place in the passed variable so that it
139
+ * can be used as a callback for array_walk_recursive.
140
+ *
141
+ * @param mixed &$data Input to check and convert if needed
142
+ * @private
143
+ */
144
+ public static function detectAndCleanUtf8(&$data)
145
+ {
146
+ if (is_string($data) && !preg_match('//u', $data)) {
147
+ $data = preg_replace_callback(
148
+ '/[\x80-\xFF]+/',
149
+ function ($m) { return utf8_encode($m[0]); },
150
+ $data
151
+ );
152
+ $data = str_replace(
153
+ array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'),
154
+ array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'),
155
+ $data
156
+ );
157
+ }
158
+ }
159
  }
wp_mail_smtp.php CHANGED
@@ -1,166 +1,167 @@
1
- <?php
2
- /**
3
- * Plugin Name: WP Mail SMTP
4
- * Version: 1.7.1
5
- * Plugin URI: https://wpmailsmtp.com/
6
- * Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.
7
- * Author: WPForms
8
- * Author URI: https://wpforms.com/
9
- * Network: false
10
- * Text Domain: wp-mail-smtp
11
- * Domain Path: /assets/languages
12
- */
13
-
14
- /**
15
- * @author WPForms
16
- * @copyright WPForms, 2007-19, All Rights Reserved
17
- * This code is released under the GPL licence version 3 or later, available here
18
- * https://www.gnu.org/licenses/gpl.txt
19
- */
20
-
21
- /**
22
- * Setting options in wp-config.php
23
- *
24
- * Specifically aimed at WP Multisite users, you can set the options for this plugin as
25
- * constants in wp-config.php. Copy the code below into wp-config.php and tweak settings.
26
- * Values from constants are NOT stripslash()'ed.
27
- *
28
- * When enabled, make sure to comment out (at the beginning of the line using //) those constants that you do not need,
29
- * or remove them completely, so they won't interfere with plugin settings.
30
- */
31
-
32
- /*
33
- define( 'WPMS_ON', true ); // True turns on the whole constants support and usage, false turns it off.
34
-
35
- define( 'WPMS_DO_NOT_SEND', true ); // Or false, in that case constant is ignored.
36
-
37
- define( 'WPMS_MAIL_FROM', 'mail@example.com' );
38
- define( 'WPMS_MAIL_FROM_FORCE', true ); // True turns it on, false turns it off.
39
- define( 'WPMS_MAIL_FROM_NAME', 'From Name' );
40
- define( 'WPMS_MAIL_FROM_NAME_FORCE', true ); // True turns it on, false turns it off.
41
- define( 'WPMS_MAILER', 'sendinblue' ); // Possible values: 'mail', 'sendinblue', 'mailgun', 'sendgrid', 'gmail', 'smtp'.
42
- define( 'WPMS_SET_RETURN_PATH', true ); // Sets $phpmailer->Sender if true, relevant only for Other SMTP mailer.
43
-
44
- // Recommended mailer.
45
- define( 'WPMS_SENDINBLUE_API_KEY', '' );
46
-
47
- define( 'WPMS_MAILGUN_API_KEY', '' );
48
- define( 'WPMS_MAILGUN_DOMAIN', '' );
49
- define( 'WPMS_MAILGUN_REGION', 'US' ); // or 'EU' for Europe.
50
-
51
- define( 'WPMS_SENDGRID_API_KEY', '' );
52
-
53
- define( 'WPMS_GMAIL_CLIENT_ID', '' );
54
- define( 'WPMS_GMAIL_CLIENT_SECRET', '' );
55
-
56
- define( 'WPMS_SMTP_HOST', 'localhost' ); // The SMTP mail host.
57
- define( 'WPMS_SMTP_PORT', 25 ); // The SMTP server port number.
58
- define( 'WPMS_SSL', '' ); // Possible values '', 'ssl', 'tls' - note TLS is not STARTTLS.
59
- define( 'WPMS_SMTP_AUTH', true ); // True turns it on, false turns it off.
60
- define( 'WPMS_SMTP_USER', 'username' ); // SMTP authentication username, only used if WPMS_SMTP_AUTH is true.
61
- define( 'WPMS_SMTP_PASS', 'password' ); // SMTP authentication password, only used if WPMS_SMTP_AUTH is true.
62
- define( 'WPMS_SMTP_AUTOTLS', true ); // True turns it on, false turns it off.
63
- */
64
-
65
- /**
66
- * Don't allow multiple versions of 1.5.x (Lite and Pro) and above to be active.
67
- *
68
- * @since 1.5.0
69
- */
70
- if ( function_exists( 'wp_mail_smtp' ) ) {
71
-
72
- if ( ! function_exists( 'wp_mail_smtp_deactivate' ) ) {
73
- /**
74
- * Deactivate if plugin already activated.
75
- * Needed when transitioning from 1.5+ Lite to Pro.
76
- *
77
- * @since 1.5.0
78
- */
79
- function wp_mail_smtp_deactivate() {
80
-
81
- deactivate_plugins( plugin_basename( __FILE__ ) );
82
- }
83
- }
84
- add_action( 'admin_init', 'wp_mail_smtp_deactivate' );
85
-
86
- // Do not process the plugin code further.
87
- return;
88
- }
89
-
90
- if ( ! function_exists( 'wp_mail_smtp_check_pro_loading_allowed' ) ) {
91
- /**
92
- * Don't allow 1.4.x and below to break when 1.5+ Pro is activated.
93
- * This will stop the current plugin from loading and display a message in admin area.
94
- *
95
- * @since 1.5.0
96
- */
97
- function wp_mail_smtp_check_pro_loading_allowed() {
98
-
99
- // Check for pro without using wp_mail_smtp()->is_pro(), because at this point it's too early.
100
- if ( ! is_readable( rtrim( plugin_dir_path( __FILE__ ), '/\\' ) . '/src/Pro/Pro.php' ) ) {
101
- // Currently, not a pro version of the plugin is loaded.
102
- return false;
103
- }
104
-
105
- if ( ! function_exists( 'is_plugin_active' ) ) {
106
- require_once ABSPATH . '/wp-admin/includes/plugin.php';
107
- }
108
-
109
- // Search for old plugin name.
110
- if ( is_plugin_active( 'wp-mail-smtp/wp_mail_smtp.php' ) ) {
111
- // As Pro is loaded and Lite too - deactivate *silently* itself not to break older SMTP plugin.
112
- deactivate_plugins( plugin_basename( __FILE__ ) );
113
-
114
- add_action( 'admin_notices', 'wp_mail_smtp_lite_deactivation_notice' );
115
-
116
- return true;
117
- }
118
-
119
- return false;
120
- }
121
-
122
- if ( ! function_exists( 'wp_mail_smtp_lite_deactivation_notice' ) ) {
123
- /**
124
- * Display the notice after deactivation.
125
- *
126
- * @since 1.5.0
127
- */
128
- function wp_mail_smtp_lite_deactivation_notice() {
129
-
130
- echo '<div class="notice notice-warning"><p>' . esc_html__( 'Please deactivate the free version of the WP Mail SMTP plugin before activating WP Mail SMTP Pro.', 'wp-mail-smtp' ) . '</p></div>';
131
-
132
- if ( isset( $_GET['activate'] ) ) { // phpcs:ignore
133
- unset( $_GET['activate'] ); // phpcs:ignore
134
- }
135
- }
136
- }
137
-
138
- // Stop the plugin loading.
139
- if ( wp_mail_smtp_check_pro_loading_allowed() === true ) {
140
- return;
141
- }
142
- }
143
-
144
- if ( ! defined( 'WPMS_PLUGIN_VER' ) ) {
145
- define( 'WPMS_PLUGIN_VER', '1.7.1' );
146
- }
147
- if ( ! defined( 'WPMS_PHP_VER' ) ) {
148
- define( 'WPMS_PHP_VER', '5.3.6' );
149
- }
150
-
151
- /**
152
- * Newer PHP version 5.3+ will be handled a lot differently,
153
- * with better code and newer logic.
154
- *
155
- * @since 1.0.0
156
- */
157
- if ( version_compare( phpversion(), WPMS_PHP_VER, '>=' ) ) {
158
- require_once dirname( __FILE__ ) . '/wp-mail-smtp.php';
159
-
160
- return;
161
- }
162
-
163
- /**
164
- * PHP 5.2 only.
165
- */
166
- require_once dirname( __FILE__ ) . '/wp-mail-smtp-0.11.2.php';
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: WP Mail SMTP
4
+ * Version: 1.8.0
5
+ * Plugin URI: https://wpmailsmtp.com/
6
+ * Description: Reconfigures the <code>wp_mail()</code> function to use Gmail/Mailgun/SendGrid/SMTP instead of the default <code>mail()</code> and creates an options page to manage the settings.
7
+ * Author: WPForms
8
+ * Author URI: https://wpforms.com/
9
+ * Network: false
10
+ * Text Domain: wp-mail-smtp
11
+ * Domain Path: /assets/languages
12
+ */
13
+
14
+ /**
15
+ * @author WPForms
16
+ * @copyright WPForms, 2007-19, All Rights Reserved
17
+ * This code is released under the GPL licence version 3 or later, available here
18
+ * https://www.gnu.org/licenses/gpl.txt
19
+ */
20
+
21
+ /**
22
+ * Setting options in wp-config.php
23
+ *
24
+ * Specifically aimed at WP Multisite users, you can set the options for this plugin as
25
+ * constants in wp-config.php. Copy the code below into wp-config.php and tweak settings.
26
+ * Values from constants are NOT stripslash()'ed.
27
+ *
28
+ * When enabled, make sure to comment out (at the beginning of the line using //) those constants that you do not need,
29
+ * or remove them completely, so they won't interfere with plugin settings.
30
+ */
31
+
32
+ /*
33
+ define( 'WPMS_ON', true ); // True turns on the whole constants support and usage, false turns it off.
34
+
35
+ define( 'WPMS_DO_NOT_SEND', true ); // Or false, in that case constant is ignored.
36
+
37
+ define( 'WPMS_MAIL_FROM', 'mail@example.com' );
38
+ define( 'WPMS_MAIL_FROM_FORCE', true ); // True turns it on, false turns it off.
39
+ define( 'WPMS_MAIL_FROM_NAME', 'From Name' );
40
+ define( 'WPMS_MAIL_FROM_NAME_FORCE', true ); // True turns it on, false turns it off.
41
+ define( 'WPMS_MAILER', 'sendinblue' ); // Possible values: 'mail', 'sendinblue', 'mailgun', 'sendgrid', 'gmail', 'smtp'.
42
+ define( 'WPMS_SET_RETURN_PATH', true ); // Sets $phpmailer->Sender if true, relevant only for Other SMTP mailer.
43
+
44
+ // Recommended mailers.
45
+ define( 'WPMS_PEPIPOST_API_KEY', '' );
46
+ define( 'WPMS_SENDINBLUE_API_KEY', '' );
47
+
48
+ define( 'WPMS_MAILGUN_API_KEY', '' );
49
+ define( 'WPMS_MAILGUN_DOMAIN', '' );
50
+ define( 'WPMS_MAILGUN_REGION', 'US' ); // or 'EU' for Europe.
51
+
52
+ define( 'WPMS_SENDGRID_API_KEY', '' );
53
+
54
+ define( 'WPMS_GMAIL_CLIENT_ID', '' );
55
+ define( 'WPMS_GMAIL_CLIENT_SECRET', '' );
56
+
57
+ define( 'WPMS_SMTP_HOST', 'localhost' ); // The SMTP mail host.
58
+ define( 'WPMS_SMTP_PORT', 25 ); // The SMTP server port number.
59
+ define( 'WPMS_SSL', '' ); // Possible values '', 'ssl', 'tls' - note TLS is not STARTTLS.
60
+ define( 'WPMS_SMTP_AUTH', true ); // True turns it on, false turns it off.
61
+ define( 'WPMS_SMTP_USER', 'username' ); // SMTP authentication username, only used if WPMS_SMTP_AUTH is true.
62
+ define( 'WPMS_SMTP_PASS', 'password' ); // SMTP authentication password, only used if WPMS_SMTP_AUTH is true.
63
+ define( 'WPMS_SMTP_AUTOTLS', true ); // True turns it on, false turns it off.
64
+ */
65
+
66
+ /**
67
+ * Don't allow multiple versions of 1.5.x (Lite and Pro) and above to be active.
68
+ *
69
+ * @since 1.5.0
70
+ */
71
+ if ( function_exists( 'wp_mail_smtp' ) ) {
72
+
73
+ if ( ! function_exists( 'wp_mail_smtp_deactivate' ) ) {
74
+ /**
75
+ * Deactivate if plugin already activated.
76
+ * Needed when transitioning from 1.5+ Lite to Pro.
77
+ *
78
+ * @since 1.5.0
79
+ */
80
+ function wp_mail_smtp_deactivate() {
81
+
82
+ deactivate_plugins( plugin_basename( __FILE__ ) );
83
+ }
84
+ }
85
+ add_action( 'admin_init', 'wp_mail_smtp_deactivate' );
86
+
87
+ // Do not process the plugin code further.
88
+ return;
89
+ }
90
+
91
+ if ( ! function_exists( 'wp_mail_smtp_check_pro_loading_allowed' ) ) {
92
+ /**
93
+ * Don't allow 1.4.x and below to break when 1.5+ Pro is activated.
94
+ * This will stop the current plugin from loading and display a message in admin area.
95
+ *
96
+ * @since 1.5.0
97
+ */
98
+ function wp_mail_smtp_check_pro_loading_allowed() {
99
+
100
+ // Check for pro without using wp_mail_smtp()->is_pro(), because at this point it's too early.
101
+ if ( ! is_readable( rtrim( plugin_dir_path( __FILE__ ), '/\\' ) . '/src/Pro/Pro.php' ) ) {
102
+ // Currently, not a pro version of the plugin is loaded.
103
+ return false;
104
+ }
105
+
106
+ if ( ! function_exists( 'is_plugin_active' ) ) {
107
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
108
+ }
109
+
110
+ // Search for old plugin name.
111
+ if ( is_plugin_active( 'wp-mail-smtp/wp_mail_smtp.php' ) ) {
112
+ // As Pro is loaded and Lite too - deactivate *silently* itself not to break older SMTP plugin.
113
+ deactivate_plugins( plugin_basename( __FILE__ ) );
114
+
115
+ add_action( 'admin_notices', 'wp_mail_smtp_lite_deactivation_notice' );
116
+
117
+ return true;
118
+ }
119
+
120
+ return false;
121
+ }
122
+
123
+ if ( ! function_exists( 'wp_mail_smtp_lite_deactivation_notice' ) ) {
124
+ /**
125
+ * Display the notice after deactivation.
126
+ *
127
+ * @since 1.5.0
128
+ */
129
+ function wp_mail_smtp_lite_deactivation_notice() {
130
+
131
+ echo '<div class="notice notice-warning"><p>' . esc_html__( 'Please deactivate the free version of the WP Mail SMTP plugin before activating WP Mail SMTP Pro.', 'wp-mail-smtp' ) . '</p></div>';
132
+
133
+ if ( isset( $_GET['activate'] ) ) { // phpcs:ignore
134
+ unset( $_GET['activate'] ); // phpcs:ignore
135
+ }
136
+ }
137
+ }
138
+
139
+ // Stop the plugin loading.
140
+ if ( wp_mail_smtp_check_pro_loading_allowed() === true ) {
141
+ return;
142
+ }
143
+ }
144
+
145
+ if ( ! defined( 'WPMS_PLUGIN_VER' ) ) {
146
+ define( 'WPMS_PLUGIN_VER', '1.8.0' );
147
+ }
148
+ if ( ! defined( 'WPMS_PHP_VER' ) ) {
149
+ define( 'WPMS_PHP_VER', '5.3.6' );
150
+ }
151
+
152
+ /**
153
+ * Newer PHP version 5.3+ will be handled a lot differently,
154
+ * with better code and newer logic.
155
+ *
156
+ * @since 1.0.0
157
+ */
158
+ if ( version_compare( phpversion(), WPMS_PHP_VER, '>=' ) ) {
159
+ require_once dirname( __FILE__ ) . '/wp-mail-smtp.php';
160
+
161
+ return;
162
+ }
163
+
164
+ /**
165
+ * PHP 5.2 only.
166
+ */
167
+ require_once dirname( __FILE__ ) . '/wp-mail-smtp-0.11.2.php';