1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-mail for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Mail\Header; 10 11use Laminas\Mime\Mime; 12 13class GenericHeader implements HeaderInterface, UnstructuredInterface 14{ 15 /** 16 * @var string 17 */ 18 protected $fieldName = null; 19 20 /** 21 * @var string 22 */ 23 protected $fieldValue = null; 24 25 /** 26 * Header encoding 27 * 28 * @var null|string 29 */ 30 protected $encoding; 31 32 /** 33 * @param string $headerLine 34 * @return GenericHeader 35 */ 36 public static function fromString($headerLine) 37 { 38 list($name, $value) = self::splitHeaderLine($headerLine); 39 $value = HeaderWrap::mimeDecodeValue($value); 40 $header = new static($name, $value); 41 42 return $header; 43 } 44 45 /** 46 * Splits the header line in `name` and `value` parts. 47 * 48 * @param string $headerLine 49 * @return string[] `name` in the first index and `value` in the second. 50 * @throws Exception\InvalidArgumentException If header does not match with the format ``name:value`` 51 */ 52 public static function splitHeaderLine($headerLine) 53 { 54 $parts = explode(':', $headerLine, 2); 55 if (count($parts) !== 2) { 56 throw new Exception\InvalidArgumentException('Header must match with the format "name:value"'); 57 } 58 59 if (! HeaderName::isValid($parts[0])) { 60 throw new Exception\InvalidArgumentException('Invalid header name detected'); 61 } 62 63 if (! HeaderValue::isValid($parts[1])) { 64 throw new Exception\InvalidArgumentException('Invalid header value detected'); 65 } 66 67 $parts[1] = ltrim($parts[1]); 68 69 return $parts; 70 } 71 72 /** 73 * Constructor 74 * 75 * @param string $fieldName Optional 76 * @param string $fieldValue Optional 77 */ 78 public function __construct($fieldName = null, $fieldValue = null) 79 { 80 if ($fieldName) { 81 $this->setFieldName($fieldName); 82 } 83 84 if ($fieldValue !== null) { 85 $this->setFieldValue($fieldValue); 86 } 87 } 88 89 /** 90 * Set header name 91 * 92 * @param string $fieldName 93 * @return GenericHeader 94 * @throws Exception\InvalidArgumentException; 95 */ 96 public function setFieldName($fieldName) 97 { 98 if (! is_string($fieldName) || empty($fieldName)) { 99 throw new Exception\InvalidArgumentException('Header name must be a string'); 100 } 101 102 // Pre-filter to normalize valid characters, change underscore to dash 103 $fieldName = str_replace(' ', '-', ucwords(str_replace(['_', '-'], ' ', $fieldName))); 104 105 if (! HeaderName::isValid($fieldName)) { 106 throw new Exception\InvalidArgumentException( 107 'Header name must be composed of printable US-ASCII characters, except colon.' 108 ); 109 } 110 111 $this->fieldName = $fieldName; 112 return $this; 113 } 114 115 public function getFieldName() 116 { 117 return $this->fieldName; 118 } 119 120 /** 121 * Set header value 122 * 123 * @param string $fieldValue 124 * @return GenericHeader 125 * @throws Exception\InvalidArgumentException; 126 */ 127 public function setFieldValue($fieldValue) 128 { 129 $fieldValue = (string) $fieldValue; 130 131 if (! HeaderWrap::canBeEncoded($fieldValue)) { 132 throw new Exception\InvalidArgumentException( 133 'Header value must be composed of printable US-ASCII characters and valid folding sequences.' 134 ); 135 } 136 137 $this->fieldValue = $fieldValue; 138 $this->encoding = null; 139 140 return $this; 141 } 142 143 public function getFieldValue($format = HeaderInterface::FORMAT_RAW) 144 { 145 if (HeaderInterface::FORMAT_ENCODED === $format) { 146 return HeaderWrap::wrap($this->fieldValue, $this); 147 } 148 149 return $this->fieldValue; 150 } 151 152 public function setEncoding($encoding) 153 { 154 if ($encoding === $this->encoding) { 155 return $this; 156 } 157 158 if ($encoding === null) { 159 $this->encoding = null; 160 return $this; 161 } 162 163 $encoding = strtoupper($encoding); 164 if ($encoding === 'UTF-8') { 165 $this->encoding = $encoding; 166 return $this; 167 } 168 169 if ($encoding === 'ASCII' && Mime::isPrintable($this->fieldValue)) { 170 $this->encoding = $encoding; 171 return $this; 172 } 173 174 $this->encoding = null; 175 176 return $this; 177 } 178 179 public function getEncoding() 180 { 181 if (! $this->encoding) { 182 $this->encoding = Mime::isPrintable($this->fieldValue) ? 'ASCII' : 'UTF-8'; 183 } 184 185 return $this->encoding; 186 } 187 188 public function toString() 189 { 190 $name = $this->getFieldName(); 191 if (empty($name)) { 192 throw new Exception\RuntimeException('Header name is not set, use setFieldName()'); 193 } 194 $value = $this->getFieldValue(HeaderInterface::FORMAT_ENCODED); 195 196 return $name . ': ' . $value; 197 } 198} 199