sebastianfeldmann /
phpbu
| 1 | <?php |
||
| 2 | namespace phpbu\App\Log; |
||
| 3 | |||
| 4 | use phpbu\App\Exception; |
||
| 5 | use phpbu\App\Event; |
||
| 6 | use phpbu\App\Listener; |
||
| 7 | use phpbu\App\Result; |
||
| 8 | use phpbu\App\Util\Arr; |
||
| 9 | use Throwable; |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Webhook Logger |
||
| 13 | * |
||
| 14 | * @package phpbu |
||
| 15 | * @subpackage Log |
||
| 16 | * @author Cees Vogel <[email protected]> |
||
| 17 | * @author Sebastian Feldmann <[email protected]> |
||
| 18 | * @copyright Sebastian Feldmann <[email protected]> |
||
| 19 | * @license https://opensource.org/licenses/MIT The MIT License (MIT) |
||
| 20 | * @link https://phpbu.de/ |
||
| 21 | * @since Class available since Release 5.0.0 |
||
| 22 | */ |
||
| 23 | class Webhook implements Listener, Logger |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * Start time |
||
| 27 | * |
||
| 28 | * @var float |
||
| 29 | */ |
||
| 30 | private $start; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * Uri to call |
||
| 34 | * |
||
| 35 | * @var string |
||
| 36 | */ |
||
| 37 | private $uri; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Request method GET|POST |
||
| 41 | * |
||
| 42 | * @var string |
||
| 43 | */ |
||
| 44 | private $method; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * Request timeout (seconds) |
||
| 48 | * |
||
| 49 | * @var int |
||
| 50 | */ |
||
| 51 | private $timeout; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Basic auth username |
||
| 55 | * |
||
| 56 | * @var string |
||
| 57 | */ |
||
| 58 | private $username; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Basic auth password |
||
| 62 | * |
||
| 63 | * @var string |
||
| 64 | */ |
||
| 65 | private $password; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Request content type |
||
| 69 | * |
||
| 70 | * @var string |
||
| 71 | */ |
||
| 72 | private $contentType; |
||
| 73 | |||
| 74 | /** |
||
| 75 | * Body template to use |
||
| 76 | * |
||
| 77 | * @var string |
||
| 78 | */ |
||
| 79 | private $template; |
||
| 80 | |||
| 81 | /** |
||
| 82 | * List of available default formatter |
||
| 83 | * |
||
| 84 | * @var array |
||
| 85 | */ |
||
| 86 | private $availableFormatter = [ |
||
| 87 | 'multipart/form-data' => '\\phpbu\\App\\Log\\ResultFormatter\\FormData', |
||
| 88 | 7 | 'application/json' => '\\phpbu\\App\\Log\\ResultFormatter\\Json', |
|
| 89 | 'application/xml' => '\\phpbu\\App\\Log\\ResultFormatter\\Xml' |
||
| 90 | 7 | ]; |
|
| 91 | 7 | ||
| 92 | /** |
||
| 93 | * Constructor will only set the start time to be able to log duration |
||
| 94 | */ |
||
| 95 | public function __construct() |
||
| 96 | { |
||
| 97 | $this->start = microtime(true); |
||
| 98 | } |
||
| 99 | |||
| 100 | /** |
||
| 101 | * Returns an array of event names this subscriber wants to listen to. |
||
| 102 | * |
||
| 103 | * The array keys are event names and the value can be: |
||
| 104 | * |
||
| 105 | 1 | * - The method name to call (priority defaults to 0) |
|
| 106 | * - An array composed of the method name to call and the priority |
||
| 107 | * - An array of arrays composed of the method names to call and respective |
||
| 108 | 1 | * priorities, or 0 if unset |
|
| 109 | * |
||
| 110 | * @return array The event names to listen to |
||
| 111 | */ |
||
| 112 | public static function getSubscribedEvents(): array |
||
| 113 | { |
||
| 114 | return [ |
||
| 115 | 'phpbu.app_end' => 'onPhpbuEnd', |
||
| 116 | ]; |
||
| 117 | } |
||
| 118 | |||
| 119 | 7 | /** |
|
| 120 | * Setup the logger. |
||
| 121 | 7 | * |
|
| 122 | 1 | * @see \phpbu\App\Log\Logger::setup |
|
| 123 | * @param array $options |
||
| 124 | 6 | * @throws \phpbu\App\Exception |
|
| 125 | 6 | */ |
|
| 126 | 6 | public function setup(array $options) |
|
| 127 | 6 | { |
|
| 128 | 6 | if (empty($options['uri'])) { |
|
| 129 | 6 | throw new Exception('no uri given'); |
|
| 130 | 6 | } |
|
| 131 | |||
| 132 | // PHP >7.2 deprecated the filter options and enabled them by default |
||
| 133 | $filterOptions = version_compare(PHP_VERSION, '7.2.0', '<') |
||
| 134 | ? FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 135 | : null; |
||
| 136 | |||
| 137 | 6 | if (!filter_var($options['uri'], FILTER_VALIDATE_URL, $filterOptions)) { |
|
| 138 | throw new Exception('webhook URI is invalid'); |
||
| 139 | 6 | } |
|
| 140 | 6 | ||
| 141 | 6 | $this->uri = $options['uri']; |
|
| 142 | 6 | $this->method = Arr::getValue($options, 'method', 'GET'); |
|
| 143 | 5 | $this->username = Arr::getValue($options, 'username', ''); |
|
| 144 | $this->password = Arr::getValue($options, 'password', ''); |
||
| 145 | 5 | $this->template = Arr::getValue($options, 'template', ''); |
|
| 146 | 1 | $this->contentType = Arr::getValue($options, 'contentType', 'multipart/form-data'); |
|
| 147 | $this->timeout = Arr::getValue($options, 'timeout', ''); |
||
|
0 ignored issues
–
show
It seems like
phpbu\App\Util\Arr::getV...options, 'timeout', '') can also be of type string. However, the property $timeout is declared as type integer. Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
|||
| 148 | } |
||
| 149 | |||
| 150 | /** |
||
| 151 | * phpbu end event. |
||
| 152 | * |
||
| 153 | * @param \phpbu\App\Event\App\End $event |
||
| 154 | 2 | */ |
|
| 155 | public function onPhpbuEnd(Event\App\End $event) |
||
| 156 | 2 | { |
|
| 157 | 2 | $result = $event->getResult(); |
|
| 158 | $data = $this->getQueryStringData($result); |
||
| 159 | $uri = $this->method === 'GET' ? $this->buildGetUri($data) : $this->uri; |
||
| 160 | $formatter = $this->getBodyFormatter(); |
||
| 161 | $body = $formatter->format($result); |
||
| 162 | |||
| 163 | $this->fireRequest($uri, $body); |
||
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | 6 | * Builds the final request uri for GET requests. |
|
| 168 | * |
||
| 169 | 6 | * @param array $data |
|
| 170 | 1 | * @return string |
|
| 171 | */ |
||
| 172 | private function buildGetUri(array $data) : string |
||
| 173 | 5 | { |
|
| 174 | 1 | $glue = strpos($this->uri, '?') !== false ? '&' : '?'; |
|
| 175 | return $this->uri . $glue . http_build_query($data); |
||
| 176 | 4 | } |
|
| 177 | 4 | ||
| 178 | /** |
||
| 179 | * Return the request body template. |
||
| 180 | * If template and body are set the body supersedes the template setting. |
||
| 181 | * |
||
| 182 | * @return \phpbu\App\Log\ResultFormatter |
||
| 183 | * @throws \phpbu\App\Exception |
||
| 184 | */ |
||
| 185 | private function getBodyFormatter() : ResultFormatter |
||
| 186 | 6 | { |
|
| 187 | if (!empty($this->template)) { |
||
| 188 | 6 | return new ResultFormatter\Template($this->template); |
|
| 189 | } |
||
| 190 | |||
| 191 | 6 | if (!isset($this->availableFormatter[$this->contentType])) { |
|
| 192 | 6 | throw new Exception('no default formatter for content-type: ' . $this->contentType); |
|
| 193 | 6 | } |
|
| 194 | 6 | $class = $this->availableFormatter[$this->contentType]; |
|
| 195 | 6 | return new $class(); |
|
| 196 | 6 | } |
|
| 197 | |||
| 198 | /** |
||
| 199 | * Returns some basic statistics as GET query string. |
||
| 200 | * |
||
| 201 | * @param \phpbu\App\Result $result |
||
| 202 | * @return array |
||
| 203 | */ |
||
| 204 | private function getQueryStringData(Result $result) : array |
||
| 205 | { |
||
| 206 | $end = microtime(true); |
||
| 207 | |||
| 208 | 5 | return [ |
|
| 209 | 'status' => $result->allOk() ? 0 : 1, |
||
| 210 | 5 | 'timestamp' => (int) $this->start, |
|
| 211 | 'duration' => round($end - $this->start, 4), |
||
| 212 | 'err-cnt' => $result->errorCount(), |
||
| 213 | 5 | 'bak-cnt' => count($result->getBackups()), |
|
| 214 | 'bak-fail' => $result->backupsFailedCount(), |
||
| 215 | ]; |
||
| 216 | } |
||
| 217 | 5 | ||
| 218 | 5 | ||
| 219 | 5 | /** |
|
| 220 | * Execute the request to the webhook uri. |
||
| 221 | * |
||
| 222 | 5 | * @param string $uri |
|
| 223 | 1 | * @param string $body |
|
| 224 | * @throws \phpbu\App\Exception |
||
| 225 | */ |
||
| 226 | 5 | protected function fireRequest(string $uri, string $body = '') |
|
| 227 | 5 | { |
|
| 228 | $headers = []; |
||
| 229 | $options = [ |
||
| 230 | 5 | 'http' => [ |
|
| 231 | 4 | 'method' => strtoupper($this->method), |
|
| 232 | 4 | ] |
|
| 233 | ]; |
||
| 234 | 1 | ||
| 235 | if (!empty($body)) { |
||
| 236 | $headers[] = 'Content-Type: ' . $this->contentType; |
||
| 237 | $options['http']['content'] = $body; |
||
| 238 | } |
||
| 239 | |||
| 240 | if (!empty($this->timeout)) { |
||
| 241 | $options['http']['timeout'] = $this->timeout; |
||
| 242 | } |
||
| 243 | |||
| 244 | if (!empty($this->username)) { |
||
| 245 | $headers[] = 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password); |
||
| 246 | } |
||
| 247 | |||
| 248 | $options['http']['header'] = implode("\r\n", $headers); |
||
| 249 | $context = stream_context_create($options); |
||
| 250 | |||
| 251 | try { |
||
| 252 | file_get_contents($uri, false, $context); |
||
| 253 | } catch (Throwable $t) { |
||
| 254 | throw new Exception('could not reach webhook: ' . $this->uri); |
||
| 255 | } |
||
| 256 | } |
||
| 257 | } |
||
| 258 |