1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-log for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-log/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-log/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Log\Writer; 10 11use Laminas\Db\Adapter\Adapter; 12use Laminas\Log\Exception; 13use Laminas\Log\Formatter\Db as DbFormatter; 14use Traversable; 15 16class Db extends AbstractWriter 17{ 18 /** 19 * Db adapter instance 20 * 21 * @var Adapter 22 */ 23 protected $db; 24 25 /** 26 * Table name 27 * 28 * @var string 29 */ 30 protected $tableName; 31 32 /** 33 * Relates database columns names to log data field keys. 34 * 35 * @var null|array 36 */ 37 protected $columnMap; 38 39 /** 40 * Field separator for sub-elements 41 * 42 * @var string 43 */ 44 protected $separator = '_'; 45 46 /** 47 * Constructor 48 * 49 * We used the Adapter instead of Laminas\Db for a performance reason. 50 * 51 * @param Adapter|array|Traversable $db 52 * @param string $tableName 53 * @param array $columnMap 54 * @param string $separator 55 * @throws Exception\InvalidArgumentException 56 */ 57 public function __construct($db, $tableName = null, array $columnMap = null, $separator = null) 58 { 59 if ($db instanceof Traversable) { 60 $db = iterator_to_array($db); 61 } 62 63 if (is_array($db)) { 64 parent::__construct($db); 65 $separator = isset($db['separator']) ? $db['separator'] : null; 66 $columnMap = isset($db['column']) ? $db['column'] : null; 67 $tableName = isset($db['table']) ? $db['table'] : null; 68 $db = isset($db['db']) ? $db['db'] : null; 69 } 70 71 if (! $db instanceof Adapter) { 72 throw new Exception\InvalidArgumentException('You must pass a valid Laminas\Db\Adapter\Adapter'); 73 } 74 75 $tableName = (string) $tableName; 76 if ('' === $tableName) { 77 throw new Exception\InvalidArgumentException( 78 'You must specify a table name. Either directly in the constructor, or via options' 79 ); 80 } 81 82 $this->db = $db; 83 $this->tableName = $tableName; 84 $this->columnMap = $columnMap; 85 86 if (! empty($separator)) { 87 $this->separator = $separator; 88 } 89 90 if (! $this->hasFormatter()) { 91 $this->setFormatter(new DbFormatter()); 92 } 93 } 94 95 /** 96 * Remove reference to database adapter 97 * 98 * @return void 99 */ 100 public function shutdown() 101 { 102 $this->db = null; 103 } 104 105 /** 106 * Write a message to the log. 107 * 108 * @param array $event event data 109 * @return void 110 * @throws Exception\RuntimeException 111 */ 112 protected function doWrite(array $event) 113 { 114 if (null === $this->db) { 115 throw new Exception\RuntimeException('Database adapter is null'); 116 } 117 118 $event = $this->formatter->format($event); 119 120 // Transform the event array into fields 121 if (null === $this->columnMap) { 122 $dataToInsert = $this->eventIntoColumn($event); 123 } else { 124 $dataToInsert = $this->mapEventIntoColumn($event, $this->columnMap); 125 } 126 127 $statement = $this->db->query($this->prepareInsert($dataToInsert)); 128 $statement->execute($dataToInsert); 129 } 130 131 /** 132 * Prepare the INSERT SQL statement 133 * 134 * @param array $fields 135 * @return string 136 */ 137 protected function prepareInsert(array $fields) 138 { 139 $keys = array_keys($fields); 140 $sql = 'INSERT INTO ' . $this->db->platform->quoteIdentifier($this->tableName) . ' (' . 141 implode(",", array_map([$this->db->platform, 'quoteIdentifier'], $keys)) . ') VALUES (' . 142 implode(",", array_map([$this->db->driver, 'formatParameterName'], $keys)) . ')'; 143 144 return $sql; 145 } 146 147 /** 148 * Map event into column using the $columnMap array 149 * 150 * @param array $event 151 * @param array $columnMap 152 * @return array 153 */ 154 protected function mapEventIntoColumn(array $event, array $columnMap = null) 155 { 156 if (empty($event)) { 157 return []; 158 } 159 160 $data = []; 161 foreach ($event as $name => $value) { 162 if (is_array($value)) { 163 foreach ($value as $key => $subvalue) { 164 if (isset($columnMap[$name][$key])) { 165 if (is_scalar($subvalue)) { 166 $data[$columnMap[$name][$key]] = $subvalue; 167 continue; 168 } 169 170 $data[$columnMap[$name][$key]] = var_export($subvalue, true); 171 } 172 } 173 } elseif (isset($columnMap[$name])) { 174 $data[$columnMap[$name]] = $value; 175 } 176 } 177 return $data; 178 } 179 180 /** 181 * Transform event into column for the db table 182 * 183 * @param array $event 184 * @return array 185 */ 186 protected function eventIntoColumn(array $event) 187 { 188 if (empty($event)) { 189 return []; 190 } 191 192 $data = []; 193 foreach ($event as $name => $value) { 194 if (is_array($value)) { 195 foreach ($value as $key => $subvalue) { 196 if (is_scalar($subvalue)) { 197 $data[$name . $this->separator . $key] = $subvalue; 198 continue; 199 } 200 201 $data[$name . $this->separator . $key] = var_export($subvalue, true); 202 } 203 } else { 204 $data[$name] = $value; 205 } 206 } 207 return $data; 208 } 209} 210