1<?php declare(strict_types=1);
2
3/*
4 * This file is part of the Monolog package.
5 *
6 * (c) Jordi Boggiano <j.boggiano@seld.be>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Monolog\Handler;
13
14use Monolog\Logger;
15use Monolog\Utils;
16use Monolog\Formatter\FlowdockFormatter;
17use Monolog\Formatter\FormatterInterface;
18
19/**
20 * Sends notifications through the Flowdock push API
21 *
22 * This must be configured with a FlowdockFormatter instance via setFormatter()
23 *
24 * Notes:
25 * API token - Flowdock API token
26 *
27 * @author Dominik Liebler <liebler.dominik@gmail.com>
28 * @see https://www.flowdock.com/api/push
29 *
30 * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
31 */
32class FlowdockHandler extends SocketHandler
33{
34    /**
35     * @var string
36     */
37    protected $apiToken;
38
39    /**
40     * @throws MissingExtensionException if OpenSSL is missing
41     */
42    public function __construct(
43        string $apiToken,
44        $level = Logger::DEBUG,
45        bool $bubble = true,
46        bool $persistent = false,
47        float $timeout = 0.0,
48        float $writingTimeout = 10.0,
49        ?float $connectionTimeout = null,
50        ?int $chunkSize = null
51    ) {
52        if (!extension_loaded('openssl')) {
53            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler');
54        }
55
56        parent::__construct(
57            'ssl://api.flowdock.com:443',
58            $level,
59            $bubble,
60            $persistent,
61            $timeout,
62            $writingTimeout,
63            $connectionTimeout,
64            $chunkSize
65        );
66        $this->apiToken = $apiToken;
67    }
68
69    /**
70     * {@inheritDoc}
71     */
72    public function setFormatter(FormatterInterface $formatter): HandlerInterface
73    {
74        if (!$formatter instanceof FlowdockFormatter) {
75            throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
76        }
77
78        return parent::setFormatter($formatter);
79    }
80
81    /**
82     * Gets the default formatter.
83     */
84    protected function getDefaultFormatter(): FormatterInterface
85    {
86        throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly');
87    }
88
89    /**
90     * {@inheritDoc}
91     */
92    protected function write(array $record): void
93    {
94        parent::write($record);
95
96        $this->closeSocket();
97    }
98
99    /**
100     * {@inheritDoc}
101     */
102    protected function generateDataStream(array $record): string
103    {
104        $content = $this->buildContent($record);
105
106        return $this->buildHeader($content) . $content;
107    }
108
109    /**
110     * Builds the body of API call
111     *
112     * @phpstan-param FormattedRecord $record
113     */
114    private function buildContent(array $record): string
115    {
116        return Utils::jsonEncode($record['formatted']['flowdock']);
117    }
118
119    /**
120     * Builds the header of the API Call
121     */
122    private function buildHeader(string $content): string
123    {
124        $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n";
125        $header .= "Host: api.flowdock.com\r\n";
126        $header .= "Content-Type: application/json\r\n";
127        $header .= "Content-Length: " . strlen($content) . "\r\n";
128        $header .= "\r\n";
129
130        return $header;
131    }
132}
133