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 DateTimeInterface;
12use Laminas\Log\Exception;
13use Laminas\Log\Formatter\FormatterInterface;
14use Laminas\Stdlib\ArrayUtils;
15use MongoDB\BSON\UTCDateTime;
16use MongoDB\Driver\BulkWrite;
17use MongoDB\Driver\Manager;
18use MongoDB\Driver\WriteConcern;
19use Traversable;
20
21/**
22 * MongoDB log writer.
23 */
24class MongoDB extends AbstractWriter
25{
26    /**
27     * @var Manager
28     */
29    protected $manager;
30
31    /**
32     * @var string
33     */
34    protected $database;
35
36    /**
37     * @var WriteConcern
38     */
39    protected $writeConcern;
40
41    /**
42     * Constructor
43     *
44     * @param Manager|array|Traversable $manager
45     * @param string $database
46     * @param string $collection
47     * @param WriteConcern|array|Traversable $writeConcern
48     * @throws Exception\InvalidArgumentException
49     */
50    public function __construct($manager, $database = null, $collection = null, $writeConcern = null)
51    {
52        if (! extension_loaded('mongodb')) {
53            throw new Exception\ExtensionNotLoadedException('Missing ext/mongodb');
54        }
55
56        if ($manager instanceof Traversable) {
57            // Configuration may be multi-dimensional due to save options
58            $manager = ArrayUtils::iteratorToArray($manager);
59        }
60
61        if (is_array($manager)) {
62            parent::__construct($manager);
63            $writeConcern = isset($manager['write_concern']) ? $manager['write_concern'] : new WriteConcern(1);
64            $collection   = isset($manager['collection']) ? $manager['collection'] : null;
65            $database     = isset($manager['database']) ? $manager['database'] : null;
66            $manager      = isset($manager['manager']) ? $manager['manager'] : null;
67        }
68
69        if (null === $database) {
70            throw new Exception\InvalidArgumentException('The database parameter cannot be empty');
71        }
72
73        if (null !== $collection) {
74            $database = sprintf('%s.%s', $database, $collection);
75        }
76
77        if (! $manager instanceof Manager) {
78            throw new Exception\InvalidArgumentException(sprintf(
79                'Parameter of type %s is invalid; must be MongoDB\Driver\Manager',
80                (is_object($manager) ? get_class($manager) : gettype($manager))
81            ));
82        }
83
84        if ($writeConcern instanceof Traversable) {
85            $writeConcern = iterator_to_array($writeConcern);
86        }
87
88        if (is_array($writeConcern)) {
89            $wstring      = isset($writeConcern['wstring']) ? $writeConcern['wstring'] : 1;
90            $wtimeout     = isset($writeConcern['wtimeout']) ? $writeConcern['wtimeout'] : 0;
91            $journal      = isset($writeConcern['journal']) ? $writeConcern['journal'] : false;
92            $writeConcern = new WriteConcern($wstring, $wtimeout, $journal);
93        }
94
95        $this->manager      = $manager;
96        $this->database     = $database;
97        $this->writeConcern = $writeConcern;
98    }
99
100    /**
101     * This writer does not support formatting.
102     *
103     * @param string|FormatterInterface $formatter
104     * @param array|null $options (unused)
105     * @return WriterInterface
106     */
107    public function setFormatter($formatter, array $options = null)
108    {
109        return $this;
110    }
111
112    /**
113     * Write a message to the log.
114     *
115     * @param array $event Event data
116     * @return void
117     * @throws Exception\RuntimeException
118     */
119    protected function doWrite(array $event)
120    {
121        if (null === $this->manager) {
122            throw new Exception\RuntimeException('MongoDB\Driver\Manager must be defined');
123        }
124
125        if (isset($event['timestamp']) && $event['timestamp'] instanceof DateTimeInterface) {
126            $millis = (int) floor((float) $event['timestamp']->format('U.u') * 1000);
127            $event['timestamp'] = new UTCDateTime($millis);
128        }
129
130        $bulkWrite = new BulkWrite();
131        $bulkWrite->insert($event);
132
133        $this->manager->executeBulkWrite($this->database, $bulkWrite, $this->writeConcern);
134    }
135}
136