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\Processor; 13 14/** 15 * Injects url/method and remote IP of the current web request in all records 16 * 17 * @author Jordi Boggiano <j.boggiano@seld.be> 18 */ 19class WebProcessor implements ProcessorInterface 20{ 21 /** 22 * @var array<string, mixed>|\ArrayAccess<string, mixed> 23 */ 24 protected $serverData; 25 26 /** 27 * Default fields 28 * 29 * Array is structured as [key in record.extra => key in $serverData] 30 * 31 * @var array<string, string> 32 */ 33 protected $extraFields = [ 34 'url' => 'REQUEST_URI', 35 'ip' => 'REMOTE_ADDR', 36 'http_method' => 'REQUEST_METHOD', 37 'server' => 'SERVER_NAME', 38 'referrer' => 'HTTP_REFERER', 39 'user_agent' => 'HTTP_USER_AGENT', 40 ]; 41 42 /** 43 * @param array<string, mixed>|\ArrayAccess<string, mixed>|null $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data 44 * @param array<string, string>|array<string>|null $extraFields Field names and the related key inside $serverData to be added (or just a list of field names to use the default configured $serverData mapping). If not provided it defaults to: [url, ip, http_method, server, referrer] + unique_id if present in server data 45 */ 46 public function __construct($serverData = null, array $extraFields = null) 47 { 48 if (null === $serverData) { 49 $this->serverData = &$_SERVER; 50 } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { 51 $this->serverData = $serverData; 52 } else { 53 throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); 54 } 55 56 $defaultEnabled = ['url', 'ip', 'http_method', 'server', 'referrer']; 57 if (isset($this->serverData['UNIQUE_ID'])) { 58 $this->extraFields['unique_id'] = 'UNIQUE_ID'; 59 $defaultEnabled[] = 'unique_id'; 60 } 61 62 if (null === $extraFields) { 63 $extraFields = $defaultEnabled; 64 } 65 if (isset($extraFields[0])) { 66 foreach (array_keys($this->extraFields) as $fieldName) { 67 if (!in_array($fieldName, $extraFields)) { 68 unset($this->extraFields[$fieldName]); 69 } 70 } 71 } else { 72 $this->extraFields = $extraFields; 73 } 74 } 75 76 /** 77 * {@inheritDoc} 78 */ 79 public function __invoke(array $record): array 80 { 81 // skip processing if for some reason request data 82 // is not present (CLI or wonky SAPIs) 83 if (!isset($this->serverData['REQUEST_URI'])) { 84 return $record; 85 } 86 87 $record['extra'] = $this->appendExtraFields($record['extra']); 88 89 return $record; 90 } 91 92 public function addExtraField(string $extraName, string $serverName): self 93 { 94 $this->extraFields[$extraName] = $serverName; 95 96 return $this; 97 } 98 99 /** 100 * @param mixed[] $extra 101 * @return mixed[] 102 */ 103 private function appendExtraFields(array $extra): array 104 { 105 foreach ($this->extraFields as $extraName => $serverName) { 106 $extra[$extraName] = $this->serverData[$serverName] ?? null; 107 } 108 109 return $extra; 110 } 111} 112