| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | namespace GuzzleHttp;
 | 
        
           |  |  | 4 |   | 
        
           |  |  | 5 | use Psr\Http\Message\MessageInterface;
 | 
        
           |  |  | 6 | use Psr\Http\Message\RequestInterface;
 | 
        
           |  |  | 7 | use Psr\Http\Message\ResponseInterface;
 | 
        
           |  |  | 8 |   | 
        
           |  |  | 9 | /**
 | 
        
           |  |  | 10 |  * Formats log messages using variable substitutions for requests, responses,
 | 
        
           |  |  | 11 |  * and other transactional data.
 | 
        
           |  |  | 12 |  *
 | 
        
           |  |  | 13 |  * The following variable substitutions are supported:
 | 
        
           |  |  | 14 |  *
 | 
        
           |  |  | 15 |  * - {request}:        Full HTTP request message
 | 
        
           |  |  | 16 |  * - {response}:       Full HTTP response message
 | 
        
           |  |  | 17 |  * - {ts}:             ISO 8601 date in GMT
 | 
        
           |  |  | 18 |  * - {date_iso_8601}   ISO 8601 date in GMT
 | 
        
           |  |  | 19 |  * - {date_common_log} Apache common log date using the configured timezone.
 | 
        
           |  |  | 20 |  * - {host}:           Host of the request
 | 
        
           |  |  | 21 |  * - {method}:         Method of the request
 | 
        
           |  |  | 22 |  * - {uri}:            URI of the request
 | 
        
           |  |  | 23 |  * - {version}:        Protocol version
 | 
        
           |  |  | 24 |  * - {target}:         Request target of the request (path + query + fragment)
 | 
        
           |  |  | 25 |  * - {hostname}:       Hostname of the machine that sent the request
 | 
        
           |  |  | 26 |  * - {code}:           Status code of the response (if available)
 | 
        
           |  |  | 27 |  * - {phrase}:         Reason phrase of the response  (if available)
 | 
        
           |  |  | 28 |  * - {error}:          Any error messages (if available)
 | 
        
           |  |  | 29 |  * - {req_header_*}:   Replace `*` with the lowercased name of a request header to add to the message
 | 
        
           |  |  | 30 |  * - {res_header_*}:   Replace `*` with the lowercased name of a response header to add to the message
 | 
        
           |  |  | 31 |  * - {req_headers}:    Request headers
 | 
        
           |  |  | 32 |  * - {res_headers}:    Response headers
 | 
        
           |  |  | 33 |  * - {req_body}:       Request body
 | 
        
           |  |  | 34 |  * - {res_body}:       Response body
 | 
        
           |  |  | 35 |  *
 | 
        
           |  |  | 36 |  * @final
 | 
        
           |  |  | 37 |  */
 | 
        
           |  |  | 38 | class MessageFormatter implements MessageFormatterInterface
 | 
        
           |  |  | 39 | {
 | 
        
           |  |  | 40 |     /**
 | 
        
           |  |  | 41 |      * Apache Common Log Format.
 | 
        
           |  |  | 42 |      *
 | 
        
           |  |  | 43 |      * @link https://httpd.apache.org/docs/2.4/logs.html#common
 | 
        
           |  |  | 44 |      *
 | 
        
           |  |  | 45 |      * @var string
 | 
        
           |  |  | 46 |      */
 | 
        
           |  |  | 47 |     public const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
 | 
        
           |  |  | 48 |     public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
 | 
        
           |  |  | 49 |     public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 |     /**
 | 
        
           |  |  | 52 |      * @var string Template used to format log messages
 | 
        
           |  |  | 53 |      */
 | 
        
           |  |  | 54 |     private $template;
 | 
        
           |  |  | 55 |   | 
        
           |  |  | 56 |     /**
 | 
        
           |  |  | 57 |      * @param string $template Log message template
 | 
        
           |  |  | 58 |      */
 | 
        
           |  |  | 59 |     public function __construct(?string $template = self::CLF)
 | 
        
           |  |  | 60 |     {
 | 
        
           |  |  | 61 |         $this->template = $template ?: self::CLF;
 | 
        
           |  |  | 62 |     }
 | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 |     /**
 | 
        
           |  |  | 65 |      * Returns a formatted message string.
 | 
        
           |  |  | 66 |      *
 | 
        
           |  |  | 67 |      * @param RequestInterface       $request  Request that was sent
 | 
        
           |  |  | 68 |      * @param ResponseInterface|null $response Response that was received
 | 
        
           |  |  | 69 |      * @param \Throwable|null        $error    Exception that was received
 | 
        
           |  |  | 70 |      */
 | 
        
           |  |  | 71 |     public function format(RequestInterface $request, ?ResponseInterface $response = null, ?\Throwable $error = null): string
 | 
        
           |  |  | 72 |     {
 | 
        
           |  |  | 73 |         $cache = [];
 | 
        
           |  |  | 74 |   | 
        
           |  |  | 75 |         /** @var string */
 | 
        
           |  |  | 76 |         return \preg_replace_callback(
 | 
        
           |  |  | 77 |             '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
 | 
        
           |  |  | 78 |             function (array $matches) use ($request, $response, $error, &$cache) {
 | 
        
           |  |  | 79 |                 if (isset($cache[$matches[1]])) {
 | 
        
           |  |  | 80 |                     return $cache[$matches[1]];
 | 
        
           |  |  | 81 |                 }
 | 
        
           |  |  | 82 |   | 
        
           |  |  | 83 |                 $result = '';
 | 
        
           |  |  | 84 |                 switch ($matches[1]) {
 | 
        
           |  |  | 85 |                     case 'request':
 | 
        
           |  |  | 86 |                         $result = Psr7\Message::toString($request);
 | 
        
           |  |  | 87 |                         break;
 | 
        
           |  |  | 88 |                     case 'response':
 | 
        
           |  |  | 89 |                         $result = $response ? Psr7\Message::toString($response) : '';
 | 
        
           |  |  | 90 |                         break;
 | 
        
           |  |  | 91 |                     case 'req_headers':
 | 
        
           |  |  | 92 |                         $result = \trim($request->getMethod()
 | 
        
           |  |  | 93 |                                 . ' ' . $request->getRequestTarget())
 | 
        
           |  |  | 94 |                             . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
 | 
        
           |  |  | 95 |                             . $this->headers($request);
 | 
        
           |  |  | 96 |                         break;
 | 
        
           |  |  | 97 |                     case 'res_headers':
 | 
        
           |  |  | 98 |                         $result = $response ?
 | 
        
           |  |  | 99 |                             \sprintf(
 | 
        
           |  |  | 100 |                                 'HTTP/%s %d %s',
 | 
        
           |  |  | 101 |                                 $response->getProtocolVersion(),
 | 
        
           |  |  | 102 |                                 $response->getStatusCode(),
 | 
        
           |  |  | 103 |                                 $response->getReasonPhrase()
 | 
        
           |  |  | 104 |                             ) . "\r\n" . $this->headers($response)
 | 
        
           |  |  | 105 |                             : 'NULL';
 | 
        
           |  |  | 106 |                         break;
 | 
        
           |  |  | 107 |                     case 'req_body':
 | 
        
           |  |  | 108 |                         $result = $request->getBody()->__toString();
 | 
        
           |  |  | 109 |                         break;
 | 
        
           |  |  | 110 |                     case 'res_body':
 | 
        
           |  |  | 111 |                         if (!$response instanceof ResponseInterface) {
 | 
        
           |  |  | 112 |                             $result = 'NULL';
 | 
        
           |  |  | 113 |                             break;
 | 
        
           |  |  | 114 |                         }
 | 
        
           |  |  | 115 |   | 
        
           |  |  | 116 |                         $body = $response->getBody();
 | 
        
           |  |  | 117 |   | 
        
           |  |  | 118 |                         if (!$body->isSeekable()) {
 | 
        
           |  |  | 119 |                             $result = 'RESPONSE_NOT_LOGGEABLE';
 | 
        
           |  |  | 120 |                             break;
 | 
        
           |  |  | 121 |                         }
 | 
        
           |  |  | 122 |   | 
        
           |  |  | 123 |                         $result = $response->getBody()->__toString();
 | 
        
           |  |  | 124 |                         break;
 | 
        
           |  |  | 125 |                     case 'ts':
 | 
        
           |  |  | 126 |                     case 'date_iso_8601':
 | 
        
           |  |  | 127 |                         $result = \gmdate('c');
 | 
        
           |  |  | 128 |                         break;
 | 
        
           |  |  | 129 |                     case 'date_common_log':
 | 
        
           |  |  | 130 |                         $result = \date('d/M/Y:H:i:s O');
 | 
        
           |  |  | 131 |                         break;
 | 
        
           |  |  | 132 |                     case 'method':
 | 
        
           |  |  | 133 |                         $result = $request->getMethod();
 | 
        
           |  |  | 134 |                         break;
 | 
        
           |  |  | 135 |                     case 'version':
 | 
        
           |  |  | 136 |                         $result = $request->getProtocolVersion();
 | 
        
           |  |  | 137 |                         break;
 | 
        
           |  |  | 138 |                     case 'uri':
 | 
        
           |  |  | 139 |                     case 'url':
 | 
        
           |  |  | 140 |                         $result = $request->getUri()->__toString();
 | 
        
           |  |  | 141 |                         break;
 | 
        
           |  |  | 142 |                     case 'target':
 | 
        
           |  |  | 143 |                         $result = $request->getRequestTarget();
 | 
        
           |  |  | 144 |                         break;
 | 
        
           |  |  | 145 |                     case 'req_version':
 | 
        
           |  |  | 146 |                         $result = $request->getProtocolVersion();
 | 
        
           |  |  | 147 |                         break;
 | 
        
           |  |  | 148 |                     case 'res_version':
 | 
        
           |  |  | 149 |                         $result = $response
 | 
        
           |  |  | 150 |                             ? $response->getProtocolVersion()
 | 
        
           |  |  | 151 |                             : 'NULL';
 | 
        
           |  |  | 152 |                         break;
 | 
        
           |  |  | 153 |                     case 'host':
 | 
        
           |  |  | 154 |                         $result = $request->getHeaderLine('Host');
 | 
        
           |  |  | 155 |                         break;
 | 
        
           |  |  | 156 |                     case 'hostname':
 | 
        
           |  |  | 157 |                         $result = \gethostname();
 | 
        
           |  |  | 158 |                         break;
 | 
        
           |  |  | 159 |                     case 'code':
 | 
        
           |  |  | 160 |                         $result = $response ? $response->getStatusCode() : 'NULL';
 | 
        
           |  |  | 161 |                         break;
 | 
        
           |  |  | 162 |                     case 'phrase':
 | 
        
           |  |  | 163 |                         $result = $response ? $response->getReasonPhrase() : 'NULL';
 | 
        
           |  |  | 164 |                         break;
 | 
        
           |  |  | 165 |                     case 'error':
 | 
        
           |  |  | 166 |                         $result = $error ? $error->getMessage() : 'NULL';
 | 
        
           |  |  | 167 |                         break;
 | 
        
           |  |  | 168 |                     default:
 | 
        
           |  |  | 169 |                         // handle prefixed dynamic headers
 | 
        
           |  |  | 170 |                         if (\strpos($matches[1], 'req_header_') === 0) {
 | 
        
           |  |  | 171 |                             $result = $request->getHeaderLine(\substr($matches[1], 11));
 | 
        
           |  |  | 172 |                         } elseif (\strpos($matches[1], 'res_header_') === 0) {
 | 
        
           |  |  | 173 |                             $result = $response
 | 
        
           |  |  | 174 |                                 ? $response->getHeaderLine(\substr($matches[1], 11))
 | 
        
           |  |  | 175 |                                 : 'NULL';
 | 
        
           |  |  | 176 |                         }
 | 
        
           |  |  | 177 |                 }
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |                 $cache[$matches[1]] = $result;
 | 
        
           |  |  | 180 |                 return $result;
 | 
        
           |  |  | 181 |             },
 | 
        
           |  |  | 182 |             $this->template
 | 
        
           |  |  | 183 |         );
 | 
        
           |  |  | 184 |     }
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |     /**
 | 
        
           |  |  | 187 |      * Get headers from message as string
 | 
        
           |  |  | 188 |      */
 | 
        
           |  |  | 189 |     private function headers(MessageInterface $message): string
 | 
        
           |  |  | 190 |     {
 | 
        
           |  |  | 191 |         $result = '';
 | 
        
           |  |  | 192 |         foreach ($message->getHeaders() as $name => $values) {
 | 
        
           |  |  | 193 |             $result .= $name . ': ' . \implode(', ', $values) . "\r\n";
 | 
        
           |  |  | 194 |         }
 | 
        
           |  |  | 195 |   | 
        
           |  |  | 196 |         return \trim($result);
 | 
        
           |  |  | 197 |     }
 | 
        
           |  |  | 198 | }
 |